summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorWang Huan <wanghuan@zch06.freescale.net>2009-06-30 15:48:19 +0800
committerJustin Waters <justin.waters@timesys.com>2009-10-13 11:04:22 -0400
commit33c76f72c99afe0f6190837f15a17106b36f7ecd (patch)
tree9a3ddd5aabe1f394e2a03229a911bf641d316783 /sound
parent17984758820b3e6cc4ae22b3e695093eef8237a0 (diff)
ENGR00113364-1 ASOC:Add AK5702 support for ESAI record
Add AK5702 support for ESAI record under kernel 2.6.28. 1. Add ASOC codec driver for AK5702. 2. Add ASOC machine driver for AK5702 and ESAI. 3. Add ESAI record support. Signed-off-by: Wang Huan <b18965@freescale.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig5
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ak5702.c592
-rw-r--r--sound/soc/codecs/ak5702.h130
-rw-r--r--sound/soc/imx/Kconfig8
-rw-r--r--sound/soc/imx/Makefile2
-rw-r--r--sound/soc/imx/imx-3stack-ak5702.c229
-rw-r--r--sound/soc/imx/imx-3stack-wm8580.c2
-rw-r--r--sound/soc/imx/imx-esai.c41
9 files changed, 992 insertions, 19 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d692dbe92df3..adf30811bb78 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -5,6 +5,7 @@ config SND_SOC_ALL_CODECS
select SPI_MASTER
select SND_SOC_AD73311
select SND_SOC_AK4535
+ select SND_SOC_AK5702
select SND_SOC_CS4270
select SND_SOC_SSM2602
select SND_SOC_TLV320AIC23
@@ -43,6 +44,10 @@ config SND_SOC_AD73311
config SND_SOC_AK4535
tristate
+config SND_SOC_AK5702
+ tristate
+ depends on I2C
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 092bc879066a..b6eb70ba7533 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -2,6 +2,7 @@ snd-soc-ac97-objs := ac97.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak5702-objs := ak5702.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK5702) += snd-soc-ak5702.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
diff --git a/sound/soc/codecs/ak5702.c b/sound/soc/codecs/ak5702.c
new file mode 100644
index 000000000000..a4a046faf5cc
--- /dev/null
+++ b/sound/soc/codecs/ak5702.c
@@ -0,0 +1,592 @@
+/*
+ * ak5702.c -- AK5702 Soc Audio driver
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak5702.h"
+
+#define AK5702_VERSION "0.1"
+
+/* codec private data */
+struct ak5702_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * ak5702 register cache
+ */
+static const u16 ak5702_reg[AK5702_CACHEREGNUM] = {
+ 0x0000, 0x0024, 0x0000, 0x0001, 0x0023, 0x001f,
+ 0x0000, 0x0001, 0x0091, 0x0000, 0x00e1, 0x0000,
+ 0x00a0, 0x0000, 0x0000, 0x0000, 0x0001, 0x0020,
+ 0x0000, 0x0000, 0x0001, 0x0091, 0x0000, 0x00e1,
+ 0x0000,
+};
+
+/*
+ * read ak5702 register cache
+ */
+static inline unsigned int ak5702_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= AK5702_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+static inline unsigned int ak5702_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 data;
+ data = reg;
+
+ if (codec->hw_write(codec->control_data, &data, 1) != 1)
+ return -EIO;
+ if (codec->hw_read(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ return data;
+};
+
+/*
+ * write ak5702 register cache
+ */
+static inline void ak5702_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= AK5702_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the AK5702 register space
+ */
+static int ak5702_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ ak5702_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static const char *ak5702_mic_gain[] = { "0dB", "+15dB", "+30dB", "+36dB" };
+static const char *ak5702_adca_left_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adca_right_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adcb_left_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adcb_right_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adca_left_input[] = { "LIN1", "LIN2" };
+static const char *ak5702_adca_right_input[] = { "RIN1", "RIN2" };
+static const char *ak5702_adcb_left_input[] = { "LIN3", "LIN4" };
+static const char *ak5702_adcb_right_input[] = { "RIN3", "RIN4" };
+
+static const struct soc_enum ak5702_enum[] = {
+ SOC_ENUM_SINGLE(AK5702_MICG1, 0, 4, ak5702_mic_gain),
+ SOC_ENUM_SINGLE(AK5702_MICG2, 0, 4, ak5702_mic_gain),
+
+ SOC_ENUM_SINGLE(AK5702_SIG1, 0, 2, ak5702_adca_left_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 1, 2, ak5702_adca_right_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 0, 2, ak5702_adcb_left_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 1, 2, ak5702_adcb_right_input),
+
+ SOC_ENUM_SINGLE(AK5702_SIG1, 2, 2, ak5702_adca_left_type),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 3, 2, ak5702_adca_right_type),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 2, 2, ak5702_adcb_left_type),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 3, 2, ak5702_adcb_right_type),
+};
+
+static const struct snd_kcontrol_new ak5702_snd_controls[] = {
+ SOC_SINGLE("ADCA Left Vol", AK5702_LVOL1, 0, 242, 0),
+ SOC_SINGLE("ADCA Right Vol", AK5702_RVOL1, 0, 242, 0),
+ SOC_SINGLE("ADCB Left Vol", AK5702_LVOL2, 0, 242, 0),
+ SOC_SINGLE("ADCB Right Vol", AK5702_RVOL2, 0, 242, 0),
+
+ SOC_ENUM("MIC-AmpA Gain", ak5702_enum[0]),
+ SOC_ENUM("MIC-AmpB Gain", ak5702_enum[1]),
+
+ SOC_ENUM("ADCA Left Source", ak5702_enum[2]),
+ SOC_ENUM("ADCA Right Source", ak5702_enum[3]),
+ SOC_ENUM("ADCB Left Source", ak5702_enum[4]),
+ SOC_ENUM("ADCB Right Source", ak5702_enum[5]),
+
+ SOC_ENUM("ADCA Left Type", ak5702_enum[6]),
+ SOC_ENUM("ADCA Right Type", ak5702_enum[7]),
+ SOC_ENUM("ADCB Left Type", ak5702_enum[8]),
+ SOC_ENUM("ADCB Right Type", ak5702_enum[9]),
+};
+
+/* add non dapm controls */
+static int ak5702_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ak5702_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak5702_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* ak5702 dapm widgets */
+static const struct snd_soc_dapm_widget ak5702_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADCA Left", "Capture", AK5702_PM1, 0, 0),
+ SND_SOC_DAPM_ADC("ADCA Right", "Capture", AK5702_PM1, 1, 0),
+ SND_SOC_DAPM_ADC("ADCB Left", "Capture", AK5702_PM2, 0, 0),
+ SND_SOC_DAPM_ADC("ADCB Right", "Capture", AK5702_PM2, 1, 0),
+
+ SND_SOC_DAPM_INPUT("ADCA Left Input"),
+ SND_SOC_DAPM_INPUT("ADCA Right Input"),
+ SND_SOC_DAPM_INPUT("ADCB Left Input"),
+ SND_SOC_DAPM_INPUT("ADCB Right Input"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"ADCA Left", NULL, "ADCA Left Input"},
+ {"ADCA Right", NULL, "ADCA Right Input"},
+ {"ADCB Left", NULL, "ADCB Left Input"},
+ {"ADCB Right", NULL, "ADCB Right Input"},
+};
+
+static int ak5702_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak5702_dapm_widgets,
+ ARRAY_SIZE(ak5702_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int ak5702_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 fs = 0;
+ u8 value;
+
+ switch (freq) {
+ case 8000:
+ fs = 0x0;
+ break;
+ case 11025:
+ fs = 0x05;
+ break;
+ case 12000:
+ fs = 0x01;
+ break;
+ case 16000:
+ fs = 0x02;
+ break;
+ case 22050:
+ fs = 0x07;
+ break;
+ case 24000:
+ fs = 0x03;
+ break;
+ case 32000:
+ fs = 0x0a;
+ break;
+ case 44100:
+ fs = 0x0f;
+ break;
+ case 48000:
+ fs = 0x0b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ value = ak5702_read_reg_cache(codec, AK5702_FS1);
+ value &= (~AK5702_FS1_FS_MASK);
+ value |= fs;
+ ak5702_write(codec, AK5702_FS1, value);
+ return 0;
+}
+
+static int ak5702_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 fmt1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt1 = AK5702_FMT1_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt1 = AK5702_FMT1_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ak5702_write(codec, AK5702_FMT1, fmt1);
+ ak5702_write(codec, AK5702_FMT2, AK5702_FMT2_STEREO);
+ return 0;
+}
+
+static int ak5702_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ switch (pll_id) {
+ case AK5702_PLL_POWERDOWN:
+ reg &= (~AK5702_PLL1_PM_MASK);
+ reg |= AK5702_PLL1_POWERDOWN;
+ break;
+ case AK5702_PLL_MASTER:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_MASTER;
+ reg |= AK5702_PLL1_POWERUP;
+ break;
+ case AK5702_PLL_SLAVE:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_SLAVE;
+ reg |= AK5702_PLL1_POWERUP;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ switch (freq_in) {
+ case 11289600:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_11289600;
+ break;
+ case 12000000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_12000000;
+ break;
+ case 12288000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_12288000;
+ break;
+ case 19200000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_19200000;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ak5702_write(codec, AK5702_PLL1, reg);
+ return 0;
+}
+
+static int ak5702_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+
+ if (div_id == AK5702_BCLK_CLKDIV) {
+ reg = ak5702_read_reg_cache(codec, AK5702_FS1);
+ switch (div) {
+ case AK5702_BCLK_DIV_32:
+ reg &= (~AK5702_FS1_BCKO_MASK);
+ reg |= AK5702_FS1_BCKO_32FS;
+ ak5702_write(codec, AK5702_FS1, reg);
+ break;
+ case AK5702_BCLK_DIV_64:
+ reg &= (~AK5702_FS1_BCKO_MASK);
+ reg |= AK5702_FS1_BCKO_64FS;
+ ak5702_write(codec, AK5702_FS1, reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int ak5702_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 reg = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg | AK5702_PM1_PMVCM);
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ reg = reg | AK5702_PLL1_POWERUP | AK5702_PLL1_MASTER;
+ ak5702_write(codec, AK5702_PLL1, reg);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg | AK5702_PM1_PMVCM);
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ ak5702_write(codec, AK5702_PLL1, reg & (~AK5702_PLL1_POWERUP));
+ break;
+ case SND_SOC_BIAS_OFF:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg & (~AK5702_PM1_PMVCM));
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+static int ak5702_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak5702_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ ak5702_set_bias_level(codec, codec->suspend_bias_level);
+
+ /* Sync back everything else */
+ for (i = 0; i < ARRAY_SIZE(ak5702_reg); i++)
+ ak5702_write(codec, i, ak5702_reg[i]);
+
+ return 0;
+}
+
+#define AK5702_RATES SNDRV_PCM_RATE_8000_48000
+#define AK5702_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai ak5702_dai = {
+ .name = "AK5702",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = AK5702_RATES,
+ .formats = AK5702_FORMATS,
+ },
+ .dai_ops = {
+ .set_fmt = ak5702_set_dai_fmt,
+ .set_sysclk = ak5702_set_dai_sysclk,
+ .set_clkdiv = ak5702_set_dai_clkdiv,
+ .set_pll = ak5702_set_dai_pll,
+ },
+};
+EXPORT_SYMBOL_GPL(ak5702_dai);
+
+static int ak5702_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ u8 reg = 0;
+
+ codec->name = "AK5702";
+ codec->owner = THIS_MODULE;
+ codec->read = ak5702_read_reg_cache;
+ codec->write = ak5702_write;
+ codec->set_bias_level = ak5702_set_bias_level;
+ codec->dai = &ak5702_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(ak5702_reg);
+ codec->reg_cache = (void *)&ak5702_reg;
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ak5702: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ reg |= AK5702_PM1_PMVCM;
+ ak5702_write(codec, AK5702_PM1, reg);
+
+ /* initialize ADC */
+ reg = AK5702_SIG1_L_LIN1 | AK5702_SIG1_R_RIN2;
+ ak5702_write(codec, AK5702_SIG1, reg);
+ reg = AK5702_SIG2_L_LIN3 | AK5702_SIG2_R_RIN4;
+ ak5702_write(codec, AK5702_SIG2, reg);
+
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ reg = reg | AK5702_PM1_PMADAL | AK5702_PM1_PMADAR;
+ ak5702_write(codec, AK5702_PM1, reg);
+ reg = ak5702_read_reg_cache(codec, AK5702_PM2);
+ reg = reg | AK5702_PM2_PMADBL | AK5702_PM2_PMADBR;
+ ak5702_write(codec, AK5702_PM2, reg);
+
+ /* initialize volume */
+ ak5702_write(codec, AK5702_MICG1, AK5702_MICG1_INIT);
+ ak5702_write(codec, AK5702_MICG2, AK5702_MICG2_INIT);
+ ak5702_write(codec, AK5702_VOL1, AK5702_VOL1_IVOLAC);
+ ak5702_write(codec, AK5702_VOL2, AK5702_VOL2_IVOLBC);
+ ak5702_write(codec, AK5702_LVOL1, AK5702_LVOL1_INIT);
+ ak5702_write(codec, AK5702_RVOL1, AK5702_RVOL1_INIT);
+ ak5702_write(codec, AK5702_LVOL2, AK5702_LVOL2_INIT);
+ ak5702_write(codec, AK5702_RVOL2, AK5702_RVOL2_INIT);
+
+ ak5702_add_controls(codec);
+ ak5702_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ak5702: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *ak5702_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ak5702_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = ak5702_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = ak5702_init(socdev);
+ if (ret < 0)
+ printk(KERN_ERR "failed to initialise AK5702\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id ak5702_i2c_id[] = {
+ {"ak5702-i2c", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak5702_i2c_id);
+
+static struct i2c_driver ak5702_i2c_driver = {
+ .driver = {
+ .name = "ak5702-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak5702_i2c_probe,
+ .id_table = ak5702_i2c_id,
+};
+#endif
+
+static int ak5702_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct ak5702_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct ak5702_priv *ak5702;
+ int ret;
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ ak5702 = kzalloc(sizeof(struct ak5702_priv), GFP_KERNEL);
+ if (ak5702 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = ak5702;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ ak5702_socdev = socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->hw_read = (hw_read_t) i2c_master_recv;
+ ret = i2c_add_driver(&ak5702_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+#endif
+ return ret;
+}
+
+static int ak5702_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&ak5702_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak5702 = {
+ .probe = ak5702_probe,
+ .remove = ak5702_remove,
+ .suspend = ak5702_suspend,
+ .resume = ak5702_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak5702);
+
+MODULE_DESCRIPTION("Soc AK5702 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak5702.h b/sound/soc/codecs/ak5702.h
new file mode 100644
index 000000000000..97af6e45e498
--- /dev/null
+++ b/sound/soc/codecs/ak5702.h
@@ -0,0 +1,130 @@
+/*
+ * ak5702.h -- AK5702 Soc Audio driver
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef _AK5702_H
+#define _AK5702_H
+
+/* AK5702 register space */
+
+#define AK5702_PM1 0x00
+#define AK5702_PLL1 0x01
+#define AK5702_SIG1 0x02
+#define AK5702_MICG1 0x03
+#define AK5702_FMT1 0x04
+#define AK5702_FS1 0x05
+#define AK5702_CLK1 0x06
+#define AK5702_VOL1 0x07
+#define AK5702_LVOL1 0x08
+#define AK5702_RVOL1 0x09
+#define AK5702_TIMER1 0x0a
+#define AK5702_ALC11 0x0b
+#define AK5702_ALC12 0x0c
+#define AK5702_MODE11 0x0d
+#define AK5702_MODE12 0x0e
+#define AK5702_MODE13 0x0f
+
+#define AK5702_PM2 0x10
+#define AK5702_PLL2 0x11
+#define AK5702_SIG2 0x12
+#define AK5702_MICG2 0x13
+#define AK5702_FMT2 0x14
+#define AK5702_FS2 0x15
+#define AK5702_CLK2 0x16
+#define AK5702_VOL2 0x17
+#define AK5702_LVOL2 0x18
+#define AK5702_RVOL2 0x19
+#define AK5702_TIMER2 0x1a
+#define AK5702_ALC21 0x1b
+#define AK5702_ALC22 0x1c
+#define AK5702_MODE21 0x1d
+#define AK5702_MODE22 0x1e
+
+#define AK5702_CACHEREGNUM 0x1F
+
+#define AK5702_PM1_PMADAL 0x01
+#define AK5702_PM1_PMADAR 0x02
+#define AK5702_PM1_PMVCM 0x04
+#define AK5702_PM2_PMADBL 0x01
+#define AK5702_PM2_PMADBR 0x02
+
+#define AK5702_PLL1_POWERDOWN 0x0
+#define AK5702_PLL1_POWERUP 0x01
+#define AK5702_PLL1_MASTER 0x02
+#define AK5702_PLL1_SLAVE 0x0
+#define AK5702_PLL1_11289600 0x10
+#define AK5702_PLL1_12000000 0x24
+#define AK5702_PLL1_12288000 0x14
+#define AK5702_PLL1_19200000 0x20
+
+#define AK5702_SIG1_L_LIN1 0x0
+#define AK5702_SIG1_L_LIN2 0x01
+#define AK5702_SIG1_R_RIN1 0x0
+#define AK5702_SIG1_R_RIN2 0x02
+#define AK5702_SIG1_PMMPA 0x10
+#define AK5702_SIG2_L_LIN3 0x0
+#define AK5702_SIG2_L_LIN4 0x01
+#define AK5702_SIG2_R_RIN3 0x0
+#define AK5702_SIG2_R_RIN4 0x02
+#define AK5702_SIG2_PMMPB 0x10
+
+#define AK5702_MICG1_INIT 0x0
+#define AK5702_MICG2_INIT 0x0
+
+#define AK5702_FMT1_I2S 0x23
+#define AK5702_FMT1_MSB 0x22
+#define AK5702_FMT2_STEREO 0x20
+#define AK5702_FS1_BCKO_32FS 0x10
+#define AK5702_FS1_BCKO_64FS 0x20
+#define AK5702_CLK1_PS_256FS 0x0
+#define AK5702_CLK1_PS_128FS 0x01
+#define AK5702_CLK1_PS_64FS 0x02
+#define AK5702_CLK1_PS_32FS 0x03
+#define AK5702_VOL1_IVOLAC 0x01
+#define AK5702_VOL2_IVOLBC 0x01
+#define AK5702_LVOL1_INIT 0x91
+#define AK5702_RVOL1_INIT 0x91
+#define AK5702_LVOL2_INIT 0x91
+#define AK5702_RVOL2_INIT 0x91
+
+#define AK5702_PLL1_PM_MASK 0x01
+#define AK5702_PLL1_MODE_MASK 0x02
+#define AK5702_PLL1_PLL_MASK 0x3c
+#define AK5702_FS1_BCKO_MASK 0x30
+#define AK5702_FS1_FS_MASK 0x0f
+#define AK5702_CLK1_PS_MASK 0x03
+
+/* clock divider id */
+#define AK5702_BCLK_CLKDIV 0
+#define AK5702_MCLK_CLKDIV 1
+
+/* bit clock div values */
+#define AK5702_BCLK_DIV_32 0
+#define AK5702_BCLK_DIV_64 1
+
+/* m clock div values */
+#define AK5702_MCLK_DIV_32 0
+#define AK5702_MCLK_DIV_64 1
+#define AK5702_MCLK_DIV_128 2
+#define AK5702_MCLK_DIV_256 3
+
+/* PLL master and slave modes */
+#define AK5702_PLL_POWERDOWN 0
+#define AK5702_PLL_MASTER 1
+#define AK5702_PLL_SLAVE 2
+
+struct ak5702_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak5702_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak5702;
+
+#endif
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index d2726e395e1d..c81f943a2e10 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -53,6 +53,14 @@ config SND_SOC_IMX_3STACK_WM8580
Say Y if you want to add support for Soc audio on IMX 3STACK
with the WM8580
+config SND_SOC_IMX_3STACK_AK5702
+ tristate "SoC Audio support for IMX - AK5702"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_AK5702
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the AK5702
+
config SND_SOC_IMX_3STACK_BLUETOOTH
tristate "SoC Audio support for IMX - BLUETOOTH"
select SND_MXC_SOC_SSI
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index a09b24618107..0460f965a85b 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -16,6 +16,8 @@ snd-soc-imx-3stack-ak4647-objs := imx-3stack-ak4647.o
obj-$(CONFIG_SND_SOC_IMX_3STACK_AK4647) += snd-soc-imx-3stack-ak4647.o
snd-soc-imx-3stack-wm8580-objs := imx-3stack-wm8580.o
obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8580) += snd-soc-imx-3stack-wm8580.o
+snd-soc-imx-3stack-ak5702-objs := imx-3stack-ak5702.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_AK5702) += snd-soc-imx-3stack-ak5702.o
snd-soc-imx-3stack-bt-objs := imx-3stack-bt.o
obj-$(CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH) += snd-soc-imx-3stack-bt.o
diff --git a/sound/soc/imx/imx-3stack-ak5702.c b/sound/soc/imx/imx-3stack-ak5702.c
new file mode 100644
index 000000000000..9d7b3575109b
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-ak5702.c
@@ -0,0 +1,229 @@
+/*
+ * imx-3stack-ak5702.c -- SoC audio for imx_3stack
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/ak5702.h"
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+static struct imx_3stack_pcm_state clk_state;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+
+ /* disable the PLL if there are no active Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ u32 dai_format;
+
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_ASYNC;
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffff, 2);
+
+ /* set the ESAI system clock as input */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ /* set codec BCLK division */
+ snd_soc_dai_set_clkdiv(codec_dai, AK5702_BCLK_CLKDIV,
+ AK5702_BCLK_DIV_32);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_pll(codec_dai, 1, 12000000, 0);
+ return 0;
+}
+
+/*
+ * imx_3stack ak5702 DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+/* imx_3stack machine dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+/* example machine audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line in jack */
+ {"ADCA Left Input", NULL, "Line In Jack"},
+ {"ADCA Right Input", NULL, "Line In Jack"},
+ {"ADCB Left Input", NULL, "Line In Jack"},
+ {"ADCB Right Input", NULL, "Line In Jack"},
+};
+
+static int imx_3stack_ak5702_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "ak5702",
+ .stream_name = "ak5702",
+ .cpu_dai = &imx_esai_dai,
+ .codec_dai = &ak5702_dai,
+ .init = imx_3stack_ak5702_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_machine_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_ak5702,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ */
+static int __devinit imx_3stack_ak5702_probe(struct platform_device *pdev)
+{
+ struct ak5702_setup_data *setup;
+
+ imx_esai_dai.name = "imx-esai-txrx";
+
+ setup = kzalloc(sizeof(struct ak5702_setup_data), GFP_KERNEL);
+ setup->i2c_bus = 1;
+ setup->i2c_address = 0x13;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ gpio_activate_esai_ports();
+ return 0;
+}
+
+static int imx_3stack_ak5702_remove(struct platform_device *pdev)
+{
+ gpio_deactivate_esai_ports();
+ return 0;
+}
+
+static struct platform_driver imx_3stack_ak5702_driver = {
+ .probe = imx_3stack_ak5702_probe,
+ .remove = imx_3stack_ak5702_remove,
+ .driver = {
+ .name = "imx-3stack-ak5702",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_ak5702_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_ak5702_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ALSA SoC ak5702 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-wm8580.c b/sound/soc/imx/imx-3stack-wm8580.c
index 29f61a884fc8..e12478e59d3f 100644
--- a/sound/soc/imx/imx-3stack-wm8580.c
+++ b/sound/soc/imx/imx-3stack-wm8580.c
@@ -381,7 +381,7 @@ static int __devinit imx_3stack_wm8580_probe(struct platform_device *pdev)
{
struct wm8580_setup_data *setup;
- imx_esai_dai.name = "imx-esai-tx";
+ imx_esai_dai.name = "imx-esai-txrx";
setup = kzalloc(sizeof(struct wm8580_setup_data), GFP_KERNEL);
setup->spi = 1;
diff --git a/sound/soc/imx/imx-esai.c b/sound/soc/imx/imx-esai.c
index 9b4d1ace2421..2f85e6ed4c11 100644
--- a/sound/soc/imx/imx-esai.c
+++ b/sound/soc/imx/imx-esai.c
@@ -137,7 +137,7 @@
#define ESAI_RFCR_RE0 (1 << 2)
#define ESAI_RFCR_RFR (1 << 1)
#define ESAI_RFCR_RFEN (1 << 0)
-#define ESAI_RFCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 2)
+#define ESAI_RFCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 3)
#define ESAI_RFCR_RE_MASK 0xfffc3
#define ESAI_RFCR_RFWM(x) ((x-1) << 8)
#define ESAI_RFCR_RWA_MASK 0xf8ffff
@@ -254,9 +254,9 @@
#define ESAI_RCR_RE2 (1 << 2)
#define ESAI_RCR_RE1 (1 << 1)
#define ESAI_RCR_RE0 (1 << 0)
-#define ESAI_RCR_RE(x) (0xf >> (4 - ((x + 1) >> 1)))
+#define ESAI_RCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 1)
-#define ESAI_TCR_RSWS_MASK 0xff83ff
+#define ESAI_RCR_RSWS_MASK 0xff83ff
#define ESAI_RCR_RSWS_STL8_WDL8 (0x00 << 10)
#define ESAI_RCR_RSWS_STL12_WDL8 (0x04 << 10)
#define ESAI_RCR_RSWS_STL12_WDL12 (0x01 << 10)
@@ -544,11 +544,15 @@ static int imx_esai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
saicr |= ESAI_SAICR_SYNC;
tcr &= ESAI_TCR_TMOD_MASK;
+ rcr &= ESAI_RCR_RMOD_MASK;
/* tdm - only for stereo atm */
- if (fmt & SND_SOC_DAIFMT_TDM)
+ if (fmt & SND_SOC_DAIFMT_TDM) {
tcr |= ESAI_TCR_TMOD_NETWORK;
- else
+ rcr |= ESAI_RCR_RMOD_NETWORK;
+ } else {
tcr |= ESAI_TCR_TMOD_NORMAL;
+ rcr |= ESAI_RCR_RMOD_NORMAL;
+ }
if (cpu_dai->id & IMX_DAI_ESAI_TX) {
__raw_writel(tcr, ESAI_TCR);
@@ -684,26 +688,17 @@ static int imx_esai_hw_rx_params(struct snd_pcm_substream
rfcr &= ~ESAI_RFCR_RFR;
rfcr &= ESAI_RFCR_RWA_MASK;
- rcr &= ESAI_TCR_RSWS_MASK;
+ rcr &= ESAI_RCR_RSWS_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
rfcr |= ESAI_WORD_LEN_16;
- rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL16;
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- rfcr |= ESAI_WORD_LEN_20;
- rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL20;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- rfcr |= ESAI_WORD_LEN_24;
- rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL24;
+ rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL16_WDL16;
break;
}
channels = params_channels(params);
rfcr &= ESAI_RFCR_RE_MASK;
rfcr |= ESAI_RFCR_RE(channels);
- rfcr |= ESAI_RFCR_RFEN;
rfcr |= ESAI_RFCR_RFWM(64);
@@ -724,7 +719,7 @@ static int imx_esai_hw_params(struct snd_pcm_substream
return 0;
return imx_esai_hw_tx_params(substream, params);
} else {
- if (__raw_readl(ESAI_RCR) & ESAI_RCR_RE0)
+ if (__raw_readl(ESAI_RCR) & ESAI_RCR_RE1)
return 0;
return imx_esai_hw_rx_params(substream, params);
}
@@ -732,12 +727,13 @@ static int imx_esai_hw_params(struct snd_pcm_substream
static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd)
{
- u32 reg, tfcr = 0;
+ u32 reg, tfcr = 0, rfcr = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
tfcr = __raw_readl(ESAI_TFCR);
reg = __raw_readl(ESAI_TCR);
} else {
+ rfcr = __raw_readl(ESAI_RFCR);
reg = __raw_readl(ESAI_RCR);
}
switch (cmd) {
@@ -751,6 +747,8 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd)
reg |= ESAI_TCR_TE(substream->runtime->channels);
__raw_writel(reg, ESAI_TCR);
} else {
+ rfcr |= ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
reg &= ~ESAI_RCR_RPR;
reg |= ESAI_RCR_RE(substream->runtime->channels);
__raw_writel(reg, ESAI_RCR);
@@ -772,6 +770,13 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd)
} else {
reg &= ~ESAI_RCR_RE(substream->runtime->channels);
__raw_writel(reg, ESAI_RCR);
+ reg |= ESAI_RCR_RPR;
+ __raw_writel(reg, ESAI_RCR);
+ rfcr |= ESAI_RFCR_RFR;
+ rfcr &= ~ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
+ rfcr &= ~ESAI_RFCR_RFR;
+ __raw_writel(rfcr, ESAI_RFCR);
}
break;
default: