summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/Makefile3
-rw-r--r--sound/soc/codecs/aic326x_tiload.c5
-rw-r--r--sound/soc/codecs/base_main_Rate48_pps_driver.h11
-rw-r--r--sound/soc/codecs/rt5639.c16
-rw-r--r--sound/soc/codecs/rt5640.c47
-rw-r--r--sound/soc/codecs/second_rate_pps_driver.h7
-rw-r--r--sound/soc/codecs/spdif_transciever.c38
-rw-r--r--sound/soc/codecs/tlv320aic326x.c24
-rw-r--r--sound/soc/codecs/tlv320aic326x.h11
-rw-r--r--sound/soc/codecs/tlv320aic326x_mini-dsp.c32
-rw-r--r--sound/soc/codecs/tlv320aic326x_minidsp_config.c11
-rw-r--r--sound/soc/codecs/wm8903.c8
-rw-r--r--sound/soc/sh/siu_pcm.c5
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra20_i2s.c3
-rw-r--r--sound/soc/tegra/tegra30_ahub.c127
-rw-r--r--sound/soc/tegra/tegra30_ahub.h14
-rw-r--r--sound/soc/tegra/tegra30_i2s.c241
-rw-r--r--sound/soc/tegra/tegra30_i2s.h14
-rw-r--r--sound/soc/tegra/tegra_aic326x.c8
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c4
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h3
-rw-r--r--sound/soc/tegra/tegra_max98088.c20
-rw-r--r--sound/soc/tegra/tegra_max98095.c2
-rw-r--r--sound/soc/tegra/tegra_p1852.c2
-rw-r--r--sound/soc/tegra/tegra_pcm.c107
-rw-r--r--sound/soc/tegra/tegra_pcm.h20
-rw-r--r--sound/soc/tegra/tegra_rt5640.c59
-rw-r--r--sound/soc/tegra/tegra_tdm_pcm.c160
-rw-r--r--sound/soc/tegra/tegra_wm8753.c2
-rw-r--r--sound/soc/tegra/tegra_wm8903.c2
-rw-r--r--sound/soc/tegra/trimslice.c2
32 files changed, 848 insertions, 162 deletions
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 2eaef5b52e08..d80b7bbff2e8 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -35,8 +35,7 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
-snd-soc-tlv320aic326x-objs := tlv320aic326x.o tlv320aic326x_minidsp_config.o
-snd-soc-tlv320aic326x-objs += tlv320aic326x_mini-dsp.o aic326x_tiload.o
+snd-soc-tlv320aic326x-objs := tlv320aic326x.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
diff --git a/sound/soc/codecs/aic326x_tiload.c b/sound/soc/codecs/aic326x_tiload.c
index 00aa4d4ce7d7..07615dc5ebd7 100644
--- a/sound/soc/codecs/aic326x_tiload.c
+++ b/sound/soc/codecs/aic326x_tiload.c
@@ -61,7 +61,8 @@ static void aic3262_dump_page(struct i2c_client *i2c, u8 page);
/* externs */
extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page);
extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book);
-extern int aic3262_write(struct snd_soc_codec *codec, u16 reg, u8 value);
+extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value);
int aic3262_driver_init(struct snd_soc_codec *codec);
/************** Dynamic aic3262 driver, TI LOAD support ***************/
@@ -236,7 +237,7 @@ static ssize_t tiload_write(struct file *file, const char __user * buf,
return i2c_master_send(i2c, wr_data, count);
}
-static int tiload_ioctl( struct file *filp,
+static long tiload_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
int num = 0;
diff --git a/sound/soc/codecs/base_main_Rate48_pps_driver.h b/sound/soc/codecs/base_main_Rate48_pps_driver.h
index 4d6f227cc42e..dff91858940e 100644
--- a/sound/soc/codecs/base_main_Rate48_pps_driver.h
+++ b/sound/soc/codecs/base_main_Rate48_pps_driver.h
@@ -16,17 +16,6 @@ static control base_speaker_SRS_VOLUME_controls[] = {
static char * base_speaker_SRS_VOLUME_control_names[] = {
};
-/*//INSTRUCTIONS & COEFFICIENTS
-typedef struct {
- u8 reg_off;
- u8 reg_val;
-} reg_value;*/
-
-static char * base_speaker_SRS_REG_Section_names[] = {
- "miniDSP_A_reg_values",
- "miniDSP_D_reg_values",
-};
-
reg_value base_speaker_SRS_REG_init_Section_program[] = {
{ 0,0x0},
{ 0x7F,0x00},
diff --git a/sound/soc/codecs/rt5639.c b/sound/soc/codecs/rt5639.c
index 06c8451a27c8..c9b14e9f0bc1 100644
--- a/sound/soc/codecs/rt5639.c
+++ b/sound/soc/codecs/rt5639.c
@@ -421,8 +421,18 @@ static int rt5639_readable_register(
int rt5639_headset_detect(struct snd_soc_codec *codec, int jack_insert)
{
int jack_type;
+ int sclk_src;
if (jack_insert) {
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ snd_soc_write(codec, RT5639_PWR_ANLG1, 0x2004);
+ snd_soc_write(codec, RT5639_MICBIAS, 0x3830);
+ snd_soc_write(codec, RT5639_DUMMY1 , 0x3701);
+ }
+ sclk_src = snd_soc_read(codec, RT5639_GLB_CLK) &
+ RT5639_SCLK_SRC_MASK;
+ snd_soc_update_bits(codec, RT5639_GLB_CLK,
+ RT5639_SCLK_SRC_MASK, 0x3 << RT5639_SCLK_SRC_SFT);
snd_soc_update_bits(codec, RT5639_PWR_ANLG1,
RT5639_PWR_LDO2, RT5639_PWR_LDO2);
snd_soc_update_bits(codec, RT5639_PWR_ANLG2,
@@ -434,13 +444,15 @@ int rt5639_headset_detect(struct snd_soc_codec *codec, int jack_insert)
RT5639_PWR_MB_PU | RT5639_PWR_CLK25M_PU);
snd_soc_update_bits(codec, RT5639_DUMMY1,
0x1, 0x1);
- msleep(50);
+ msleep(100);
if (snd_soc_read(codec, RT5639_IRQ_CTRL2) & 0x8)
jack_type = RT5639_HEADPHO_DET;
else
jack_type = RT5639_HEADSET_DET;
snd_soc_update_bits(codec, RT5639_IRQ_CTRL2,
RT5639_MB1_OC_CLR, 0);
+ snd_soc_update_bits(codec, RT5639_GLB_CLK,
+ RT5639_SCLK_SRC_MASK, sclk_src);
} else {
snd_soc_update_bits(codec, RT5639_MICBIAS,
RT5639_MIC1_OVCD_MASK,
@@ -1186,8 +1198,6 @@ static int spk_event(struct snd_soc_dapm_widget *w,
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
-
switch (event) {
case SND_SOC_DAPM_POST_PMU:
printk("hp_event --SND_SOC_DAPM_POST_PMU\n");
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 4f50b39cb48a..8407c638cf8a 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -25,7 +25,7 @@
#include <sound/tlv.h>
#include "rt5640.h"
-#if (CONFIG_SND_SOC_RT5642_MODULE | CONFIG_SND_SOC_RT5642)
+#if defined(CONFIG_SND_SOC_RT5642_MODULE) || defined(CONFIG_SND_SOC_RT5642)
#include "rt5640-dsp.h"
#endif
@@ -422,8 +422,18 @@ static int rt5640_readable_register(
int rt5640_headset_detect(struct snd_soc_codec *codec, int jack_insert)
{
int jack_type;
+ int sclk_src;
if (jack_insert) {
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ snd_soc_write(codec, RT5640_PWR_ANLG1, 0x2004);
+ snd_soc_write(codec, RT5640_MICBIAS, 0x3830);
+ snd_soc_write(codec, RT5640_DUMMY1 , 0x3701);
+ }
+ sclk_src = snd_soc_read(codec, RT5640_GLB_CLK) &
+ RT5640_SCLK_SRC_MASK;
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, 0x3 << RT5640_SCLK_SRC_SFT);
snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
RT5640_PWR_LDO2, RT5640_PWR_LDO2);
snd_soc_update_bits(codec, RT5640_PWR_ANLG2,
@@ -435,13 +445,15 @@ int rt5640_headset_detect(struct snd_soc_codec *codec, int jack_insert)
RT5640_PWR_MB_PU | RT5640_PWR_CLK25M_PU);
snd_soc_update_bits(codec, RT5640_DUMMY1,
0x1, 0x1);
- msleep(50);
+ msleep(100);
if (snd_soc_read(codec, RT5640_IRQ_CTRL2) & 0x8)
jack_type = RT5640_HEADPHO_DET;
else
jack_type = RT5640_HEADSET_DET;
snd_soc_update_bits(codec, RT5640_IRQ_CTRL2,
RT5640_MB1_OC_CLR, 0);
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, sclk_src);
} else {
snd_soc_update_bits(codec, RT5640_MICBIAS,
RT5640_MIC1_OVCD_MASK,
@@ -1167,8 +1179,6 @@ static int spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- static unsigned int spkl_out_enable;
- static unsigned int spkr_out_enable;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1194,9 +1204,6 @@ static int spk_event(struct snd_soc_dapm_widget *w,
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
- static unsigned int hp_out_enable;
-
switch (event) {
case SND_SOC_DAPM_POST_PMU:
pr_info("hp_event --SND_SOC_DAPM_POST_PMU\n");
@@ -1808,9 +1815,8 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
ret |= RT5640_U_IF3;
break;
-#if (CONFIG_SND_SOC_RT5643_MODULE | CONFIG_SND_SOC_RT5643 | \
- CONFIG_SND_SOC_RT5646_MODULE | CONFIG_SND_SOC_RT5646)
-
+#if defined(CONFIG_SND_SOC_RT5643_MODULE) || defined(CONFIG_SND_SOC_RT5643) || \
+ defined(CONFIG_SND_SOC_RT5646_MODULE) || defined(CONFIG_SND_SOC_RT5646)
case RT5640_AIF3:
if (val == RT5640_IF_312 || val == RT5640_IF_321)
ret |= RT5640_U_IF1;
@@ -1910,8 +1916,8 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
RT5640_I2S_DL_MASK, val_len);
snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
-#if (CONFIG_SND_SOC_RT5643_MODULE | CONFIG_SND_SOC_RT5643 | \
- CONFIG_SND_SOC_RT5646_MODULE | CONFIG_SND_SOC_RT5646)
+#if defined(CONFIG_SND_SOC_RT5643_MODULE) || defined(CONFIG_SND_SOC_RT5643) || \
+ defined(CONFIG_SND_SOC_RT5646_MODULE) || defined(CONFIG_SND_SOC_RT5646)
if (dai_sel & RT5640_U_IF3) {
mask_clk = RT5640_I2S_BCLK_MS3_MASK | RT5640_I2S_PD3_MASK;
val_clk = bclk_ms << RT5640_I2S_BCLK_MS3_SFT |
@@ -1994,8 +2000,8 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
RT5640_I2S_DF_MASK, reg_val);
}
-#if (CONFIG_SND_SOC_RT5643_MODULE | CONFIG_SND_SOC_RT5643 | \
- CONFIG_SND_SOC_RT5646_MODULE | CONFIG_SND_SOC_RT5646)
+#if defined(CONFIG_SND_SOC_RT5643_MODULE) || defined(CONFIG_SND_SOC_RT5643) || \
+ defined(CONFIG_SND_SOC_RT5646_MODULE) || defined(CONFIG_SND_SOC_RT5646)
if (dai_sel & RT5640_U_IF3) {
snd_soc_update_bits(codec, RT5640_I2S3_SDP,
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
@@ -2123,10 +2129,8 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
break;
case RT5640_PLL1_S_BCLK1:
case RT5640_PLL1_S_BCLK2:
-
-#if (CONFIG_SND_SOC_RT5643_MODULE | CONFIG_SND_SOC_RT5643 | \
- CONFIG_SND_SOC_RT5646_MODULE | CONFIG_SND_SOC_RT5646)
-
+#if defined(CONFIG_SND_SOC_RT5643_MODULE) || defined(CONFIG_SND_SOC_RT5643) || \
+ defined(CONFIG_SND_SOC_RT5646_MODULE) || defined(CONFIG_SND_SOC_RT5646)
case RT5640_PLL1_S_BCLK3:
#endif
@@ -2346,8 +2350,7 @@ static int rt5640_probe(struct snd_soc_codec *codec)
rt5640_reg_init(codec);
#endif
-
-#if (CONFIG_SND_SOC_RT5642_MODULE | CONFIG_SND_SOC_RT5642)
+#if defined(CONFIG_SND_SOC_RT5642_MODULE) || defined(CONFIG_SND_SOC_RT5642)
rt5640_register_dsp(codec);
#endif
@@ -2446,8 +2449,8 @@ struct snd_soc_dai_driver rt5640_dai[] = {
},
.ops = &rt5640_aif_dai_ops,
},
-#if (CONFIG_SND_SOC_RT5643_MODULE | CONFIG_SND_SOC_RT5643 | \
- CONFIG_SND_SOC_RT5646_MODULE | CONFIG_SND_SOC_RT5646)
+#if defined(CONFIG_SND_SOC_RT5643_MODULE) || defined(CONFIG_SND_SOC_RT5643) || \
+ defined(CONFIG_SND_SOC_RT5646_MODULE) || defined(CONFIG_SND_SOC_RT5646)
{
.name = "rt5640-aif3",
.id = RT5640_AIF3,
diff --git a/sound/soc/codecs/second_rate_pps_driver.h b/sound/soc/codecs/second_rate_pps_driver.h
index c6c128a027dd..9956c08bcd80 100644
--- a/sound/soc/codecs/second_rate_pps_driver.h
+++ b/sound/soc/codecs/second_rate_pps_driver.h
@@ -14,13 +14,6 @@ static control main44_VOLUME_controls[] = {
static char *main44_VOLUME_control_names[] = {
};
-
-
-static char *main44_REG_Section_names[] = {
- "miniDSP_A_reg_values",
- "miniDSP_D_reg_values",
-};
-
reg_value main44_REG_Section_init_program[] = {
{ 0,0x0},
{ 0x7F,0x00},
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index 7c1bf5c04eb9..6173b3a5b082 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -8,12 +8,15 @@
* Author: Steve Chen, <schen@mvista.com>
* Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
* Copyright: (C) 2009 Texas Instruments, India
+ * Copyright: (C) 2009-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+#include <asm/mach-types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
@@ -26,9 +29,34 @@
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-
static struct snd_soc_codec_driver soc_codec_spdif_dit;
+static int spdif_probe(struct snd_soc_codec *codec) {
+ codec->dapm.idle_bias_off = 1;
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget spdif_dapm_widgets[] = {
+ SND_SOC_DAPM_VMID("spdif dummy Vmid"),
+};
+
+static int spdif_write(struct snd_soc_codec * codec, unsigned int reg,
+ unsigned int val){
+ return 0;
+}
+
+static int spdif_read(struct snd_soc_codec * codec, unsigned int reg){
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_spdif_dit1 = {
+ .probe = spdif_probe,
+ .dapm_widgets = spdif_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(spdif_dapm_widgets),
+ .read = spdif_read,
+ .write = spdif_write,
+};
+
static struct snd_soc_dai_driver dit_stub_dai = {
.name = "dit-hifi",
.playback = {
@@ -49,8 +77,12 @@ static struct snd_soc_dai_driver dit_stub_dai = {
static int spdif_dit_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit,
- &dit_stub_dai, 1);
+ if(machine_is_kai() || machine_is_tegra_enterprise())
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_spdif_dit1, &dit_stub_dai, 1);
+ else
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_spdif_dit, &dit_stub_dai, 1);
}
static int spdif_dit_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/tlv320aic326x.c b/sound/soc/codecs/tlv320aic326x.c
index 8bbd295a3328..5d6b92904d56 100644
--- a/sound/soc/codecs/tlv320aic326x.c
+++ b/sound/soc/codecs/tlv320aic326x.c
@@ -81,9 +81,6 @@ static u8 aic3262_reg_ctl;
* This function reprograms the clock dividers etc. this flag can be used to
* disable this when the clock dividers are programmed by pps config file
*/
-static int soc_static_freq_config = 1;
-static struct aic3262_priv *aic3262_priv_data;
-static struct i2c_client *i2c_pdev;
static struct snd_soc_codec *aic3262_codec;
/*
@@ -849,7 +846,6 @@ static const struct aic3262_rate_divs aic3262_divs[] = {
static void aic3262_multi_i2s_dump_regs(struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
unsigned int counter;
DBG(KERN_INFO "#%s: Dai Active %d ASI%d REGS DUMP\n",
@@ -1965,7 +1961,7 @@ static int aic3262_multi_i2s_hw_params(struct snd_pcm_substream *substream,
* We can use this function to disable the DAC and ADC specific inputs from the
* individual ASI Ports of the Audio Codec.
*/
-static int aic3262_multi_i2s_shutdown(struct snd_pcm_substream *substream,
+static void aic3262_multi_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -2106,7 +2102,7 @@ static int aic3262_multi_i2s_shutdown(struct snd_pcm_substream *substream,
aic3262->active_count--;
}
err:
- return 0;
+ return;
}
@@ -2547,13 +2543,13 @@ static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("MAL Switch", HP_AMP_CNTL_R1, 7, 1, 0),
SOC_DAPM_SINGLE("LDAC Switch", HP_AMP_CNTL_R1, 5, 1, 0),
SOC_DAPM_SINGLE_TLV("LOL-B1 Volume", HP_AMP_CNTL_R2, 0,
- 0x7f, 1, lo_hp_tlv),
+ 0x7f, 0, lo_hp_tlv),
};
/* Right HPR Mixer */
static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("LOR-B1 Volume", HP_AMP_CNTL_R3, 0,
- 0x7f, 1, lo_hp_tlv),
+ 0x7f, 0, lo_hp_tlv),
SOC_DAPM_SINGLE("LDAC Switch", HP_AMP_CNTL_R1, 2, 1, 0),
SOC_DAPM_SINGLE("RDAC Switch", HP_AMP_CNTL_R1, 4, 1, 0),
SOC_DAPM_SINGLE("MAR Switch", HP_AMP_CNTL_R1, 6, 1, 0),
@@ -3565,7 +3561,7 @@ void aic3262_write_reg_cache(struct snd_soc_codec *codec,
*----------------------------------------------------------------------------
*/
-u8 aic3262_read(struct snd_soc_codec *codec, u16 reg)
+unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg)
{
struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
u8 value;
@@ -3573,7 +3569,7 @@ u8 aic3262_read(struct snd_soc_codec *codec, u16 reg)
u16 *cache = codec->reg_cache;
u16 cmd;
u8 buffer[2];
- int rc;
+ int rc = 0;
reg = reg % 128;
if (reg >= AIC3262_CACHEREGNUM) {
@@ -3619,7 +3615,8 @@ u8 aic3262_read(struct snd_soc_codec *codec, u16 reg)
*
*----------------------------------------------------------------------------
*/
-int aic3262_write(struct snd_soc_codec *codec, u16 reg, u8 value)
+int aic3262_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
{
struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
u8 data[2];
@@ -3855,8 +3852,6 @@ int i2c_verify_book0(struct snd_soc_codec *codec)
static int aic3262_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
- u8 value;
switch (level) {
/* full On */
case SND_SOC_BIAS_ON:
@@ -4400,6 +4395,7 @@ static int aic3262_spi_write(struct spi_device *spi, const char *data, int len)
return len;
}
+#ifdef RUN_DELAYED_WORK
/*
* This function forces any delayed work to be queued and run.
*/
@@ -4418,6 +4414,8 @@ static int run_delayed_work(struct delayed_work *dwork)
}
return ret;
}
+#endif
+
static int __devinit aic3262_spi_probe(struct spi_device *spi)
{
int ret;
diff --git a/sound/soc/codecs/tlv320aic326x.h b/sound/soc/codecs/tlv320aic326x.h
index a31cc9eca5ca..bfcbefc5c079 100644
--- a/sound/soc/codecs/tlv320aic326x.h
+++ b/sound/soc/codecs/tlv320aic326x.h
@@ -36,7 +36,7 @@
/* Enable register caching on write */
#define EN_REG_CACHE 1
-#define MULTIBYTE_CONFIG_SUPPORT
+//#define MULTIBYTE_CONFIG_SUPPORT
/*Setting all codec reg/write locally*/
/* This definition is added as the snd_ direct call are
@@ -45,12 +45,12 @@ page, so fix that before commenting this line*/
#define LOCAL_REG_ACCESS 1
/* Macro to enable the inclusion of tiload kernel driver */
-#define AIC3262_TiLoad
+//#define AIC3262_TiLoad
/* Macro enables or disables support for miniDSP in the driver */
/* Enable the AIC3262_TiLoad macro first before enabling these macros */
-#define CONFIG_MINI_DSP
+//#define CONFIG_MINI_DSP
/*#undef CONFIG_MINI_DSP*/
/* Enable or disable controls to have Input routing*/
@@ -657,11 +657,12 @@ extern int aic326x_headset_detect(struct snd_soc_codec *codec,
extern int aic326x_headset_button_init(struct snd_soc_codec *codec,
struct snd_soc_jack *jack, int jack_type);
-extern u8 aic3262_read(struct snd_soc_codec *codec, u16 reg);
+extern unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg);
extern u16 aic3262_read_2byte(struct snd_soc_codec *codec, u16 reg);
extern int aic3262_reset_cache(struct snd_soc_codec *codec);
extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page);
-extern int aic3262_write(struct snd_soc_codec *codec, u16 reg, u8 value);
+extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value);
extern void aic3262_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, u8 value);
extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book);
diff --git a/sound/soc/codecs/tlv320aic326x_mini-dsp.c b/sound/soc/codecs/tlv320aic326x_mini-dsp.c
index 6d55abb4dac8..0ef0fd09760c 100644
--- a/sound/soc/codecs/tlv320aic326x_mini-dsp.c
+++ b/sound/soc/codecs/tlv320aic326x_mini-dsp.c
@@ -601,25 +601,27 @@ struct process_flow{
int
set_minidsp_mode(struct snd_soc_codec *codec, int new_mode, int new_config)
{
-
- if (codec == NULL) {
- printk(KERN_INFO "%s codec is NULL\n",__func__);
- }
- struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct aic3262_priv *aic326x;
+ struct snd_soc_dapm_context *dapm;
struct process_flow * pflows = &miniDSP_programs[new_mode];
- u8 reg63, reg81, pll_pow, ndac_pow, mdac_pow, nadc_pow, madc_pow;
-
+ u8 pll_pow, ndac_pow, mdac_pow, nadc_pow;
u8 adc_status,dac_status;
- u8 reg, val;
- u8 shift;
- volatile u16 counter;
- int (*ptransfer)(struct snd_soc_codec *codec,
- reg_value *program_ptr,
- int size);
+ int (*ptransfer)(struct snd_soc_codec *codec, reg_value *program_ptr,
+ int size);
printk("%s:New Switch mode = %d New Config= %d\n", __func__, new_mode,new_config);
+
+ if (codec == NULL) {
+ printk(KERN_INFO "%s codec is NULL\n", __func__);
+ return 0;
+ }
+ aic326x = snd_soc_codec_get_drvdata(codec);
+ dapm = &codec->dapm;
+
+ printk(KERN_INFO "%s:New Switch mode = %d New Config= %d\n", __func__,
+ new_mode, new_config);
+
if (new_mode >= ARRAY_SIZE(miniDSP_programs))
return 0; // error condition
if (new_config > MAXCONFIG)
@@ -1231,7 +1233,7 @@ static int __new_control_put_minidsp_mux(struct snd_kcontrol *kcontrol,
int ret_val = -1, array_size;
control *array;
char **array_names;
- char *control_name, *control_name1, *control_name2;
+ char *control_name, *control_name1;
struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
i2c = codec->control_data;
diff --git a/sound/soc/codecs/tlv320aic326x_minidsp_config.c b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
index e34ffbe2ca82..97a41c3ecf2b 100644
--- a/sound/soc/codecs/tlv320aic326x_minidsp_config.c
+++ b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
@@ -347,17 +347,12 @@ int aic3262_add_multiconfig_controls(struct snd_soc_codec *codec)
*---------------------------------------------------------------------------
*/
void minidsp_multiconfig(struct snd_soc_codec *codec,
- reg_value *a_patch, int a_size,
- reg_value *d_patch, int d_size)
+ reg_value *a_patch, int a_size, reg_value *d_patch, int d_size)
{
- struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
- int val1,val2;
int adc_status,dac_status;
int (*ptransfer)(struct snd_soc_codec *codec,
- reg_value *program_ptr,
- int size);
-
-printk("======in the config_multiconfiguration function==== \n");
+ reg_value *program_ptr, int size);
+ printk(KERN_INFO "======in the config_multiconfiguration function====\n");
#ifndef MULTIBYTE_I2C
ptransfer = byte_i2c_array_transfer;
#else
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 0218d6ce0557..2a2d16886989 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1404,6 +1404,7 @@ static struct {
{ 1500, 0x9, 0x2, 2 },
};
+#ifdef SYS_BCLK_RATIO
/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */
static struct {
int ratio;
@@ -1427,6 +1428,7 @@ static struct {
{ 440, 19 },
{ 480, 20 },
};
+#endif
/* Sample rates for DSP */
static struct {
@@ -1778,9 +1780,6 @@ static int wm8903_resume(struct snd_soc_codec *codec)
if (wm8903->irq)
enable_irq(wm8903->irq);
- /* Bring the codec back up to standby first to minimise pop/clicks */
- wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
/* Sync back everything else */
if (tmp_cache) {
for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
@@ -1791,6 +1790,9 @@ static int wm8903_resume(struct snd_soc_codec *codec)
dev_err(codec->dev, "Failed to allocate temporary cache\n");
}
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
return 0;
}
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index de9abd032fd4..5cfcc655e95f 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -180,13 +180,8 @@ static int siu_pcm_rd_set(struct siu_port *port_info,
sg_dma_len(&sg) = size;
sg_dma_address(&sg) = buff;
-<<<<<<< HEAD
- desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
- &sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-=======
desc = dmaengine_prep_slave_sg(siu_stream->chan,
&sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers
if (!desc) {
dev_err(dev, "Failed to allocate dma descriptor\n");
return -ENOMEM;
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index d546046d1e1d..9c4346aa265c 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -2,6 +2,7 @@ GCOV_PROFILE := y
# Tegra platform Support
snd-soc-tegra-pcm-objs := tegra_pcm.o
+snd-soc-tegra-tdm-pcm-objs := tegra_tdm_pcm.o
snd-soc-tegra20-spdif-objs := tegra20_spdif.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
snd-soc-tegra20-das-objs := tegra20_das.o
@@ -13,6 +14,7 @@ snd-soc-tegra30-dam-objs := tegra30_dam.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-tdm-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index e6de1c07a399..d90c0991ed65 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -191,8 +191,9 @@ static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
u32 reg;
int ret, sample_size, srate, i2sclock, bitcnt, i2sclk_div;
+ u32 bit_format = i2s->reg_ctrl & TEGRA20_I2S_CTRL_BIT_FORMAT_MASK;
- if ((i2s->reg_ctrl & TEGRA20_I2S_CTRL_BIT_FORMAT_I2S) &&
+ if ((bit_format == TEGRA20_I2S_CTRL_BIT_FORMAT_I2S) &&
(params_channels(params) != 2)) {
dev_err(dev, "Only Stereo is supported in I2s mode\n");
return -EINVAL;
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 710d9465b4b0..30b7e481acc4 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -116,6 +116,15 @@ void tegra30_ahub_disable_clocks(void)
clk_disable(ahub->clk_d_audio);
}
+/*
+ * for TDM mode, ahub has to run faster than I2S controller. This will avoid
+ * FIFO overflow/underflow, the causes of slot-hopping symptoms
+ */
+void tegra30_ahub_clock_set_rate(int rate)
+{
+ clk_set_rate(ahub->clk_d_audio, rate);
+}
+
#ifdef CONFIG_DEBUG_FS
static inline u32 tegra30_ahub_read(u32 space, u32 reg)
{
@@ -272,6 +281,75 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
return 0;
}
+int tegra30_ahub_rx_fifo_is_enabled(int i2s_id)
+{
+ int val, mask;
+
+ val = tegra30_apbif_read(TEGRA30_AHUB_I2S_LIVE_STATUS);
+ mask = (TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED << (i2s_id*2));
+ val &= mask;
+ return val;
+}
+
+int tegra30_ahub_tx_fifo_is_enabled(int i2s_id)
+{
+ int val, mask;
+
+ val = tegra30_apbif_read(TEGRA30_AHUB_I2S_LIVE_STATUS);
+ mask = (TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED << (i2s_id*2));
+ val &= mask;
+
+ return val;
+}
+
+int tegra30_ahub_set_rx_fifo_pack_mode(enum tegra30_ahub_rxcif rxcif,
+ unsigned int pack_mode)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ tegra30_ahub_enable_clocks();
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK;
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN;
+
+ if ((pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16) ||
+ (pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4))
+ val |= (TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+ pack_mode);
+ tegra30_apbif_write(reg, val);
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_set_tx_fifo_pack_mode(enum tegra30_ahub_txcif txcif,
+ unsigned int pack_mode)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ tegra30_ahub_enable_clocks();
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK;
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN;
+
+ if ((pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16) ||
+ (pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4))
+ val |= (TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+ pack_mode);
+ tegra30_apbif_write(reg, val);
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
@@ -480,6 +558,54 @@ int tegra30_ahub_set_tx_cif_channels(enum tegra30_ahub_txcif txcif,
return 0;
}
+int tegra30_ahub_set_rx_cif_bits(enum tegra30_ahub_rxcif rxcif,
+ unsigned int audio_bits,
+ unsigned int client_bits)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ unsigned int reg, val;
+
+ tegra30_ahub_enable_clocks();
+
+ reg = TEGRA30_AHUB_CIF_RX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK);
+ val |= ((audio_bits) << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ ((client_bits) << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_set_tx_cif_bits(enum tegra30_ahub_txcif txcif,
+ unsigned int audio_bits,
+ unsigned int client_bits)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ unsigned int reg, val;
+
+ tegra30_ahub_enable_clocks();
+
+ reg = TEGRA30_AHUB_CIF_TX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK);
+ val |= ((audio_bits) << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ ((client_bits) << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+
static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
{
struct resource *res0, *res1, *region;
@@ -507,6 +633,7 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
goto err_free;
}
clkm_rate = clk_get_rate(clk_get_parent(ahub->clk_d_audio));
+
while (clkm_rate > 12000000)
clkm_rate >>= 1;
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
index 7de1b7c86c7f..8dc27abc5aed 100644
--- a/sound/soc/tegra/tegra30_ahub.h
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -464,6 +464,7 @@ enum tegra30_ahub_rxcif {
extern void tegra30_ahub_enable_clocks(void);
extern void tegra30_ahub_disable_clocks(void);
+extern void tegra30_ahub_clock_set_rate(int rate);
extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
unsigned long *fiforeg,
@@ -471,8 +472,13 @@ extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
extern int tegra30_ahub_set_rx_cif_channels(enum tegra30_ahub_rxcif rxcif,
unsigned int audio_ch,
unsigned int client_ch);
+extern int tegra30_ahub_set_rx_cif_bits(enum tegra30_ahub_rxcif rxcif,
+ unsigned int audio_bits,
+ unsigned int client_bits);
extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_set_rx_fifo_pack_mode(enum tegra30_ahub_rxcif rxcif,
+ unsigned int pack_mode);
extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif);
extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
@@ -481,14 +487,22 @@ extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
extern int tegra30_ahub_set_tx_cif_channels(enum tegra30_ahub_txcif txcif,
unsigned int audio_ch,
unsigned int client_ch);
+extern int tegra30_ahub_set_tx_cif_bits(enum tegra30_ahub_txcif txcif,
+ unsigned int audio_bits,
+ unsigned int client_bits);
extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_set_tx_fifo_pack_mode(enum tegra30_ahub_txcif txcif,
+ unsigned int pack_mode);
extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_rx_fifo_is_enabled(int i2s_id);
+extern int tegra30_ahub_tx_fifo_is_enabled(int i2s_id);
+
#ifdef CONFIG_PM
extern int tegra30_ahub_apbif_resume(void);
#endif
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 02d1038ea36e..449359837efb 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -36,6 +36,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/delay.h>
#include <mach/iomap.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -280,6 +281,194 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
return 0;
}
+static void tegra30_i2s_set_channel_bit_count(struct tegra30_i2s *i2s,
+ int i2sclock, int srate)
+{
+ int sym_bitclk, bitcnt;
+ u32 val;
+
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ sym_bitclk = !(i2sclock % (2 * srate));
+
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (!sym_bitclk)
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+}
+
+static void tegra30_i2s_set_data_offset(struct tegra30_i2s *i2s)
+{
+ u32 val;
+ int rx_data_offset = i2s->dsp_config.rx_data_offset;
+ int tx_data_offset = i2s->dsp_config.tx_data_offset;
+
+ val = (rx_data_offset <<
+ TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+ (tx_data_offset <<
+ TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val);
+}
+
+static void tegra30_i2s_set_slot_control(struct tegra30_i2s *i2s, int stream)
+{
+ u32 val;
+ int tx_mask = i2s->dsp_config.tx_mask;
+ int rx_mask = i2s->dsp_config.rx_mask;
+
+ val = tegra30_i2s_read(i2s, TEGRA30_I2S_SLOT_CTRL);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val &= ~TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK;
+ val |= (tx_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT);
+ } else {
+ val &= ~TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK;
+ val |= (rx_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT);
+ }
+
+ val &= ~TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK;
+ val |= (i2s->dsp_config.num_slots - 1)
+ << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT;
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_SLOT_CTRL, val);
+}
+
+static int tegra30_i2s_tdm_setup_clocks(struct device *dev,
+ struct tegra30_i2s *i2s, int *i2sclock)
+{
+ int ret;
+
+ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
+ if (ret) {
+ dev_err(dev, "Can't set parent of I2S clock\n");
+ return ret;
+ }
+ ret = clk_set_rate(i2s->clk_i2s, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+ } else {
+
+ ret = clk_set_rate(i2s->clk_i2s_sync, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S sync clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_audio_2x, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set audio2x clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
+ if (ret) {
+ dev_err(dev, "Can't set parent of audio2x clock\n");
+ return ret;
+ }
+ }
+ return ret;
+}
+
+
+static int tegra30_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ int i2s_client_ch, i2s_audio_ch, i2s_audio_bits, i2s_client_bits;
+ int i2sclock, srate;
+ int ret;
+
+ srate = params_rate(params);
+
+ i2sclock = srate *
+ i2s->dsp_config.num_slots *
+ i2s->dsp_config.slot_width;
+
+ ret = tegra30_i2s_tdm_setup_clocks(dev, i2s, &i2sclock);
+ if (ret)
+ return -EINVAL;
+
+ /* Run ahub clock greater than i2sclock */
+ tegra30_ahub_clock_set_rate(i2sclock*2);
+
+ tegra30_i2s_enable_clocks(i2s);
+
+ tegra30_i2s_set_channel_bit_count(i2s, i2sclock*2, srate);
+
+ i2s_client_ch = i2s->dsp_config.num_slots;
+ i2s_audio_ch = i2s->dsp_config.num_slots;
+
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
+ switch (i2s->dsp_config.slot_width) {
+ case 16:
+ i2s_audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+ i2s_client_bits = TEGRA30_AUDIOCIF_BITS_16;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16;
+ break;
+ case 32:
+ i2s_audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ i2s_client_bits = TEGRA30_AUDIOCIF_BITS_32;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_32;
+ break;
+ }
+
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((i2s_audio_ch - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((i2s_client_ch - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ (i2s_audio_bits <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ (i2s_client_bits <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+
+ tegra30_ahub_set_tx_cif_channels(i2s->txcif,
+ i2s_audio_ch,
+ i2s_client_ch);
+ tegra30_ahub_set_tx_cif_bits(i2s->txcif,
+ i2s_audio_bits,
+ i2s_client_bits);
+ tegra30_ahub_set_tx_fifo_pack_mode(i2s->txcif, 0);
+
+ } else {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val);
+
+ tegra30_ahub_set_rx_cif_channels(i2s->rxcif,
+ i2s_audio_ch,
+ i2s_client_ch);
+ tegra30_ahub_set_rx_cif_bits(i2s->rxcif,
+ i2s_audio_bits,
+ i2s_client_bits);
+ tegra30_ahub_set_rx_fifo_pack_mode(i2s->rxcif, 0);
+ }
+
+ tegra30_i2s_set_slot_control(i2s, substream->stream);
+
+ tegra30_i2s_set_data_offset(i2s);
+
+ i2s->reg_ch_ctrl &= ~TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK;
+ i2s->reg_ch_ctrl |= (i2s->dsp_config.slot_width - 1) <<
+ TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CH_CTRL, i2s->reg_ch_ctrl);
+
+ tegra30_i2s_disable_clocks(i2s);
+
+ return 0;
+}
+
static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -300,6 +489,12 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* TDM mode */
+ if ((i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) &&
+ (i2s->dsp_config.slot_width > 2))
+ return tegra30_i2s_tdm_hw_params(substream, params, dai);
+
+
srate = params_rate(params);
if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
@@ -423,12 +618,15 @@ static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s)
static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
{
- tegra30_ahub_disable_tx_fifo(i2s->txcif);
+ int dcnt = 10;
/* if this is the only user of i2s tx then disable it*/
+ tegra30_ahub_disable_tx_fifo(i2s->txcif);
if (i2s->playback_ref_count == 1) {
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
}
+ while (tegra30_ahub_tx_fifo_is_enabled(i2s->id) && dcnt--)
+ udelay(100);
}
static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
@@ -442,11 +640,14 @@ static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
{
+ int dcnt = 10;
tegra30_ahub_disable_rx_fifo(i2s->rxcif);
if (!i2s->is_call_mode_rec) {
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
}
+ while (tegra30_ahub_rx_fifo_is_enabled(i2s->id) && dcnt--)
+ udelay(100);
}
static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -500,6 +701,33 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
tegra30_i2s_disable_clocks(i2s);
#endif
+ /* Default values for DSP mode */
+ i2s->dsp_config.num_slots = 1;
+ i2s->dsp_config.slot_width = 2;
+ i2s->dsp_config.tx_mask = 1;
+ i2s->dsp_config.rx_mask = 1;
+ i2s->dsp_config.rx_data_offset = 1;
+ i2s->dsp_config.tx_data_offset = 1;
+
+
+ return 0;
+}
+
+int tegra30_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots,
+ int slot_width)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ i2s->dsp_config.num_slots = slots;
+ i2s->dsp_config.slot_width = slot_width;
+ i2s->dsp_config.tx_mask = tx_mask;
+ i2s->dsp_config.rx_mask = rx_mask;
+ i2s->dsp_config.rx_data_offset = 0;
+ i2s->dsp_config.tx_data_offset = 0;
+
return 0;
}
@@ -534,6 +762,7 @@ static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.set_fmt = tegra30_i2s_set_fmt,
.hw_params = tegra30_i2s_hw_params,
.trigger = tegra30_i2s_trigger,
+ .set_tdm_slot = tegra30_i2s_set_tdm_slot,
};
#define TEGRA30_I2S_DAI(id) \
@@ -543,13 +772,13 @@ static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.resume = tegra30_i2s_resume, \
.playback = { \
.channels_min = 1, \
- .channels_max = 2, \
+ .channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.capture = { \
.channels_min = 1, \
- .channels_max = 2, \
+ .channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
@@ -726,10 +955,7 @@ int tegra30_make_voice_call_connections(struct codec_config *codec_info,
tegra30_dam_enable(bb_i2s->dam_ifc, TEGRA30_DAM_ENABLE,
TEGRA30_DAM_CHIN0_SRC);
- /* if this is the only user of i2s tx then enable it*/
- if (codec_i2s->playback_ref_count == 1)
- codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
-
+ codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX;
tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL,
codec_i2s->reg_ctrl);
@@ -827,7 +1053,6 @@ static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
ret = PTR_ERR(i2s->clk_i2s);
goto exit;
}
-
i2s->clk_i2s_sync = clk_get(&pdev->dev, "ext_audio_sync");
if (IS_ERR(i2s->clk_i2s_sync)) {
dev_err(&pdev->dev, "Can't retrieve i2s_sync clock\n");
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index b9baddd5db8e..0992bf0d5b17 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -176,7 +176,7 @@
/* Number of slots in frame, minus 1 */
#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16
#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7
-#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT)
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT)
/* TDM mode slot enable bitmask */
#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8
@@ -231,6 +231,16 @@
/* Number of i2s controllers*/
#define TEGRA30_NR_I2S_IFC 5
+struct dsp_config_t {
+ int num_slots;
+ int rx_mask;
+ int tx_mask;
+ int slot_width;
+ int rx_data_offset;
+ int tx_data_offset;
+};
+
+
struct tegra30_i2s {
int id;
struct clk *clk_i2s;
@@ -254,6 +264,8 @@ struct tegra30_i2s {
#endif
int call_record_dam_ifc;
int is_call_mode_rec;
+
+ struct dsp_config_t dsp_config;
};
struct codec_config {
diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c
index 93013c5e99b6..a5c6fc0aecb7 100644
--- a/sound/soc/tegra/tegra_aic326x.c
+++ b/sound/soc/tegra/tegra_aic326x.c
@@ -1142,8 +1142,10 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev)
struct snd_soc_card *card = &snd_soc_tegra_aic326x;
struct tegra_aic326x *machine;
struct tegra_asoc_platform_data *pdata;
- int ret, i;
-
+ int ret;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ int i;
+#endif
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data supplied\n");
@@ -1158,7 +1160,7 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index 04e7e3c15cd0..b134f0808afa 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -153,12 +153,12 @@ int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data)
EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_disable);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
- struct device *dev)
+ struct device *dev, struct snd_soc_card *card)
{
int ret;
- int rate;
data->dev = dev;
+ data->card = card;
data->clk_pll_p_out1 = clk_get_sys(NULL, "pll_p_out1");
if (IS_ERR(data->clk_pll_p_out1)) {
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index 1c4e521cb4ba..512df0d54eb1 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -31,6 +31,7 @@ struct device;
struct tegra_asoc_utils_data {
struct device *dev;
+ struct snd_soc_card *card;
struct clk *clk_pll_a;
struct clk *clk_pll_a_out0;
struct clk *clk_cdev1;
@@ -47,7 +48,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
void tegra_asoc_utils_lock_clk_rate(struct tegra_asoc_utils_data *data,
int lock);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
- struct device *dev);
+ struct device *dev, struct snd_soc_card *card);
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data);
int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data);
diff --git a/sound/soc/tegra/tegra_max98088.c b/sound/soc/tegra/tegra_max98088.c
index 9546af83b39d..8c0e3935ad02 100644
--- a/sound/soc/tegra/tegra_max98088.c
+++ b/sound/soc/tegra/tegra_max98088.c
@@ -96,6 +96,7 @@ struct tegra_max98088 {
#endif
enum snd_soc_bias_level bias_level;
struct snd_soc_card *pcard;
+ volatile int clock_enabled;
};
static int tegra_call_mode_info(struct snd_kcontrol *kcontrol,
@@ -559,7 +560,7 @@ static void tegra_max98088_shutdown(struct snd_pcm_substream *substream)
} else {
if (!i2s->is_call_mode_rec)
- return 0;
+ return;
i2s->is_call_mode_rec = 0;
@@ -926,6 +927,7 @@ static int tegra_max98088_init(struct snd_soc_pcm_runtime *rtd)
machine->pcard = card;
machine->bias_level = SND_SOC_BIAS_STANDBY;
+ machine->clock_enabled = 1;
if (gpio_is_valid(pdata->gpio_spkr_en)) {
ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
@@ -1065,25 +1067,30 @@ static struct snd_soc_dai_link tegra_max98088_dai[NUM_DAI_LINKS] = {
};
static int tegra30_soc_set_bias_level(struct snd_soc_card *card,
- enum snd_soc_bias_level level)
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
{
struct tegra_max98088 *machine = snd_soc_card_get_drvdata(card);
if (machine->bias_level == SND_SOC_BIAS_OFF &&
- level != SND_SOC_BIAS_OFF)
+ level != SND_SOC_BIAS_OFF && (!machine->clock_enabled)) {
+ machine->clock_enabled = 1;
tegra_asoc_utils_clk_enable(&machine->util_data);
+ machine->bias_level = level;
+ }
return 0;
}
static int tegra30_soc_set_bias_level_post(struct snd_soc_card *card,
- enum snd_soc_bias_level level)
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
{
struct tegra_max98088 *machine = snd_soc_card_get_drvdata(card);
if (machine->bias_level != SND_SOC_BIAS_OFF &&
- level == SND_SOC_BIAS_OFF)
+ level == SND_SOC_BIAS_OFF && (machine->clock_enabled)) {
+ machine->clock_enabled = 0;
tegra_asoc_utils_clk_disable(&machine->util_data);
+ }
machine->bias_level = level;
@@ -1119,7 +1126,7 @@ static __devinit int tegra_max98088_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
@@ -1156,6 +1163,7 @@ static __devinit int tegra_max98088_driver_probe(struct platform_device *pdev)
tegra_max98088_i2s_dai_name[machine->codec_info[BT_SCO].i2s_id];
#endif
+ card->dapm.idle_bias_off = 1;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
diff --git a/sound/soc/tegra/tegra_max98095.c b/sound/soc/tegra/tegra_max98095.c
index 2104ba849cd3..95295ef4151e 100644
--- a/sound/soc/tegra/tegra_max98095.c
+++ b/sound/soc/tegra/tegra_max98095.c
@@ -630,7 +630,7 @@ static __devinit int tegra_max98095_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
diff --git a/sound/soc/tegra/tegra_p1852.c b/sound/soc/tegra/tegra_p1852.c
index 27a1ea590340..9506a1c842df 100644
--- a/sound/soc/tegra/tegra_p1852.c
+++ b/sound/soc/tegra/tegra_p1852.c
@@ -201,7 +201,7 @@ static __devinit int tegra_p1852_driver_probe(struct platform_device *pdev)
pdata->codec_info[i].name;
}
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 00bd7aa2e59d..1b4b949841aa 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -2,7 +2,7 @@
* tegra_pcm.c - Tegra PCM driver
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
*
* Based on code copyright/by:
*
@@ -29,6 +29,7 @@
*
*/
+#include <asm/mach-types.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -66,7 +67,8 @@ static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
unsigned long addr;
dma_req = &prtd->dma_req[prtd->dma_req_idx];
- prtd->dma_req_idx = 1 - prtd->dma_req_idx;
+ if (++prtd->dma_req_idx >= prtd->dma_req_count)
+ prtd->dma_req_idx -= prtd->dma_req_count;
addr = buf->addr + prtd->dma_pos;
prtd->dma_pos += dma_req->size;
@@ -130,13 +132,16 @@ static void setup_dma_rx_request(struct tegra_dma_req *req,
req->req_sel = dmap->req_sel;
}
-static int tegra_pcm_open(struct snd_pcm_substream *substream)
+int tegra_pcm_allocate(struct snd_pcm_substream *substream,
+ int dma_mode,
+ const struct snd_pcm_hardware *pcm_hardware)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct tegra_pcm_dma_params * dmap;
int ret = 0;
+ int i = 0;
prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
if (prtd == NULL)
@@ -148,13 +153,14 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream)
spin_lock_init(&prtd->lock);
dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd->dma_req_count = MAX_DMA_REQ_COUNT;
if (dmap) {
- prtd->dma_req[0].dev = prtd;
- prtd->dma_req[1].dev = prtd;
+ for (i = 0; i < prtd->dma_req_count; i++)
+ prtd->dma_req[i].dev = prtd;
prtd->dma_chan = tegra_dma_allocate_channel(
- TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
+ dma_mode,
"pcm");
if (prtd->dma_chan == NULL) {
ret = -ENOMEM;
@@ -163,7 +169,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream)
}
/* Set HW params now that initialization is complete */
- snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+ snd_soc_set_runtime_hwparams(substream, pcm_hardware);
/* Ensure period size is multiple of 8 */
ret = snd_pcm_hw_constraint_step(runtime, 0,
@@ -189,7 +195,15 @@ err:
return ret;
}
-static int tegra_pcm_close(struct snd_pcm_substream *substream)
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
+{
+ return tegra_pcm_allocate(substream,
+ TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
+ &tegra_pcm_hardware);
+
+}
+
+int tegra_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd = runtime->private_data;
@@ -202,44 +216,55 @@ static int tegra_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
+int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct tegra_pcm_dma_params * dmap;
+ int i;
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ /* Limit dma_req_count to period count */
+ if (prtd->dma_req_count > params_periods(params))
+ prtd->dma_req_count = params_periods(params);
dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dmap) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- setup_dma_tx_request(&prtd->dma_req[0], dmap);
- setup_dma_tx_request(&prtd->dma_req[1], dmap);
+ for (i = 0; i < prtd->dma_req_count; i++)
+ setup_dma_tx_request(&prtd->dma_req[i], dmap);
} else {
- setup_dma_rx_request(&prtd->dma_req[0], dmap);
- setup_dma_rx_request(&prtd->dma_req[1], dmap);
+ for (i = 0; i < prtd->dma_req_count; i++)
+ setup_dma_rx_request(&prtd->dma_req[i], dmap);
}
}
- prtd->dma_req[0].size = params_period_bytes(params);
- prtd->dma_req[1].size = prtd->dma_req[0].size;
+ for (i = 0; i < prtd->dma_req_count; i++)
+ prtd->dma_req[i].size = params_period_bytes(params);
return 0;
}
-static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
+int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct tegra_pcm_dma_params * dmap;
unsigned long flags;
+ int i;
+
+ dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dmap)
+ return 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -253,8 +278,8 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock_irqsave(&prtd->lock, flags);
prtd->running = 1;
spin_unlock_irqrestore(&prtd->lock, flags);
- tegra_pcm_queue_dma(prtd);
- tegra_pcm_queue_dma(prtd);
+ for (i = 0; i < prtd->dma_req_count; i++)
+ tegra_pcm_queue_dma(prtd);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -263,10 +288,11 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
prtd->running = 0;
spin_unlock_irqrestore(&prtd->lock, flags);
tegra_dma_cancel(prtd->dma_chan);
- if (prtd->dma_req[0].status == -TEGRA_DMA_REQ_ERROR_ABORTED)
- prtd->dma_req[0].complete(&prtd->dma_req[0]);
- if (prtd->dma_req[1].status == -TEGRA_DMA_REQ_ERROR_ABORTED)
- prtd->dma_req[1].complete(&prtd->dma_req[1]);
+ for (i = 0; i < prtd->dma_req_count; i++) {
+ if (prtd->dma_req[i].status ==
+ -TEGRA_DMA_REQ_ERROR_ABORTED)
+ prtd->dma_req[i].complete(&prtd->dma_req[i]);
+ }
break;
default:
return -EINVAL;
@@ -275,7 +301,7 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
+snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd = runtime->private_data;
@@ -288,7 +314,7 @@ static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
bytes_to_frames(runtime, dma_transfer_count);
}
-static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
+int tegra_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -310,11 +336,11 @@ static struct snd_pcm_ops tegra_pcm_ops = {
.mmap = tegra_pcm_mmap,
};
-static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream , size_t size)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = tegra_pcm_hardware.buffer_bytes_max;
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
@@ -329,7 +355,7 @@ static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
return 0;
}
-static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -349,7 +375,7 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
static u64 tegra_dma_mask = DMA_BIT_MASK(32);
-static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
+int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd, size_t size)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
@@ -363,14 +389,16 @@ static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (dai->driver->playback.channels_min) {
ret = tegra_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
+ SNDRV_PCM_STREAM_PLAYBACK,
+ size);
if (ret)
goto err;
}
if (dai->driver->capture.channels_min) {
ret = tegra_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
+ SNDRV_PCM_STREAM_CAPTURE,
+ size);
if (ret)
goto err_free_play;
}
@@ -383,16 +411,31 @@ err:
return ret;
}
-static void tegra_pcm_free(struct snd_pcm *pcm)
+int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return tegra_pcm_dma_allocate(rtd ,
+ tegra_pcm_hardware.buffer_bytes_max);
+}
+
+void tegra_pcm_free(struct snd_pcm *pcm)
{
tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
}
+static int tegra_pcm_probe(struct snd_soc_platform *platform)
+{
+ if(machine_is_kai() || machine_is_tegra_enterprise())
+ platform->dapm.idle_bias_off = 1;
+
+ return 0;
+}
+
struct snd_soc_platform_driver tegra_pcm_platform = {
.ops = &tegra_pcm_ops,
.pcm_new = tegra_pcm_new,
.pcm_free = tegra_pcm_free,
+ .probe = tegra_pcm_probe,
};
static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index dbb90339fe0d..7fe22788004b 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -33,6 +33,8 @@
#include <mach/dma.h>
+#define MAX_DMA_REQ_COUNT 2
+
struct tegra_pcm_dma_params {
unsigned long addr;
unsigned long wrap;
@@ -48,8 +50,24 @@ struct tegra_runtime_data {
int dma_pos_end;
int period_index;
int dma_req_idx;
- struct tegra_dma_req dma_req[2];
+ struct tegra_dma_req dma_req[MAX_DMA_REQ_COUNT];
struct tegra_dma_channel *dma_chan;
+ int dma_req_count;
};
+int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
+int tegra_pcm_allocate(struct snd_pcm_substream *substream,
+ int dma_mode,
+ const struct snd_pcm_hardware *pcm_hardware);
+int tegra_pcm_close(struct snd_pcm_substream *substream);
+int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
+int tegra_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma);
+int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd, size_t size);
+void tegra_pcm_free(struct snd_pcm *pcm);
+snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream);
+int tegra_pcm_hw_free(struct snd_pcm_substream *substream);
+
#endif
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index e7d58803f7c9..231b0ee61308 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -69,6 +69,8 @@ struct tegra_rt5640 {
#ifdef CONFIG_SWITCH
int jack_status;
#endif
+ enum snd_soc_bias_level bias_level;
+ volatile int clock_enabled;
};
static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream,
@@ -279,7 +281,7 @@ static int tegra_rt5640_jack_notifier(struct notifier_block *self,
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
struct tegra_rt5640_platform_data *pdata = machine->pdata;
enum headset_state state = BIT_NO_HEADSET;
- unsigned char status_jack;
+ unsigned char status_jack = 0;
if (jack == &tegra_rt5640_hp_jack) {
if (action) {
@@ -532,6 +534,9 @@ static int tegra_rt5640_init(struct snd_soc_pcm_runtime *rtd)
machine->gpio_requested |= GPIO_HP_DET;
}
+ machine->bias_level = SND_SOC_BIAS_STANDBY;
+ machine->clock_enabled = 1;
+
ret = snd_soc_add_controls(codec, cardhu_controls,
ARRAY_SIZE(cardhu_controls));
if (ret < 0)
@@ -582,10 +587,58 @@ static struct snd_soc_dai_link tegra_rt5640_dai[] = {
},
};
+static int tegra_rt5640_resume_pre(struct snd_soc_card *card)
+{
+ int val;
+ struct snd_soc_jack_gpio *gpio = &tegra_rt5640_hp_jack_gpio;
+
+ if (gpio_is_valid(gpio->gpio)) {
+ val = gpio_get_value(gpio->gpio);
+ val = gpio->invert ? !val : val;
+ snd_soc_jack_report(gpio->jack, val, gpio->report);
+ }
+
+ return 0;
+}
+
+static int tegra_rt5640_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
+
+ if (machine->bias_level == SND_SOC_BIAS_OFF &&
+ level != SND_SOC_BIAS_OFF && (!machine->clock_enabled)) {
+ machine->clock_enabled = 1;
+ tegra_asoc_utils_clk_enable(&machine->util_data);
+ machine->bias_level = level;
+ }
+
+ return 0;
+}
+
+static int tegra_rt5640_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
+
+ if (machine->bias_level != SND_SOC_BIAS_OFF &&
+ level == SND_SOC_BIAS_OFF && machine->clock_enabled) {
+ machine->clock_enabled = 0;
+ tegra_asoc_utils_clk_disable(&machine->util_data);
+ }
+
+ machine->bias_level = level;
+
+ return 0 ;
+}
+
static struct snd_soc_card snd_soc_tegra_rt5640 = {
.name = "tegra-rt5640",
.dai_link = tegra_rt5640_dai,
.num_links = ARRAY_SIZE(tegra_rt5640_dai),
+ .resume_pre = tegra_rt5640_resume_pre,
+ .set_bias_level = tegra_rt5640_set_bias_level,
+ .set_bias_level_post = tegra_rt5640_set_bias_level_post,
};
static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev)
@@ -614,7 +667,7 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
@@ -643,7 +696,7 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
-
+ card->dapm.idle_bias_off = 1;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
diff --git a/sound/soc/tegra/tegra_tdm_pcm.c b/sound/soc/tegra/tegra_tdm_pcm.c
new file mode 100644
index 000000000000..ae33bdbc588c
--- /dev/null
+++ b/sound/soc/tegra/tegra_tdm_pcm.c
@@ -0,0 +1,160 @@
+/*
+ * tegra_tdm_pcm.c - Tegra TDM PCM driver
+ *
+ * Author: Nitin Pai <npai@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ * Stephen Warren <swarren@nvidia.com>
+ * Vijay Mali <vmali@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_pcm.h"
+
+#define DRV_NAME "tegra-tdm-pcm-audio"
+
+static const struct snd_pcm_hardware tegra_tdm_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 8,
+ .channels_max = 16,
+ .period_bytes_min = 16 * 1024,
+ .period_bytes_max = 16 * 1024,
+ .periods_min = 4,
+ .periods_max = 4,
+ .buffer_bytes_max = 16 * 4 * 1024,
+ .fifo_size = 4,
+};
+
+static int tegra_tdm_pcm_open(struct snd_pcm_substream *substream)
+{
+ return tegra_pcm_allocate(substream,
+ TEGRA_DMA_MODE_CONTINUOUS_DOUBLE,
+ &tegra_tdm_pcm_hardware);
+
+}
+
+static int tegra_tdm_pcm_close(struct snd_pcm_substream *substream)
+{
+ return tegra_pcm_close(substream);
+}
+
+static int tegra_tdm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return tegra_pcm_hw_params(substream, params);
+}
+
+static int tegra_tdm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return tegra_pcm_hw_free(substream);
+}
+
+static int tegra_tdm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return tegra_pcm_trigger(substream, cmd);
+}
+
+static int tegra_tdm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return tegra_pcm_mmap(substream, vma);
+}
+
+static struct snd_pcm_ops tegra_tdm_pcm_ops = {
+ .open = tegra_tdm_pcm_open,
+ .close = tegra_tdm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = tegra_tdm_pcm_hw_params,
+ .hw_free = tegra_tdm_pcm_hw_free,
+ .trigger = tegra_tdm_pcm_trigger,
+ .pointer = tegra_pcm_pointer,
+ .mmap = tegra_tdm_pcm_mmap,
+};
+
+static int tegra_tdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return tegra_pcm_dma_allocate(rtd ,
+ tegra_tdm_pcm_hardware.buffer_bytes_max);
+}
+
+static void tegra_tdm_pcm_free(struct snd_pcm *pcm)
+{
+ return tegra_pcm_free(pcm);
+}
+
+struct snd_soc_platform_driver tegra_tdm_pcm_platform = {
+ .ops = &tegra_tdm_pcm_ops,
+ .pcm_new = tegra_tdm_pcm_new,
+ .pcm_free = tegra_tdm_pcm_free,
+};
+
+static int __devinit tegra_tdm_pcm_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &tegra_tdm_pcm_platform);
+}
+
+static int __devexit tegra_tdm_pcm_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver tegra_tdm_pcm_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_tdm_pcm_platform_probe,
+ .remove = __devexit_p(tegra_tdm_pcm_platform_remove),
+};
+
+static int __init snd_tegra_tdm_pcm_init(void)
+{
+ return platform_driver_register(&tegra_tdm_pcm_driver);
+}
+module_init(snd_tegra_tdm_pcm_init);
+
+static void __exit snd_tegra_tdm_pcm_exit(void)
+{
+ platform_driver_unregister(&tegra_tdm_pcm_driver);
+}
+module_exit(snd_tegra_tdm_pcm_exit);
+
+MODULE_AUTHOR("Nitin Pai <npai@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PCM ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index 2e532fcd1e46..795356875ba4 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -872,7 +872,7 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 55e4f61edfd2..ce608b007bef 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -805,7 +805,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
machine->pdata = pdata;
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card);
if (ret)
goto err_free_machine;
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 8fc07e9adf2e..660479b89478 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -163,7 +163,7 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
return -ENOMEM;
}
- ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
+ ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev, card);
if (ret)
goto err_free_trimslice;