Merge branch 'for-2.6.38' into for-2.6.39
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 74921f2..d609232 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -459,6 +459,7 @@
struct list_head card_list;
int num_dai;
enum snd_soc_compress_type compress_type;
+ size_t reg_size; /* reg_cache_size * reg_word_size */
/* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
@@ -756,4 +757,8 @@
#include <sound/soc-dai.h>
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *snd_soc_debugfs_root;
+#endif
+
#endif
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index a3efc52..8224db5 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -50,10 +50,12 @@
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/mid-x86/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
+source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
# Supported codecs
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index ce913bf..1ed61c5 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -10,6 +10,7 @@
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += jz4740/
+obj-$(CONFIG_SND_SOC) += mid-x86/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
@@ -17,4 +18,5 @@
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
+obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 883a312..61e36ef 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -32,6 +32,7 @@
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
+ select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
@@ -176,6 +177,9 @@
config SND_SOC_PCM3008
tristate
+config SND_SOC_SN95031
+ tristate
+
config SND_SOC_SPDIF
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 579af9c..333910a 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -19,6 +19,7 @@
snd-soc-max98088-objs := max98088.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-alc5623-objs := alc5623.o
+snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-stac9766-objs := stac9766.o
@@ -99,6 +100,7 @@
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
+obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
new file mode 100644
index 0000000..593632c
--- /dev/null
+++ b/sound/soc/codecs/sn95031.c
@@ -0,0 +1,495 @@
+/*
+ * sn95031.c - TI sn95031 Codec driver
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "sn95031.h"
+
+#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
+#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * todo:
+ * capture paths
+ * jack detection
+ * PM functions
+ */
+
+static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 value = 0;
+ int ret;
+
+ ret = intel_scu_ipc_ioread8(reg, &value);
+ if (ret)
+ pr_err("read of %x failed, err %d\n", reg, ret);
+ return value;
+
+}
+
+static inline int sn95031_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ int ret;
+
+ ret = intel_scu_ipc_iowrite8(reg, value);
+ if (ret)
+ pr_err("write of %x failed, err %d\n", reg, ret);
+ return ret;
+}
+
+static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ pr_debug("vaud_bias powering up pll\n");
+ /* power up the pll */
+ snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
+ /* enable pcm 2 */
+ snd_soc_update_bits(codec, SN95031_PCM2C2,
+ BIT(0), BIT(0));
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ pr_debug("vaud_bias power up rail\n");
+ /* power up the rail */
+ snd_soc_write(codec, SN95031_VAUD,
+ BIT(2)|BIT(1)|BIT(0));
+ msleep(1);
+ } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ /* turn off pcm */
+ pr_debug("vaud_bias power dn pcm\n");
+ snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
+ snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+ }
+ break;
+
+
+ case SND_SOC_BIAS_OFF:
+ pr_debug("vaud_bias _OFF doing rail shutdown\n");
+ snd_soc_write(codec, SN95031_VAUD, BIT(3));
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+ /* power up the rail */
+ snd_soc_write(w->codec, SN95031_VHSP, 0x3D);
+ snd_soc_write(w->codec, SN95031_VHSN, 0x3F);
+ msleep(1);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+ snd_soc_write(w->codec, SN95031_VHSP, 0xC4);
+ snd_soc_write(w->codec, SN95031_VHSN, 0x04);
+ }
+ return 0;
+}
+
+static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+ /* power up the rail */
+ snd_soc_write(w->codec, SN95031_VIHF, 0x27);
+ msleep(1);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+ snd_soc_write(w->codec, SN95031_VIHF, 0x24);
+ }
+ return 0;
+}
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
+
+ /* all end points mic, hs etc */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("EPOUT"),
+ SND_SOC_DAPM_OUTPUT("IHFOUTL"),
+ SND_SOC_DAPM_OUTPUT("IHFOUTR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+ SND_SOC_DAPM_OUTPUT("VIB1OUT"),
+ SND_SOC_DAPM_OUTPUT("VIB2OUT"),
+
+ SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
+ sn95031_vhs_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
+ sn95031_vihf_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* playback path driver enables */
+ SND_SOC_DAPM_PGA("Headset Left Playback",
+ SN95031_DRIVEREN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Right Playback",
+ SN95031_DRIVEREN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Left Playback",
+ SN95031_DRIVEREN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Right Playback",
+ SN95031_DRIVEREN, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Vibra1 Playback",
+ SN95031_DRIVEREN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Vibra2 Playback",
+ SN95031_DRIVEREN, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Earpiece Playback",
+ SN95031_DRIVEREN, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Left Playback",
+ SN95031_LOCTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Right Playback",
+ SN95031_LOCTL, 4, 0, NULL, 0),
+
+ /* playback path filter enable */
+ SND_SOC_DAPM_PGA("Headset Left Filter",
+ SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Right Filter",
+ SN95031_HSEPRXCTRL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Left Filter",
+ SN95031_IHFRXCTRL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Right Filter",
+ SN95031_IHFRXCTRL, 1, 0, NULL, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
+ SN95031_DACCONFIG, 0, 0),
+ SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
+ SN95031_DACCONFIG, 1, 0),
+ SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
+ SN95031_DACCONFIG, 2, 0),
+ SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
+ SN95031_DACCONFIG, 3, 0),
+ SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
+ SN95031_VIB1C5, 1, 0),
+ SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
+ SN95031_VIB2C5, 1, 0),
+};
+
+static const struct snd_soc_dapm_route sn95031_audio_map[] = {
+ /* headset and earpiece map */
+ { "HPOUTL", NULL, "Headset Left Playback" },
+ { "HPOUTR", NULL, "Headset Right Playback" },
+ { "EPOUT", NULL, "Earpiece Playback" },
+ { "Headset Left Playback", NULL, "Headset Left Filter"},
+ { "Headset Right Playback", NULL, "Headset Right Filter"},
+ { "Earpiece Playback", NULL, "Headset Left Filter"},
+ { "Headset Left Filter", NULL, "HSDAC Left"},
+ { "Headset Right Filter", NULL, "HSDAC Right"},
+ { "HSDAC Left", NULL, "Headset Rail"},
+ { "HSDAC Right", NULL, "Headset Rail"},
+
+ /* speaker map */
+ { "IHFOUTL", "NULL", "Speaker Left Playback"},
+ { "IHFOUTR", "NULL", "Speaker Right Playback"},
+ { "Speaker Left Playback", NULL, "Speaker Left Filter"},
+ { "Speaker Right Playback", NULL, "Speaker Right Filter"},
+ { "Speaker Left Filter", NULL, "IHFDAC Left"},
+ { "Speaker Right Filter", NULL, "IHFDAC Right"},
+ { "IHFDAC Left", NULL, "Speaker Rail"},
+ { "IHFDAC Right", NULL, "Speaker Rail"},
+
+ /* vibra map */
+ { "VIB1OUT", NULL, "Vibra1 Playback"},
+ { "Vibra1 Playback", NULL, "Vibra1 DAC"},
+
+ { "VIB2OUT", NULL, "Vibra2 Playback"},
+ { "Vibra2 Playback", NULL, "Vibra2 DAC"},
+
+ /* lineout */
+ { "LINEOUTL", NULL, "Lineout Left Playback"},
+ { "LINEOUTR", NULL, "Lineout Right Playback"},
+ { "Lineout Left Playback", NULL, "Headset Left Filter"},
+ { "Lineout Left Playback", NULL, "Speaker Left Filter"},
+ { "Lineout Left Playback", NULL, "Vibra1 DAC"},
+ { "Lineout Right Playback", NULL, "Headset Right Filter"},
+ { "Lineout Right Playback", NULL, "Speaker Right Filter"},
+ { "Lineout Right Playback", NULL, "Vibra2 DAC"},
+};
+
+/* speaker and headset mutes, for audio pops and clicks */
+static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec,
+ SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
+ snd_soc_update_bits(dai->codec,
+ SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
+ return 0;
+}
+
+static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec,
+ SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
+ snd_soc_update_bits(dai->codec,
+ SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
+ return 0;
+}
+
+int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ unsigned int format, rate;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ format = BIT(4)|BIT(5);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ format = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
+ BIT(4)|BIT(5), format);
+
+ switch (params_rate(params)) {
+ case 48000:
+ pr_debug("RATE_48000\n");
+ rate = 0;
+ break;
+
+ case 44100:
+ pr_debug("RATE_44100\n");
+ rate = BIT(7);
+ break;
+
+ default:
+ pr_err("ERR rate %d\n", params_rate(params));
+ return -EINVAL;
+ }
+ snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
+
+ return 0;
+}
+
+/* Codec DAI section */
+static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
+ .digital_mute = sn95031_pcm_hs_mute,
+ .hw_params = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
+ .digital_mute = sn95031_pcm_spkr_mute,
+ .hw_params = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
+ .hw_params = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
+ .hw_params = sn95031_pcm_hw_params,
+};
+
+struct snd_soc_dai_driver sn95031_dais[] = {
+{
+ .name = "SN95031 Headset",
+ .playback = {
+ .stream_name = "Headset",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SN95031_RATES,
+ .formats = SN95031_FORMATS,
+ },
+ .ops = &sn95031_headset_dai_ops,
+},
+{ .name = "SN95031 Speaker",
+ .playback = {
+ .stream_name = "Speaker",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SN95031_RATES,
+ .formats = SN95031_FORMATS,
+ },
+ .ops = &sn95031_speaker_dai_ops,
+},
+{ .name = "SN95031 Vibra1",
+ .playback = {
+ .stream_name = "Vibra1",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SN95031_RATES,
+ .formats = SN95031_FORMATS,
+ },
+ .ops = &sn95031_vib1_dai_ops,
+},
+{ .name = "SN95031 Vibra2",
+ .playback = {
+ .stream_name = "Vibra2",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SN95031_RATES,
+ .formats = SN95031_FORMATS,
+ },
+ .ops = &sn95031_vib2_dai_ops,
+},
+};
+
+/* codec registration */
+static int sn95031_codec_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ pr_debug("codec_probe called\n");
+
+ codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+ codec->dapm.idle_bias_off = 1;
+
+ /* PCM interface config
+ * This sets the pcm rx slot conguration to max 6 slots
+ * for max 4 dais (2 stereo and 2 mono)
+ */
+ snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
+ snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
+ snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
+ /* pcm port setting
+ * This sets the pcm port to slave and clock at 19.2Mhz which
+ * can support 6slots, sampling rate set per stream in hw-params
+ */
+ snd_soc_write(codec, SN95031_PCM1C1, 0x00);
+ snd_soc_write(codec, SN95031_PCM2C1, 0x01);
+ snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
+ snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
+ /* vendor vibra workround, the vibras are muted by
+ * custom register so unmute them
+ */
+ snd_soc_write(codec, SN95031_SSR5, 0x80);
+ snd_soc_write(codec, SN95031_SSR6, 0x80);
+ snd_soc_write(codec, SN95031_VIB1C5, 0x00);
+ snd_soc_write(codec, SN95031_VIB2C5, 0x00);
+ /* configure vibras for pcm port */
+ snd_soc_write(codec, SN95031_VIB1C3, 0x00);
+ snd_soc_write(codec, SN95031_VIB2C3, 0x00);
+
+ /* soft mute ramp time */
+ snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
+ /* fix the initial volume at 1dB,
+ * default in +9dB,
+ * 1dB give optimal swing on DAC, amps
+ */
+ snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
+ snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
+ snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
+ snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
+ /* dac mode and lineout workaround */
+ snd_soc_write(codec, SN95031_SSR2, 0x10);
+ snd_soc_write(codec, SN95031_SSR3, 0x40);
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
+ ARRAY_SIZE(sn95031_dapm_widgets));
+ if (ret)
+ pr_err("soc_dapm_new_control failed %d", ret);
+ ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
+ ARRAY_SIZE(sn95031_audio_map));
+ if (ret)
+ pr_err("soc_dapm_add_routes failed %d", ret);
+
+ return ret;
+}
+
+static int sn95031_codec_remove(struct snd_soc_codec *codec)
+{
+ pr_debug("codec_remove called\n");
+ sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+struct snd_soc_codec_driver sn95031_codec = {
+ .probe = sn95031_codec_probe,
+ .remove = sn95031_codec_remove,
+ .read = sn95031_read,
+ .write = sn95031_write,
+ .set_bias_level = sn95031_set_vaud_bias,
+};
+
+static int __devinit sn95031_device_probe(struct platform_device *pdev)
+{
+ pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
+ sn95031_dais, ARRAY_SIZE(sn95031_dais));
+}
+
+static int __devexit sn95031_device_remove(struct platform_device *pdev)
+{
+ pr_debug("codec device remove called\n");
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver sn95031_codec_driver = {
+ .driver = {
+ .name = "sn95031",
+ .owner = THIS_MODULE,
+ },
+ .probe = sn95031_device_probe,
+ .remove = sn95031_device_remove,
+};
+
+static int __init sn95031_init(void)
+{
+ pr_debug("driver init called\n");
+ return platform_driver_register(&sn95031_codec_driver);
+}
+module_init(sn95031_init);
+
+static void __exit sn95031_exit(void)
+{
+ pr_debug("driver exit called\n");
+ platform_driver_unregister(&sn95031_codec_driver);
+}
+module_exit(sn95031_exit);
+
+MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sn95031");
diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
new file mode 100644
index 0000000..e2b17d9
--- /dev/null
+++ b/sound/soc/codecs/sn95031.h
@@ -0,0 +1,99 @@
+/*
+ * sn95031.h - TI sn95031 Codec driver
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#ifndef _SN95031_H
+#define _SN95031_H
+
+/*register map*/
+#define SN95031_VAUD 0xDB
+#define SN95031_VHSP 0xDC
+#define SN95031_VHSN 0xDD
+#define SN95031_VIHF 0xC9
+
+#define SN95031_AUDPLLCTRL 0x240
+#define SN95031_DMICBUF0123 0x241
+#define SN95031_DMICBUF45 0x242
+#define SN95031_DMICGPO 0x244
+#define SN95031_DMICMUX 0x245
+#define SN95031_DMICLK 0x246
+#define SN95031_MICBIAS 0x247
+#define SN95031_ADCCONFIG 0x248
+#define SN95031_MICAMP1 0x249
+#define SN95031_MICAMP2 0x24A
+#define SN95031_NOISEMUX 0x24B
+#define SN95031_AUDIOMUX12 0x24C
+#define SN95031_AUDIOMUX34 0x24D
+#define SN95031_AUDIOSINC 0x24E
+#define SN95031_AUDIOTXEN 0x24F
+#define SN95031_HSEPRXCTRL 0x250
+#define SN95031_IHFRXCTRL 0x251
+#define SN95031_HSMIXER 0x256
+#define SN95031_DACCONFIG 0x257
+#define SN95031_SOFTMUTE 0x258
+#define SN95031_HSLVOLCTRL 0x259
+#define SN95031_HSRVOLCTRL 0x25A
+#define SN95031_IHFLVOLCTRL 0x25B
+#define SN95031_IHFRVOLCTRL 0x25C
+#define SN95031_DRIVEREN 0x25D
+#define SN95031_LOCTL 0x25E
+#define SN95031_VIB1C1 0x25F
+#define SN95031_VIB1C2 0x260
+#define SN95031_VIB1C3 0x261
+#define SN95031_VIB1SPIPCM1 0x262
+#define SN95031_VIB1SPIPCM2 0x263
+#define SN95031_VIB1C5 0x264
+#define SN95031_VIB2C1 0x265
+#define SN95031_VIB2C2 0x266
+#define SN95031_VIB2C3 0x267
+#define SN95031_VIB2SPIPCM1 0x268
+#define SN95031_VIB2SPIPCM2 0x269
+#define SN95031_VIB2C5 0x26A
+#define SN95031_BTNCTRL1 0x26B
+#define SN95031_BTNCTRL2 0x26C
+#define SN95031_PCM1TXSLOT01 0x26D
+#define SN95031_PCM1TXSLOT23 0x26E
+#define SN95031_PCM1TXSLOT45 0x26F
+#define SN95031_PCM1RXSLOT0_3 0x270
+#define SN95031_PCM1RXSLOT45 0x271
+#define SN95031_PCM2TXSLOT01 0x272
+#define SN95031_PCM2TXSLOT23 0x273
+#define SN95031_PCM2TXSLOT45 0x274
+#define SN95031_PCM2RXSLOT01 0x275
+#define SN95031_PCM2RXSLOT23 0x276
+#define SN95031_PCM2RXSLOT45 0x277
+#define SN95031_PCM1C1 0x278
+#define SN95031_PCM1C2 0x279
+#define SN95031_PCM1C3 0x27A
+#define SN95031_PCM2C1 0x27B
+#define SN95031_PCM2C2 0x27C
+/*end codec register defn*/
+
+/*vendor defn these are not part of avp*/
+#define SN95031_SSR2 0x381
+#define SN95031_SSR3 0x382
+#define SN95031_SSR5 0x384
+#define SN95031_SSR6 0x385
+
+#endif
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 6045cbd..ac210cc 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -1498,6 +1498,7 @@
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, 0);
+ codec->cache_sync = 1;
break;
}
diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig
new file mode 100644
index 0000000..e0123a4
--- /dev/null
+++ b/sound/soc/mid-x86/Kconfig
@@ -0,0 +1,13 @@
+config SND_MFLD_MACHINE
+ tristate "SOC Machine Audio driver for Intel Medfield MID platform"
+ select SND_SOC_SN95031
+ select SND_SST_PLATFORM
+ help
+ This adds support for ASoC machine driver for Intel(R) MID Medfield platform
+ used as alsa device in audio substem in Intel(R) MID devices
+ Say Y if you have such a device
+ If unsure select "N".
+
+config SND_SST_PLATFORM
+ tristate
+ depends on SND_INTEL_SST
diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile
new file mode 100644
index 0000000..6398833
--- /dev/null
+++ b/sound/soc/mid-x86/Makefile
@@ -0,0 +1,5 @@
+snd-soc-sst-platform-objs := sst_platform.o
+snd-soc-mfld-machine-objs := mfld_machine.o
+
+obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c
new file mode 100644
index 0000000..1a330be
--- /dev/null
+++ b/sound/soc/mid-x86/mfld_machine.c
@@ -0,0 +1,296 @@
+/*
+ * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/sn95031.h"
+
+#define MID_MONO 1
+#define MID_STEREO 2
+#define MID_MAX_CAP 5
+
+static unsigned int hs_switch;
+static unsigned int lo_dac;
+
+/* sound card controls */
+static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum headset_enum =
+ SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum lo_enum =
+ SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static int headset_get_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hs_switch;
+ return 0;
+}
+
+static int headset_set_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] == hs_switch)
+ return 0;
+
+ if (ucontrol->value.integer.value[0]) {
+ pr_debug("hs_set HS path\n");
+ snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL");
+ snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR");
+ snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+ } else {
+ pr_debug("hs_set EP path\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+ snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+ }
+ snd_soc_dapm_sync(&codec->dapm);
+ hs_switch = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static void lo_enable_out_pins(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
+ snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
+ snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
+ snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
+ snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
+ snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+ if (hs_switch) {
+ snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL");
+ snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR");
+ snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+ } else {
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+ snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+ }
+}
+
+static int lo_get_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = lo_dac;
+ return 0;
+}
+
+static int lo_set_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] == lo_dac)
+ return 0;
+
+ /* we dont want to work with last state of lineout so just enable all
+ * pins and then disable pins not required
+ */
+ lo_enable_out_pins(codec);
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ pr_debug("set vibra path\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
+ snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+ snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
+ break;
+
+ case 1:
+ pr_debug("set hs path\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+ snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+ snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+ snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
+ break;
+
+ case 2:
+ pr_debug("set spkr path\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
+ snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+ snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
+ break;
+
+ case 3:
+ pr_debug("set null path\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
+ snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+ snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
+ break;
+ }
+ snd_soc_dapm_sync(&codec->dapm);
+ lo_dac = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_kcontrol_new mfld_snd_controls[] = {
+ SOC_ENUM_EXT("Playback Switch", headset_enum,
+ headset_get_switch, headset_set_switch),
+ SOC_ENUM_EXT("Lineout Mux", lo_enum,
+ lo_get_switch, lo_set_switch),
+};
+
+static int mfld_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret_val;
+
+ ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
+ ARRAY_SIZE(mfld_snd_controls));
+ if (ret_val) {
+ pr_err("soc_add_controls failed %d", ret_val);
+ return ret_val;
+ }
+ /* default is earpiece pin, userspace sets it explcitly */
+ snd_soc_dapm_disable_pin(dapm, "HPOUTL");
+ snd_soc_dapm_disable_pin(dapm, "HPOUTR");
+ /* default is lineout NC, userspace sets it explcitly */
+ snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
+ lo_dac = 3;
+ hs_switch = 0;
+ return snd_soc_dapm_sync(dapm);
+}
+
+struct snd_soc_dai_link mfld_msic_dailink[] = {
+ {
+ .name = "Medfield Headset",
+ .stream_name = "Headset",
+ .cpu_dai_name = "Headset-cpu-dai",
+ .codec_dai_name = "SN95031 Headset",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = mfld_init,
+ },
+ {
+ .name = "Medfield Speaker",
+ .stream_name = "Speaker",
+ .cpu_dai_name = "Speaker-cpu-dai",
+ .codec_dai_name = "SN95031 Speaker",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+ {
+ .name = "Medfield Vibra",
+ .stream_name = "Vibra1",
+ .cpu_dai_name = "Vibra1-cpu-dai",
+ .codec_dai_name = "SN95031 Vibra1",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+ {
+ .name = "Medfield Haptics",
+ .stream_name = "Vibra2",
+ .cpu_dai_name = "Vibra2-cpu-dai",
+ .codec_dai_name = "SN95031 Vibra2",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_mfld = {
+ .name = "medfield_audio",
+ .dai_link = mfld_msic_dailink,
+ .num_links = ARRAY_SIZE(mfld_msic_dailink),
+};
+
+static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
+{
+ struct platform_device *socdev;
+ int ret_val = 0;
+
+ pr_debug("snd_mfld_mc_probe called\n");
+
+ socdev = platform_device_alloc("soc-audio", -1);
+ if (!socdev) {
+ pr_err("soc-audio device allocation failed\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(socdev, &snd_soc_card_mfld);
+ ret_val = platform_device_add(socdev);
+ if (ret_val) {
+ pr_err("Unable to add soc-audio device, err %d\n", ret_val);
+ platform_device_put(socdev);
+ }
+
+ platform_set_drvdata(pdev, socdev);
+
+ pr_debug("successfully exited probe\n");
+ return ret_val;
+}
+
+static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
+{
+ struct platform_device *socdev = platform_get_drvdata(pdev);
+ pr_debug("snd_mfld_mc_remove called\n");
+
+ platform_device_unregister(socdev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver snd_mfld_mc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "msic_audio",
+ },
+ .probe = snd_mfld_mc_probe,
+ .remove = __devexit_p(snd_mfld_mc_remove),
+};
+
+static int __init snd_mfld_driver_init(void)
+{
+ pr_debug("snd_mfld_driver_init called\n");
+ return platform_driver_register(&snd_mfld_mc_driver);
+}
+module_init(snd_mfld_driver_init);
+
+static void __exit snd_mfld_driver_exit(void)
+{
+ pr_debug("snd_mfld_driver_exit called\n");
+ platform_driver_unregister(&snd_mfld_mc_driver);
+}
+module_exit(snd_mfld_driver_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msic-audio");
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
new file mode 100644
index 0000000..a4e3fa3
--- /dev/null
+++ b/sound/soc/mid-x86/sst_platform.c
@@ -0,0 +1,466 @@
+/*
+ * sst_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
+#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include "sst_platform.h"
+
+static struct snd_pcm_hardware sst_platform_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP|
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
+ SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
+ SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
+ .rates = (SNDRV_PCM_RATE_8000|
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = SST_MIN_RATE,
+ .rate_max = SST_MAX_RATE,
+ .channels_min = SST_MIN_CHANNEL,
+ .channels_max = SST_MAX_CHANNEL,
+ .buffer_bytes_max = SST_MAX_BUFFER,
+ .period_bytes_min = SST_MIN_PERIOD_BYTES,
+ .period_bytes_max = SST_MAX_PERIOD_BYTES,
+ .periods_min = SST_MIN_PERIODS,
+ .periods_max = SST_MAX_PERIODS,
+ .fifo_size = SST_FIFO_SIZE,
+};
+
+/* MFLD - MSIC */
+struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+ .name = "Headset-cpu-dai",
+ .id = 0,
+ .playback = {
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Speaker-cpu-dai",
+ .id = 1,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Vibra1-cpu-dai",
+ .id = 2,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_MONO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Vibra2-cpu-dai",
+ .id = 3,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+};
+
+/* helper functions */
+static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+ int state)
+{
+ spin_lock(&stream->status_lock);
+ stream->stream_status = state;
+ spin_unlock(&stream->status_lock);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+ int state;
+
+ spin_lock(&stream->status_lock);
+ state = stream->stream_status;
+ spin_unlock(&stream->status_lock);
+ return state;
+}
+
+static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
+ struct snd_sst_stream_params *param)
+{
+
+ param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
+ param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+ param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+ param->uc.pcm_params.reserved = 0;
+ param->uc.pcm_params.sfreq = substream->runtime->rate;
+ param->uc.pcm_params.ring_buffer_size =
+ snd_pcm_lib_buffer_bytes(substream);
+ param->uc.pcm_params.period_count = substream->runtime->period_size;
+ param->uc.pcm_params.ring_buffer_addr =
+ virt_to_phys(substream->dma_buffer.area);
+ pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
+ pr_debug("sfreq= %d, wd_sz = %d\n",
+ param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ struct snd_sst_stream_params param = {{{0,},},};
+ struct snd_sst_params str_params = {0};
+ int ret_val;
+
+ /* set codec params and inform SST driver the same */
+ sst_fill_pcm_params(substream, ¶m);
+ substream->runtime->dma_area = substream->dma_buffer.area;
+ str_params.sparams = param;
+ str_params.codec = param.uc.pcm_params.codec;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ str_params.ops = STREAM_OPS_PLAYBACK;
+ str_params.device_type = substream->pcm->device + 1;
+ pr_debug("Playbck stream,Device %d\n",
+ substream->pcm->device);
+ } else {
+ str_params.ops = STREAM_OPS_CAPTURE;
+ str_params.device_type = SND_SST_DEVICE_CAPTURE;
+ pr_debug("Capture stream,Device %d\n",
+ substream->pcm->device);
+ }
+ ret_val = stream->sstdrv_ops->control_set(SST_SND_ALLOC, &str_params);
+ pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+ if (ret_val < 0)
+ return ret_val;
+
+ stream->stream_info.str_id = ret_val;
+ pr_debug("str id : %d\n", stream->stream_info.str_id);
+ return ret_val;
+}
+
+static void sst_period_elapsed(void *mad_substream)
+{
+ struct snd_pcm_substream *substream = mad_substream;
+ struct sst_runtime_stream *stream;
+ int status;
+
+ if (!substream || !substream->runtime)
+ return;
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return;
+ status = sst_get_stream_status(stream);
+ if (status != SST_PLATFORM_RUNNING)
+ return;
+ snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ int ret_val;
+
+ pr_debug("setting buffer ptr param\n");
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ stream->stream_info.period_elapsed = sst_period_elapsed;
+ stream->stream_info.mad_substream = substream;
+ stream->stream_info.buffer_ptr = 0;
+ stream->stream_info.sfreq = substream->runtime->rate;
+ ret_val = stream->sstdrv_ops->control_set(SST_SND_STREAM_INIT,
+ &stream->stream_info);
+ if (ret_val)
+ pr_err("control_set ret error %d\n", ret_val);
+ return ret_val;
+
+}
+/* end -- helper functions */
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct sst_runtime_stream *stream;
+ int ret_val = 0;
+
+ pr_debug("sst_platform_open called\n");
+ runtime = substream->runtime;
+ runtime->hw = sst_platform_pcm_hw;
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+ spin_lock_init(&stream->status_lock);
+ stream->stream_info.str_id = 0;
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ stream->stream_info.mad_substream = substream;
+ /* allocate memory for SST API set */
+ stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
+ GFP_KERNEL);
+ if (!stream->sstdrv_ops) {
+ pr_err("sst: mem allocation for ops fail\n");
+ kfree(stream);
+ return -ENOMEM;
+ }
+ stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+ /* registering with SST driver to get access to SST APIs to use */
+ ret_val = register_sst_card(stream->sstdrv_ops);
+ if (ret_val) {
+ pr_err("sst: sst card registration failed\n");
+ return ret_val;
+ }
+ runtime->private_data = stream;
+ return snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int sst_platform_close(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ pr_debug("sst_platform_close called\n");
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ if (str_id)
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_FREE, &str_id);
+ kfree(stream->sstdrv_ops);
+ kfree(stream);
+ return ret_val;
+}
+
+static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ pr_debug("sst_platform_pcm_prepare called\n");
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ if (stream->stream_info.str_id) {
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_DROP, &str_id);
+ return ret_val;
+ }
+
+ ret_val = sst_platform_alloc_stream(substream);
+ if (ret_val < 0)
+ return ret_val;
+ snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+ "%d", stream->stream_info.str_id);
+
+ ret_val = sst_platform_init_stream(substream);
+ if (ret_val)
+ return ret_val;
+ substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+ return ret_val;
+}
+
+static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int ret_val = 0, str_id;
+ struct sst_runtime_stream *stream;
+
+ pr_debug("sst_platform_pcm_trigger called\n");
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pr_debug("sst: Trigger Start\n");
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_START, &str_id);
+ if (ret_val)
+ break;
+ sst_set_stream_status(stream, SST_PLATFORM_RUNNING);
+ stream->stream_info.mad_substream = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("sst: in stop\n");
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_DROP, &str_id);
+ if (ret_val)
+ break;
+ sst_set_stream_status(stream, SST_PLATFORM_DROPPED);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("sst: in pause\n");
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_PAUSE, &str_id);
+ if (ret_val)
+ break;
+ sst_set_stream_status(stream, SST_PLATFORM_PAUSED);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("sst: in pause release\n");
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_RESUME, &str_id);
+ if (ret_val)
+ break;
+ sst_set_stream_status(stream, SST_PLATFORM_RUNNING);
+ break;
+ default:
+ ret_val = -EINVAL;
+ }
+ return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val, status;
+ struct pcm_stream_info *str_info;
+
+ stream = substream->runtime->private_data;
+ status = sst_get_stream_status(stream);
+ if (status == SST_PLATFORM_INIT)
+ return 0;
+ str_info = &stream->stream_info;
+ ret_val = stream->sstdrv_ops->control_set(
+ SST_SND_BUFFER_POINTER, str_info);
+ if (ret_val) {
+ pr_err("sst: error code = %d\n", ret_val);
+ return ret_val;
+ }
+ return stream->stream_info.buffer_ptr;
+}
+
+
+static struct snd_pcm_ops sst_platform_ops = {
+ .open = sst_platform_open,
+ .close = sst_platform_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = sst_platform_pcm_prepare,
+ .trigger = sst_platform_pcm_trigger,
+ .pointer = sst_platform_pcm_pointer,
+};
+
+static void sst_pcm_free(struct snd_pcm *pcm)
+{
+ pr_debug("sst_pcm_free called\n");
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int retval = 0;
+
+ pr_debug("sst_pcm_new called\n");
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ SST_MIN_BUFFER, SST_MAX_BUFFER);
+ if (retval) {
+ pr_err("dma buffer allocationf fail\n");
+ return retval;
+ }
+ }
+ return retval;
+}
+struct snd_soc_platform_driver sst_soc_platform_drv = {
+ .ops = &sst_platform_ops,
+ .pcm_new = sst_pcm_new,
+ .pcm_free = sst_pcm_free,
+};
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ pr_debug("sst_platform_probe called\n");
+ ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
+ if (ret) {
+ pr_err("registering soc platform failed\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_dais(&pdev->dev,
+ sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+ if (ret) {
+ pr_err("registering cpu dais failed\n");
+ snd_soc_unregister_platform(&pdev->dev);
+ }
+ return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+
+ snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
+ snd_soc_unregister_platform(&pdev->dev);
+ pr_debug("sst_platform_remove sucess\n");
+ return 0;
+}
+
+static struct platform_driver sst_platform_driver = {
+ .driver = {
+ .name = "sst-platform",
+ .owner = THIS_MODULE,
+ },
+ .probe = sst_platform_probe,
+ .remove = sst_platform_remove,
+};
+
+static int __init sst_soc_platform_init(void)
+{
+ pr_debug("sst_soc_platform_init called\n");
+ return platform_driver_register(&sst_platform_driver);
+}
+module_init(sst_soc_platform_init);
+
+static void __exit sst_soc_platform_exit(void)
+{
+ platform_driver_unregister(&sst_platform_driver);
+ pr_debug("sst_soc_platform_exit sucess\n");
+}
+module_exit(sst_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-platform");
diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h
new file mode 100644
index 0000000..df37028
--- /dev/null
+++ b/sound/soc/mid-x86/sst_platform.h
@@ -0,0 +1,63 @@
+/*
+ * sst_platform.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#ifndef __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#define SST_MONO 1
+#define SST_STEREO 2
+#define SST_MAX_CAP 5
+
+#define SST_MIN_RATE 8000
+#define SST_MAX_RATE 48000
+#define SST_MIN_CHANNEL 1
+#define SST_MAX_CHANNEL 5
+#define SST_MAX_BUFFER (800*1024)
+#define SST_MIN_BUFFER (800*1024)
+#define SST_MIN_PERIOD_BYTES 32
+#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER
+#define SST_MIN_PERIODS 2
+#define SST_MAX_PERIODS (1024*2)
+#define SST_FIFO_SIZE 0
+#define SST_CARD_NAMES "intel_mid_card"
+#define MSIC_VENDOR_ID 3
+
+struct sst_runtime_stream {
+ int stream_status;
+ struct pcm_stream_info stream_info;
+ struct intel_sst_card_ops *sstdrv_ops;
+ spinlock_t status_lock;
+};
+
+enum sst_drv_status {
+ SST_PLATFORM_INIT = 1,
+ SST_PLATFORM_STARTED,
+ SST_PLATFORM_RUNNING,
+ SST_PLATFORM_PAUSED,
+ SST_PLATFORM_DROPPED,
+};
+
+#endif
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 4770a95..f97110e 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -12,24 +12,24 @@
* published by the Free Software Foundation.
*/
-#include <linux/init.h>
-#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <sound/soc.h>
-#include <plat/regs-ac97.h>
#include <mach/dma.h>
+#include <plat/regs-ac97.h>
#include <plat/audio.h>
#include "dma.h"
-#include "ac97.h"
#define AC_CMD_ADDR(x) (x << 16)
#define AC_CMD_DATA(x) (x & 0xffff)
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
struct s3c_ac97_info {
struct clk *ac97_clk;
void __iomem *regs;
diff --git a/sound/soc/samsung/ac97.h b/sound/soc/samsung/ac97.h
deleted file mode 100644
index 0d0e1b5..0000000
--- a/sound/soc/samsung/ac97.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* sound/soc/samsung/ac97.h
- *
- * ALSA SoC Audio Layer - S3C AC97 Controller driver
- * Evolved from s3c2443-ac97.h
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- * Author: Jaswinder Singh <jassi.brar@samsung.com>
- * Credits: Graeme Gregory, Sean Choi
- *
- * 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 __S3C_AC97_H_
-#define __S3C_AC97_H_
-
-#define S3C_AC97_DAI_PCM 0
-#define S3C_AC97_DAI_MIC 1
-
-#endif /* __S3C_AC97_H_ */
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 2124019..9bce1df 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -14,17 +14,11 @@
* option) any later version.
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <mach/hardware.h>
@@ -32,6 +26,9 @@
#include "dma.h"
+#define ST_RUNNING (1<<0)
+#define ST_OPENED (1<<1)
+
static const struct snd_pcm_hardware dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index f8cd2b4..c506592 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -12,9 +12,6 @@
#ifndef _S3C_AUDIO_H
#define _S3C_AUDIO_H
-#define ST_RUNNING (1<<0)
-#define ST_OPENED (1<<1)
-
struct s3c_dma_params {
struct s3c2410_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
@@ -22,9 +19,4 @@
int dma_size; /* Size of the DMA transfer */
};
-#define S3C24XX_DAI_I2S 0
-
-/* platform data */
-extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
-
#endif
diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c
index 34dd9ef..f6b3a3c 100644
--- a/sound/soc/samsung/goni_wm8994.c
+++ b/sound/soc/samsung/goni_wm8994.c
@@ -11,21 +11,13 @@
*
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
#include <sound/soc.h>
#include <sound/jack.h>
+
#include <asm/mach-types.h>
#include <mach/gpio.h>
-#include <mach/regs-clock.h>
-#include <linux/mfd/wm8994/core.h>
-#include <linux/mfd/wm8994/registers.h>
#include "../codecs/wm8994.h"
-#include "dma.h"
-#include "i2s.h"
#define MACHINE_NAME 0
#define CPU_VOICE_DAI 1
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
index c45f7ce..241f55d 100644
--- a/sound/soc/samsung/h1940_uda1380.c
+++ b/sound/soc/samsung/h1940_uda1380.c
@@ -13,25 +13,16 @@
*
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
#include <linux/gpio.h>
#include <sound/soc.h>
-#include <sound/uda1380.h>
#include <sound/jack.h>
#include <plat/regs-iis.h>
-
#include <mach/h1940-latch.h>
-
#include <asm/mach-types.h>
-#include "dma.h"
#include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
static unsigned int rates[] = {
11025,
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index d00ac3a..ffa09b3 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -15,9 +15,8 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <plat/audio.h>
diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c
index 0880252..3b53ad5 100644
--- a/sound/soc/samsung/jive_wm8750.c
+++ b/sound/soc/samsung/jive_wm8750.c
@@ -11,22 +11,11 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
#include <asm/mach-types.h>
-#include "dma.h"
#include "s3c2412-i2s.h"
-
#include "../codecs/wm8750.h"
static const struct snd_soc_dapm_route audio_map[] = {
diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c
index a2bb34d..bd91c19 100644
--- a/sound/soc/samsung/ln2440sbc_alc650.c
+++ b/sound/soc/samsung/ln2440sbc_alc650.c
@@ -16,15 +16,8 @@
*
*/
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
-#include "dma.h"
-#include "ac97.h"
-
static struct snd_soc_card ln2440sbc;
static struct snd_soc_dai_link ln2440sbc_dai[] = {
diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c
index 3eec610..69e08fd 100644
--- a/sound/soc/samsung/neo1973_gta02_wm8753.c
+++ b/sound/soc/samsung/neo1973_gta02_wm8753.c
@@ -13,25 +13,15 @@
* option) any later version.
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
+
#include <sound/soc.h>
#include <asm/mach-types.h>
-
#include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-#include <asm/io.h>
#include <mach/gta02.h>
+
#include "../codecs/wm8753.h"
-#include "dma.h"
#include "s3c24xx-i2s.h"
static struct snd_soc_card neo1973_gta02;
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 48d0b75..38aac7d 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -11,20 +11,11 @@
* published by the Free Software Foundation.
*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
#include <linux/clk.h>
-#include <linux/kernel.h>
-#include <linux/gpio.h>
#include <linux/io.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <plat/audio.h>
#include <plat/dma.h>
@@ -32,6 +23,113 @@
#include "dma.h"
#include "pcm.h"
+/*Register Offsets */
+#define S3C_PCM_CTL 0x00
+#define S3C_PCM_CLKCTL 0x04
+#define S3C_PCM_TXFIFO 0x08
+#define S3C_PCM_RXFIFO 0x0C
+#define S3C_PCM_IRQCTL 0x10
+#define S3C_PCM_IRQSTAT 0x14
+#define S3C_PCM_FIFOSTAT 0x18
+#define S3C_PCM_CLRINT 0x20
+
+/* PCM_CTL Bit-Fields */
+#define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f
+#define S3C_PCM_CTL_TXDIPSTICK_SHIFT 13
+#define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f
+#define S3C_PCM_CTL_RXDIPSTICK_SHIFT 7
+#define S3C_PCM_CTL_TXDMA_EN (0x1 << 6)
+#define S3C_PCM_CTL_RXDMA_EN (0x1 << 5)
+#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1 << 4)
+#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1 << 3)
+#define S3C_PCM_CTL_TXFIFO_EN (0x1 << 2)
+#define S3C_PCM_CTL_RXFIFO_EN (0x1 << 1)
+#define S3C_PCM_CTL_ENABLE (0x1 << 0)
+
+/* PCM_CLKCTL Bit-Fields */
+#define S3C_PCM_CLKCTL_SERCLK_EN (0x1 << 19)
+#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1 << 18)
+#define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff
+#define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff
+#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT 9
+#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT 0
+
+/* PCM_TXFIFO Bit-Fields */
+#define S3C_PCM_TXFIFO_DVALID (0x1 << 16)
+#define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0)
+
+/* PCM_RXFIFO Bit-Fields */
+#define S3C_PCM_RXFIFO_DVALID (0x1 << 16)
+#define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0)
+
+/* PCM_IRQCTL Bit-Fields */
+#define S3C_PCM_IRQCTL_IRQEN (0x1 << 14)
+#define S3C_PCM_IRQCTL_WRDEN (0x1 << 12)
+#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1 << 11)
+#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1 << 10)
+#define S3C_PCM_IRQCTL_TXFULLEN (0x1 << 9)
+#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1 << 8)
+#define S3C_PCM_IRQCTL_TXSTARVEN (0x1 << 7)
+#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6)
+#define S3C_PCM_IRQCTL_RXEMPTEN (0x1 << 5)
+#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1 << 4)
+#define S3C_PCM_IRQCTL_RXFULLEN (0x1 << 3)
+#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1 << 2)
+#define S3C_PCM_IRQCTL_RXSTARVEN (0x1 << 1)
+#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0)
+
+/* PCM_IRQSTAT Bit-Fields */
+#define S3C_PCM_IRQSTAT_IRQPND (0x1 << 13)
+#define S3C_PCM_IRQSTAT_WRD_XFER (0x1 << 12)
+#define S3C_PCM_IRQSTAT_TXEMPTY (0x1 << 11)
+#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1 << 10)
+#define S3C_PCM_IRQSTAT_TXFULL (0x1 << 9)
+#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8)
+#define S3C_PCM_IRQSTAT_TXSTARV (0x1 << 7)
+#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1 << 6)
+#define S3C_PCM_IRQSTAT_RXEMPT (0x1 << 5)
+#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4)
+#define S3C_PCM_IRQSTAT_RXFULL (0x1 << 3)
+#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2)
+#define S3C_PCM_IRQSTAT_RXSTARV (0x1 << 1)
+#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1 << 0)
+
+/* PCM_FIFOSTAT Bit-Fields */
+#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f << 14)
+#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1 << 13)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1 << 12)
+#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1 << 11)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1 << 10)
+#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f << 4)
+#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1 << 3)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1 << 2)
+#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1 << 1)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1 << 0)
+
+/**
+ * struct s3c_pcm_info - S3C PCM Controller information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device register block.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ */
+struct s3c_pcm_info {
+ spinlock_t lock;
+ struct device *dev;
+ void __iomem *regs;
+
+ unsigned int sclk_per_fs;
+
+ /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
+ unsigned int idleclk;
+
+ struct clk *pclk;
+ struct clk *cclk;
+
+ struct s3c_dma_params *dma_playback;
+ struct s3c_dma_params *dma_capture;
+};
+
static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
.name = "PCM Stereo out"
};
diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h
index 03393dc..726baf8 100644
--- a/sound/soc/samsung/pcm.h
+++ b/sound/soc/samsung/pcm.h
@@ -9,116 +9,9 @@
#ifndef __S3C_PCM_H
#define __S3C_PCM_H __FILE__
-/*Register Offsets */
-#define S3C_PCM_CTL (0x00)
-#define S3C_PCM_CLKCTL (0x04)
-#define S3C_PCM_TXFIFO (0x08)
-#define S3C_PCM_RXFIFO (0x0C)
-#define S3C_PCM_IRQCTL (0x10)
-#define S3C_PCM_IRQSTAT (0x14)
-#define S3C_PCM_FIFOSTAT (0x18)
-#define S3C_PCM_CLRINT (0x20)
-
-/* PCM_CTL Bit-Fields */
-#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f)
-#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13)
-#define S3C_PCM_CTL_RXDIPSTICK_MASK (0x3f)
-#define S3C_PCM_CTL_RXDIPSTICK_SHIFT (7)
-#define S3C_PCM_CTL_TXDMA_EN (0x1<<6)
-#define S3C_PCM_CTL_RXDMA_EN (0x1<<5)
-#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4)
-#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3)
-#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2)
-#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1)
-#define S3C_PCM_CTL_ENABLE (0x1<<0)
-
-/* PCM_CLKCTL Bit-Fields */
-#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19)
-#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18)
-#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff)
-#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff)
-#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9)
-#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0)
-
-/* PCM_TXFIFO Bit-Fields */
-#define S3C_PCM_TXFIFO_DVALID (0x1<<16)
-#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0)
-
-/* PCM_RXFIFO Bit-Fields */
-#define S3C_PCM_RXFIFO_DVALID (0x1<<16)
-#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0)
-
-/* PCM_IRQCTL Bit-Fields */
-#define S3C_PCM_IRQCTL_IRQEN (0x1<<14)
-#define S3C_PCM_IRQCTL_WRDEN (0x1<<12)
-#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11)
-#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10)
-#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9)
-#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8)
-#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7)
-#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6)
-#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5)
-#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4)
-#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3)
-#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2)
-#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1)
-#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0)
-
-/* PCM_IRQSTAT Bit-Fields */
-#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13)
-#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12)
-#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11)
-#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10)
-#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9)
-#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8)
-#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7)
-#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6)
-#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5)
-#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4)
-#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3)
-#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2)
-#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1)
-#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0)
-
-/* PCM_FIFOSTAT Bit-Fields */
-#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14)
-#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12)
-#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10)
-#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4)
-#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2)
-#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0)
-
#define S3C_PCM_CLKSRC_PCLK 0
#define S3C_PCM_CLKSRC_MUX 1
#define S3C_PCM_SCLK_PER_FS 0
-/**
- * struct s3c_pcm_info - S3C PCM Controller information
- * @dev: The parent device passed to use from the probe.
- * @regs: The pointer to the device register block.
- * @dma_playback: DMA information for playback channel.
- * @dma_capture: DMA information for capture channel.
- */
-struct s3c_pcm_info {
- spinlock_t lock;
- struct device *dev;
- void __iomem *regs;
-
- unsigned int sclk_per_fs;
-
- /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
- unsigned int idleclk;
-
- struct clk *pclk;
- struct clk *cclk;
-
- struct s3c_dma_params *dma_playback;
- struct s3c_dma_params *dma_capture;
-};
-
#endif /* __S3C_PCM_H */
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
index f400274..1e574a5 100644
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -17,26 +17,15 @@
*
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
#include <linux/gpio.h>
-#include <linux/clk.h>
#include <sound/soc.h>
-#include <sound/uda1380.h>
#include <sound/jack.h>
#include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-
#include <asm/mach-types.h>
-#include "dma.h"
#include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
static int rx1950_startup(struct snd_pcm_substream *substream);
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index 094f36e..52074a2b 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -20,9 +20,8 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <mach/dma.h>
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 7ea8378..841ab14 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -16,21 +16,13 @@
* option) any later version.
*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/clk.h>
-#include <linux/kernel.h>
#include <linux/io.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
+#include <sound/pcm_params.h>
#include <mach/regs-gpio.h>
#include <mach/dma.h>
@@ -39,8 +31,6 @@
#include "regs-i2s-v2.h"
#include "s3c2412-i2s.h"
-#define S3C2412_I2S_DEBUG 0
-
static struct s3c2410_dma_client s3c2412_dma_client_out = {
.name = "I2S PCM Stereo out"
};
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index 13e41ed..63d8849 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -14,28 +14,16 @@
* option) any later version.
*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
#include <linux/delay.h>
#include <linux/clk.h>
-#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
-#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <mach/regs-clock.h>
-
-#include <asm/dma.h>
#include <mach/dma.h>
-
#include <plat/regs-iis.h>
#include "dma.h"
diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c
index a434032d..349566f 100644
--- a/sound/soc/samsung/s3c24xx_simtec.c
+++ b/sound/soc/samsung/s3c24xx_simtec.c
@@ -7,20 +7,13 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
#include <plat/audio-simtec.h>
-#include "dma.h"
#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c
index bb4292e..d7b3e6e 100644
--- a/sound/soc/samsung/s3c24xx_simtec_hermes.c
+++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c
@@ -7,18 +7,8 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
static const struct snd_soc_dapm_widget dapm_widgets[] = {
diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
index fbba4e3..ff6168f 100644
--- a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
+++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
@@ -7,22 +7,10 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
-#include "../codecs/tlv320aic23.h"
-
/* supported machines:
*
* Machine Connections AMP
diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c
index cdc8ecb..ce749a1 100644
--- a/sound/soc/samsung/s3c24xx_uda134x.c
+++ b/sound/soc/samsung/s3c24xx_uda134x.c
@@ -11,22 +11,15 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
#include <linux/clk.h>
-#include <linux/mutex.h>
#include <linux/gpio.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
+
#include <sound/soc.h>
#include <sound/s3c24xx_uda134x.h>
-#include <sound/uda134x.h>
#include <plat/regs-iis.h>
-#include "dma.h"
#include "s3c24xx-i2s.h"
-#include "../codecs/uda134x.h"
-
/* #define ENFORCE_RATES 1 */
/*
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
index 61e2b52..0a2c4f2 100644
--- a/sound/soc/samsung/smartq_wm8987.c
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -13,20 +13,14 @@
*
*/
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/gpio.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
-#include "dma.h"
#include "i2s.h"
-
#include "../codecs/wm8750.h"
/*
diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c
index 3be7e7e..3a0dbfc 100644
--- a/sound/soc/samsung/smdk2443_wm9710.c
+++ b/sound/soc/samsung/smdk2443_wm9710.c
@@ -12,15 +12,8 @@
*
*/
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/soc.h>
-#include "dma.h"
-#include "ac97.h"
-
static struct snd_soc_card smdk2443;
static struct snd_soc_dai_link smdk2443_dai[] = {
diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c
index cb2f4d0..d42fe8d 100644
--- a/sound/soc/samsung/smdk_spdif.c
+++ b/sound/soc/samsung/smdk_spdif.c
@@ -10,15 +10,10 @@
*
*/
-#include <linux/module.h>
-#include <linux/device.h>
#include <linux/clk.h>
-#include <plat/devs.h>
-
#include <sound/soc.h>
-#include "dma.h"
#include "spdif.h"
/* Audio clock settings are belonged to board specific part. Every
diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c
index b2cff1a..8aacf23 100644
--- a/sound/soc/samsung/smdk_wm8580.c
+++ b/sound/soc/samsung/smdk_wm8580.c
@@ -10,17 +10,12 @@
* option) any later version.
*/
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <asm/mach-types.h>
#include "../codecs/wm8580.h"
-#include "dma.h"
#include "i2s.h"
/*
diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c
index ae5fed6..fffe3c1 100644
--- a/sound/soc/samsung/smdk_wm9713.c
+++ b/sound/soc/samsung/smdk_wm9713.c
@@ -11,13 +11,8 @@
*
*/
-#include <linux/module.h>
-#include <linux/device.h>
#include <sound/soc.h>
-#include "dma.h"
-#include "ac97.h"
-
static struct snd_soc_card smdk;
/*
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index f0816404..28c491d 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -13,9 +13,8 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <plat/audio.h>
#include <mach/dma.h>
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
index a14820a..56cd342 100644
--- a/sound/soc/sh/fsi-ak4642.c
+++ b/sound/soc/sh/fsi-ak4642.c
@@ -18,6 +18,7 @@
const char *cpu_dai;
const char *codec;
const char *platform;
+ int id;
};
static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
@@ -60,7 +61,7 @@
pdata = (struct fsi_ak4642_data *)id_entry->driver_data;
- fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A);
+ fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
if (!fsi_snd_device)
goto out;
@@ -93,6 +94,7 @@
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
+ .id = FSI_PORT_A,
};
static struct fsi_ak4642_data fsi_b_ak4642 = {
@@ -101,6 +103,7 @@
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
+ .id = FSI_PORT_B,
};
static struct fsi_ak4642_data fsi_a_ak4643 = {
@@ -109,6 +112,7 @@
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
+ .id = FSI_PORT_A,
};
static struct fsi_ak4642_data fsi_b_ak4643 = {
@@ -117,6 +121,7 @@
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
+ .id = FSI_PORT_B,
};
static struct fsi_ak4642_data fsi2_a_ak4642 = {
@@ -125,6 +130,7 @@
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
+ .id = FSI_PORT_A,
};
static struct fsi_ak4642_data fsi2_b_ak4642 = {
@@ -133,6 +139,7 @@
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
+ .id = FSI_PORT_B,
};
static struct fsi_ak4642_data fsi2_a_ak4643 = {
@@ -141,6 +148,7 @@
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",
+ .id = FSI_PORT_A,
};
static struct fsi_ak4642_data fsi2_b_ak4643 = {
@@ -149,6 +157,7 @@
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",
+ .id = FSI_PORT_B,
};
static struct platform_device_id fsi_id_table[] = {
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 8c2a21a..1a36b36 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -761,6 +761,49 @@
}
EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
+static bool snd_soc_set_cache_val(void *base, unsigned int idx,
+ unsigned int val, unsigned int word_size)
+{
+ switch (word_size) {
+ case 1: {
+ u8 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ case 2: {
+ u16 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ default:
+ BUG();
+ }
+ return false;
+}
+
+static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
+ unsigned int word_size)
+{
+ switch (word_size) {
+ case 1: {
+ const u8 *cache = base;
+ return cache[idx];
+ }
+ case 2: {
+ const u16 *cache = base;
+ return cache[idx];
+ }
+ default:
+ BUG();
+ }
+ /* unreachable */
+ return -1;
+}
+
struct snd_soc_rbtree_node {
struct rb_node node;
unsigned int reg;
@@ -924,7 +967,12 @@
static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
{
+ struct snd_soc_rbtree_node *rbtree_node;
struct snd_soc_rbtree_ctx *rbtree_ctx;
+ unsigned int val;
+ unsigned int word_size;
+ int i;
+ int ret;
codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
if (!codec->reg_cache)
@@ -936,53 +984,25 @@
if (!codec->reg_def_copy)
return 0;
-/*
- * populate the rbtree with the initialized registers. All other
- * registers will be inserted into the tree when they are first written.
- *
- * The reasoning behind this, is that we need to step through and
- * dereference the cache in u8/u16 increments without sacrificing
- * portability. This could also be done using memcpy() but that would
- * be slightly more cryptic.
- */
-#define snd_soc_rbtree_populate(cache) \
-({ \
- int ret, i; \
- struct snd_soc_rbtree_node *rbtree_node; \
- \
- ret = 0; \
- cache = codec->reg_def_copy; \
- for (i = 0; i < codec->driver->reg_cache_size; ++i) { \
- if (!cache[i]) \
- continue; \
- rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \
- if (!rbtree_node) { \
- ret = -ENOMEM; \
- snd_soc_cache_exit(codec); \
- break; \
- } \
- rbtree_node->reg = i; \
- rbtree_node->value = cache[i]; \
- rbtree_node->defval = cache[i]; \
- snd_soc_rbtree_insert(&rbtree_ctx->root, \
- rbtree_node); \
- } \
- ret; \
-})
-
- switch (codec->driver->reg_word_size) {
- case 1: {
- const u8 *cache;
-
- return snd_soc_rbtree_populate(cache);
- }
- case 2: {
- const u16 *cache;
-
- return snd_soc_rbtree_populate(cache);
- }
- default:
- BUG();
+ /*
+ * populate the rbtree with the initialized registers. All other
+ * registers will be inserted when they are first modified.
+ */
+ word_size = codec->driver->reg_word_size;
+ for (i = 0; i < codec->driver->reg_cache_size; ++i) {
+ val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+ if (!val)
+ continue;
+ rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
+ if (!rbtree_node) {
+ ret = -ENOMEM;
+ snd_soc_cache_exit(codec);
+ break;
+ }
+ rbtree_node->reg = i;
+ rbtree_node->value = val;
+ rbtree_node->defval = val;
+ snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
}
return 0;
@@ -1080,34 +1100,28 @@
unsigned int reg)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
return (reg * codec_drv->reg_word_size) /
- DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+ DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
}
static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
unsigned int reg)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) /
+ return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) /
codec_drv->reg_word_size);
}
static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+ return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
}
static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
@@ -1165,29 +1179,10 @@
}
/* write the new value to the cache */
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
- cache = lzo_block->dst;
- if (cache[blkpos] == value) {
- kfree(lzo_block->dst);
- goto out;
- }
- cache[blkpos] = value;
- }
- break;
- case 2: {
- u16 *cache;
- cache = lzo_block->dst;
- if (cache[blkpos] == value) {
- kfree(lzo_block->dst);
- goto out;
- }
- cache[blkpos] = value;
- }
- break;
- default:
- BUG();
+ if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value,
+ codec->driver->reg_word_size)) {
+ kfree(lzo_block->dst);
+ goto out;
}
/* prepare the source to be the decompressed block */
@@ -1241,25 +1236,10 @@
/* decompress the block */
ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
- if (ret >= 0) {
+ if (ret >= 0)
/* fetch the value from the cache */
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
- cache = lzo_block->dst;
- *value = cache[blkpos];
- }
- break;
- case 2: {
- u16 *cache;
- cache = lzo_block->dst;
- *value = cache[blkpos];
- }
- break;
- default:
- BUG();
- }
- }
+ *value = snd_soc_get_cache_val(lzo_block->dst, blkpos,
+ codec->driver->reg_word_size);
kfree(lzo_block->dst);
/* restore the pointer and length of the compressed block */
@@ -1301,7 +1281,7 @@
static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
{
struct snd_soc_lzo_ctx **lzo_blocks;
- size_t reg_size, bmp_size;
+ size_t bmp_size;
const struct snd_soc_codec_driver *codec_drv;
int ret, tofree, i, blksize, blkcount;
const char *p, *end;
@@ -1309,7 +1289,6 @@
ret = 0;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
/*
* If we have not been given a default register cache
@@ -1321,8 +1300,7 @@
tofree = 1;
if (!codec->reg_def_copy) {
- codec->reg_def_copy = kzalloc(reg_size,
- GFP_KERNEL);
+ codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL);
if (!codec->reg_def_copy)
return -ENOMEM;
}
@@ -1370,7 +1348,7 @@
blksize = snd_soc_lzo_get_blksize(codec);
p = codec->reg_def_copy;
- end = codec->reg_def_copy + reg_size;
+ end = codec->reg_def_copy + codec->reg_size;
/* compress the register map and fill the lzo blocks */
for (i = 0; i < blkcount; ++i, p += blksize) {
lzo_blocks[i]->src = p;
@@ -1414,28 +1392,10 @@
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
- if (codec_drv->reg_cache_default) {
- switch (codec_drv->reg_word_size) {
- case 1: {
- const u8 *cache;
-
- cache = codec_drv->reg_cache_default;
- if (cache[i] == val)
- continue;
- }
- break;
- case 2: {
- const u16 *cache;
-
- cache = codec_drv->reg_cache_default;
- if (cache[i] == val)
- continue;
- }
- break;
- default:
- BUG();
- }
- }
+ if (codec->reg_def_copy)
+ if (snd_soc_get_cache_val(codec->reg_def_copy,
+ i, codec_drv->reg_word_size) == val)
+ continue;
ret = snd_soc_write(codec, i, val);
if (ret)
return ret;
@@ -1448,50 +1408,16 @@
static int snd_soc_flat_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
-
- cache = codec->reg_cache;
- cache[reg] = value;
- }
- break;
- case 2: {
- u16 *cache;
-
- cache = codec->reg_cache;
- cache[reg] = value;
- }
- break;
- default:
- BUG();
- }
-
+ snd_soc_set_cache_val(codec->reg_cache, reg, value,
+ codec->driver->reg_word_size);
return 0;
}
static int snd_soc_flat_cache_read(struct snd_soc_codec *codec,
unsigned int reg, unsigned int *value)
{
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
-
- cache = codec->reg_cache;
- *value = cache[reg];
- }
- break;
- case 2: {
- u16 *cache;
-
- cache = codec->reg_cache;
- *value = cache[reg];
- }
- break;
- default:
- BUG();
- }
-
+ *value = snd_soc_get_cache_val(codec->reg_cache, reg,
+ codec->driver->reg_word_size);
return 0;
}
@@ -1507,24 +1433,14 @@
static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- /*
- * for flat compression, we don't need to keep a copy of the
- * original defaults register cache as it will definitely not
- * be marked as __devinitconst
- */
- kfree(codec->reg_def_copy);
- codec->reg_def_copy = NULL;
-
- if (codec_drv->reg_cache_default)
- codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
- reg_size, GFP_KERNEL);
+ if (codec->reg_def_copy)
+ codec->reg_cache = kmemdup(codec->reg_def_copy,
+ codec->reg_size, GFP_KERNEL);
else
- codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
+ codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
if (!codec->reg_cache)
return -ENOMEM;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index bac7291..cbac50b 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -48,7 +48,8 @@
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
#ifdef CONFIG_DEBUG_FS
-static struct dentry *debugfs_root;
+struct dentry *snd_soc_debugfs_root;
+EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
#endif
static DEFINE_MUTEX(client_mutex);
@@ -209,6 +210,10 @@
start++;
if (strict_strtoul(start, 16, &value))
return -EINVAL;
+
+ /* Userspace has been fiddling around behind the kernel's back */
+ add_taint(TAINT_USER);
+
snd_soc_write(codec, reg, value);
return buf_size;
}
@@ -356,7 +361,7 @@
static void soc_init_card_debugfs(struct snd_soc_card *card)
{
card->debugfs_card_root = debugfs_create_dir(card->name,
- debugfs_root);
+ snd_soc_debugfs_root);
if (!card->debugfs_card_root) {
dev_warn(card->dev,
"ASoC: Failed to create codec debugfs directory\n");
@@ -1743,6 +1748,8 @@
list_for_each_entry(codec, &codec_list, list) {
if (codec->cache_init)
continue;
+ /* by default we don't override the compress_type */
+ compress_type = 0;
/* check to see if we need to override the compress_type */
for (i = 0; i < card->num_configs; ++i) {
codec_conf = &card->codec_conf[i];
@@ -1753,18 +1760,6 @@
break;
}
}
- if (i == card->num_configs) {
- /* no need to override the compress_type so
- * go ahead and do the standard thing */
- ret = snd_soc_init_codec_cache(codec, 0);
- if (ret < 0) {
- mutex_unlock(&card->mutex);
- return;
- }
- continue;
- }
- /* override the compress_type with the one supplied in
- * the machine driver */
ret = snd_soc_init_codec_cache(codec, compress_type);
if (ret < 0) {
mutex_unlock(&card->mutex);
@@ -1901,7 +1896,7 @@
struct snd_soc_card *card = platform_get_drvdata(pdev);
int i;
- if (card->instantiated) {
+ if (card->instantiated) {
/* make sure any delayed work runs */
for (i = 0; i < card->num_rtd; i++) {
@@ -2132,19 +2127,27 @@
*
* Writes new register value.
*
- * Returns 1 for change else 0.
+ * Returns 1 for change, 0 for no change, or negative error code.
*/
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned int mask, unsigned int value)
{
int change;
unsigned int old, new;
+ int ret;
- old = snd_soc_read(codec, reg);
+ ret = snd_soc_read(codec, reg);
+ if (ret < 0)
+ return ret;
+
+ old = ret;
new = (old & ~mask) | value;
change = old != new;
- if (change)
- snd_soc_write(codec, reg, new);
+ if (change) {
+ ret = snd_soc_write(codec, reg, new);
+ if (ret < 0)
+ return ret;
+ }
return change;
}
@@ -3497,17 +3500,20 @@
/* allocate CODEC register cache */
if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
+ codec->reg_size = reg_size;
/* it is necessary to make a copy of the default register cache
* because in the case of using a compression type that requires
* the default register cache to be marked as __devinitconst the
* kernel might have freed the array by the time we initialize
* the cache.
*/
- codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
- reg_size, GFP_KERNEL);
- if (!codec->reg_def_copy) {
- ret = -ENOMEM;
- goto fail;
+ if (codec_drv->reg_cache_default) {
+ codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
+ reg_size, GFP_KERNEL);
+ if (!codec->reg_def_copy) {
+ ret = -ENOMEM;
+ goto fail;
+ }
}
}
@@ -3577,22 +3583,22 @@
static int __init snd_soc_init(void)
{
#ifdef CONFIG_DEBUG_FS
- debugfs_root = debugfs_create_dir("asoc", NULL);
- if (IS_ERR(debugfs_root) || !debugfs_root) {
+ snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
+ if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
printk(KERN_WARNING
"ASoC: Failed to create debugfs directory\n");
- debugfs_root = NULL;
+ snd_soc_debugfs_root = NULL;
}
- if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL,
+ if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
&codec_list_fops))
pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
- if (!debugfs_create_file("dais", 0444, debugfs_root, NULL,
+ if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
&dai_list_fops))
pr_warn("ASoC: Failed to create DAI list debugfs file\n");
- if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL,
+ if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
&platform_list_fops))
pr_warn("ASoC: Failed to create platform list debugfs file\n");
#endif
@@ -3604,7 +3610,7 @@
static void __exit snd_soc_exit(void)
{
#ifdef CONFIG_DEBUG_FS
- debugfs_remove_recursive(debugfs_root);
+ debugfs_remove_recursive(snd_soc_debugfs_root);
#endif
platform_driver_unregister(&soc_driver);
}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
new file mode 100644
index 0000000..5514f1c
--- /dev/null
+++ b/sound/soc/tegra/Kconfig
@@ -0,0 +1,27 @@
+config SND_TEGRA_SOC
+ tristate "SoC Audio for the Tegra System-on-Chip"
+ depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+ default m
+ help
+ Say Y or M here if you want support for SoC audio on Tegra.
+
+config SND_TEGRA_SOC_I2S
+ tristate
+ depends on SND_TEGRA_SOC
+ depends on !TEGRA_I2S_AUDIO
+ default m
+ help
+ Say Y or M if you want to add support for codecs attached to the
+ Tegra I2S interface. You will also need to select the individual
+ machine drivers to support below.
+
+config SND_TEGRA_SOC_HARMONY
+ tristate "SoC Audio support for Tegra Harmony reference board"
+ depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
+ default m
+ select SND_TEGRA_SOC_I2S
+ select SND_SOC_WM8903
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ Tegra Harmony reference board.
+
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
new file mode 100644
index 0000000..dfd2ab9
--- /dev/null
+++ b/sound/soc/tegra/Makefile
@@ -0,0 +1,14 @@
+# Tegra platform Support
+snd-soc-tegra-das-objs := tegra_das.o
+snd-soc-tegra-pcm-objs := tegra_pcm.o
+snd-soc-tegra-i2s-objs := tegra_i2s.o
+
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+
+# Tegra machine Support
+snd-soc-tegra-harmony-objs := harmony.o
+snd-soc-tegra-harmony-objs += tegra_asoc_utils.o
+
+obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c
new file mode 100644
index 0000000..bf0dbaf
--- /dev/null
+++ b/sound/soc/tegra/harmony.c
@@ -0,0 +1,179 @@
+/*
+ * harmony.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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 <asm/mach-types.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define PREFIX "ASoC Harmony: "
+
+static struct platform_device *harmony_snd_device;
+
+static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int srate, mclk, mclk_change;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 64000:
+ case 88200:
+ case 96000:
+ mclk = 128 * srate;
+ break;
+ default:
+ mclk = 256 * srate;
+ break;
+ }
+ /* FIXME: Codec only requires >= 3MHz if OSR==0 */
+ while (mclk < 6000000)
+ mclk *= 2;
+
+ err = tegra_asoc_utils_set_rate(srate, mclk, &mclk_change);
+ if (err < 0) {
+ pr_err(PREFIX "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ pr_err(PREFIX "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ pr_err(PREFIX "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ if (mclk_change) {
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ pr_err(PREFIX "codec_dai clock not set\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops harmony_asoc_ops = {
+ .hw_params = harmony_asoc_hw_params,
+};
+
+static struct snd_soc_dai_link harmony_wm8903_dai = {
+ .name = "WM8903",
+ .stream_name = "WM8903 PCM",
+ .codec_name = "wm8903-codec.0-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "wm8903-hifi",
+ .ops = &harmony_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_harmony = {
+ .name = "tegra-harmony",
+ .dai_link = &harmony_wm8903_dai,
+ .num_links = 1,
+};
+
+static int __init harmony_soc_modinit(void)
+{
+ int ret;
+
+ if (!machine_is_harmony()) {
+ pr_err(PREFIX "Not running on Tegra Harmony!\n");
+ return -ENODEV;
+ }
+
+ ret = tegra_asoc_utils_init();
+ if (ret) {
+ return ret;
+ }
+
+ /*
+ * Create and register platform device
+ */
+ harmony_snd_device = platform_device_alloc("soc-audio", -1);
+ if (harmony_snd_device == NULL) {
+ pr_err(PREFIX "platform_device_alloc failed\n");
+ ret = -ENOMEM;
+ goto err_clock_utils;
+ }
+
+ platform_set_drvdata(harmony_snd_device, &snd_soc_harmony);
+
+ ret = platform_device_add(harmony_snd_device);
+ if (ret) {
+ pr_err(PREFIX "platform_device_add failed (%d)\n",
+ ret);
+ goto err_device_put;
+ }
+
+ return 0;
+
+err_device_put:
+ platform_device_put(harmony_snd_device);
+err_clock_utils:
+ tegra_asoc_utils_fini();
+ return ret;
+}
+module_init(harmony_soc_modinit);
+
+static void __exit harmony_soc_modexit(void)
+{
+ platform_device_unregister(harmony_snd_device);
+
+ tegra_asoc_utils_fini();
+}
+module_exit(harmony_soc_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Harmony machine ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
new file mode 100644
index 0000000..711ab7f
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -0,0 +1,154 @@
+/*
+ * tegra_asoc_utils.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * 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/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include "tegra_asoc_utils.h"
+
+#define PREFIX "ASoC Tegra: "
+
+static struct clk *clk_pll_a;
+static struct clk *clk_pll_a_out0;
+static struct clk *clk_cdev1;
+
+static int set_baseclock, set_mclk;
+
+int tegra_asoc_utils_set_rate(int srate, int mclk, int *mclk_change)
+{
+ int new_baseclock;
+ int err;
+
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ new_baseclock = 56448000;
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ new_baseclock = 73728000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *mclk_change = ((new_baseclock != set_baseclock) ||
+ (mclk != set_mclk));
+ if (!*mclk_change)
+ return 0;
+
+ set_baseclock = 0;
+ set_mclk = 0;
+
+ clk_disable(clk_cdev1);
+ clk_disable(clk_pll_a_out0);
+ clk_disable(clk_pll_a);
+
+ err = clk_set_rate(clk_pll_a, new_baseclock);
+ if (err) {
+ pr_err(PREFIX "Can't set pll_a rate: %d\n", err);
+ return err;
+ }
+
+ err = clk_set_rate(clk_pll_a_out0, mclk);
+ if (err) {
+ pr_err(PREFIX "Can't set pll_a_out0 rate: %d\n", err);
+ return err;
+ }
+
+ /* Don't set cdev1 rate; its locked to pll_a_out0 */
+
+ err = clk_enable(clk_pll_a);
+ if (err) {
+ pr_err(PREFIX "Can't enable pll_a: %d\n", err);
+ return err;
+ }
+
+ err = clk_enable(clk_pll_a_out0);
+ if (err) {
+ pr_err(PREFIX "Can't enable pll_a_out0: %d\n", err);
+ return err;
+ }
+
+ err = clk_enable(clk_cdev1);
+ if (err) {
+ pr_err(PREFIX "Can't enable cdev1: %d\n", err);
+ return err;
+ }
+
+ set_baseclock = new_baseclock;
+ set_mclk = mclk;
+
+ return 0;
+}
+
+int tegra_asoc_utils_init(void)
+{
+ int ret;
+
+ clk_pll_a = clk_get_sys(NULL, "pll_a");
+ if (IS_ERR_OR_NULL(clk_pll_a)) {
+ pr_err(PREFIX "Can't retrieve clk pll_a\n");
+ ret = PTR_ERR(clk_pll_a);
+ goto err;
+ }
+
+ clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ if (IS_ERR_OR_NULL(clk_pll_a_out0)) {
+ pr_err(PREFIX "Can't retrieve clk pll_a_out0\n");
+ ret = PTR_ERR(clk_pll_a_out0);
+ goto err;
+ }
+
+ clk_cdev1 = clk_get_sys(NULL, "cdev1");
+ if (IS_ERR_OR_NULL(clk_cdev1)) {
+ pr_err(PREFIX "Can't retrieve clk cdev1\n");
+ ret = PTR_ERR(clk_cdev1);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (!IS_ERR_OR_NULL(clk_cdev1))
+ clk_put(clk_cdev1);
+ if (!IS_ERR_OR_NULL(clk_pll_a_out0))
+ clk_put(clk_pll_a_out0);
+ if (!IS_ERR_OR_NULL(clk_pll_a))
+ clk_put(clk_pll_a);
+ return ret;
+}
+
+void tegra_asoc_utils_fini(void)
+{
+ clk_put(clk_cdev1);
+ clk_put(clk_pll_a_out0);
+ clk_put(clk_pll_a);
+}
+
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
new file mode 100644
index 0000000..855f8f6
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -0,0 +1,31 @@
+/*
+ * tegra_asoc_utils.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef __TEGRA_ASOC_UTILS_H__
+#define __TEGRA_ASOC_UTILS_H_
+
+int tegra_asoc_utils_set_rate(int srate, int mclk_rate, int *mclk_change);
+int tegra_asoc_utils_init(void);
+void tegra_asoc_utils_fini(void);
+
+#endif
+
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
new file mode 100644
index 0000000..01eb9c9
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.c
@@ -0,0 +1,264 @@
+/*
+ * tegra_das.c - Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * 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/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/soc.h>
+#include "tegra_das.h"
+
+#define DRV_NAME "tegra-das"
+
+static struct tegra_das *das;
+
+static inline void tegra_das_write(u32 reg, u32 val)
+{
+ __raw_writel(val, das->regs + reg);
+}
+
+static inline u32 tegra_das_read(u32 reg)
+{
+ return __raw_readl(das->regs + reg);
+}
+
+int tegra_das_connect_dap_to_dac(int dap, int dac)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA_DAS_DAP_CTRL_SEL +
+ (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+ reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
+
+ tegra_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
+
+int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
+ int sdata1rx, int sdata2rx)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA_DAS_DAP_CTRL_SEL +
+ (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+ reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
+ !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
+ !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
+ !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
+
+ tegra_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
+
+int tegra_das_connect_dac_to_dap(int dac, int dap)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+ (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+ reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
+ dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
+ dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
+
+ tegra_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_das_show(struct seq_file *s, void *unused)
+{
+ int i;
+ u32 addr;
+ u32 reg;
+
+ for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
+ addr = TEGRA_DAS_DAP_CTRL_SEL +
+ (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+ reg = tegra_das_read(addr);
+ seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
+ }
+
+ for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
+ addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+ (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+ reg = tegra_das_read(addr);
+ seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
+ i, reg);
+ }
+
+ return 0;
+}
+
+static int tegra_das_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_das_show, inode->i_private);
+}
+
+static const struct file_operations tegra_das_debug_fops = {
+ .open = tegra_das_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void tegra_das_debug_add(struct tegra_das *das)
+{
+ das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
+ snd_soc_debugfs_root, das,
+ &tegra_das_debug_fops);
+}
+
+static void tegra_das_debug_remove(struct tegra_das *das)
+{
+ if (das->debug)
+ debugfs_remove(das->debug);
+}
+#else
+static inline void tegra_das_debug_add(struct tegra_das *das)
+{
+}
+
+static inline void tegra_das_debug_remove(struct tegra_das *das)
+{
+}
+#endif
+
+static int __devinit tegra_das_probe(struct platform_device *pdev)
+{
+ struct resource *res, *region;
+ int ret = 0;
+
+ if (das)
+ return -ENODEV;
+
+ das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL);
+ if (!das) {
+ dev_err(&pdev->dev, "Can't allocate tegra_das\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ das->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_free;
+ }
+
+ region = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (!region) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ das->regs = ioremap(res->start, resource_size(res));
+ if (!das->regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ tegra_das_debug_add(das);
+
+ platform_set_drvdata(pdev, das);
+
+ return 0;
+
+err_release:
+ release_mem_region(res->start, resource_size(res));
+err_free:
+ kfree(das);
+ das = 0;
+exit:
+ return ret;
+}
+
+static int __devexit tegra_das_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ if (!das)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, NULL);
+
+ tegra_das_debug_remove(das);
+
+ iounmap(das->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(das);
+ das = 0;
+
+ return 0;
+}
+
+static struct platform_driver tegra_das_driver = {
+ .probe = tegra_das_probe,
+ .remove = __devexit_p(tegra_das_remove),
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init tegra_das_modinit(void)
+{
+ return platform_driver_register(&tegra_das_driver);
+}
+module_init(tegra_das_modinit);
+
+static void __exit tegra_das_modexit(void)
+{
+ platform_driver_unregister(&tegra_das_driver);
+}
+module_exit(tegra_das_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra DAS driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
new file mode 100644
index 0000000..2c96c7b
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.h
@@ -0,0 +1,135 @@
+/*
+ * tegra_das.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef __TEGRA_DAS_H__
+#define __TEGRA_DAS_H__
+
+/* Register TEGRA_DAS_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_CTRL_SEL 0x00
+#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5
+#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5
+
+/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_SEL_DAC1 0
+#define TEGRA_DAS_DAP_SEL_DAC2 1
+#define TEGRA_DAS_DAP_SEL_DAC3 2
+#define TEGRA_DAS_DAP_SEL_DAP1 16
+#define TEGRA_DAS_DAP_SEL_DAP2 17
+#define TEGRA_DAS_DAP_SEL_DAP3 18
+#define TEGRA_DAS_DAP_SEL_DAP4 19
+#define TEGRA_DAS_DAP_SEL_DAP5 20
+
+/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4
+
+/*
+ * Values for:
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
+ */
+#define TEGRA_DAS_DAC_SEL_DAP1 0
+#define TEGRA_DAS_DAC_SEL_DAP2 1
+#define TEGRA_DAS_DAC_SEL_DAP3 2
+#define TEGRA_DAS_DAC_SEL_DAP4 3
+#define TEGRA_DAS_DAC_SEL_DAP5 4
+
+/*
+ * Names/IDs of the DACs/DAPs.
+ */
+
+#define TEGRA_DAS_DAP_ID_1 0
+#define TEGRA_DAS_DAP_ID_2 1
+#define TEGRA_DAS_DAP_ID_3 2
+#define TEGRA_DAS_DAP_ID_4 3
+#define TEGRA_DAS_DAP_ID_5 4
+
+#define TEGRA_DAS_DAC_ID_1 0
+#define TEGRA_DAS_DAC_ID_2 1
+#define TEGRA_DAS_DAC_ID_3 2
+
+struct tegra_das {
+ struct device *dev;
+ void __iomem *regs;
+ struct dentry *debug;
+};
+
+/*
+ * Terminology:
+ * DAS: Digital audio switch (HW module controlled by this driver)
+ * DAP: Digital audio port (port/pins on Tegra device)
+ * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
+ *
+ * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
+ * DAC, or another DAP. When DAPs are connected, one must be the master and
+ * one the slave. Each DAC allows selection of a specific DAP for input, to
+ * cater for the case where N DAPs are connected to 1 DAC for broadcast
+ * output.
+ *
+ * This driver is dumb; no attempt is made to ensure that a valid routing
+ * configuration is programmed.
+ */
+
+/*
+ * Connect a DAP to to a DAC
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
+ */
+extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
+
+/*
+ * Connect a DAP to to another DAP
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
+ * master: Is this DAP the master (1) or slave (0)
+ * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
+ * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
+ */
+extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
+ int master, int sdata1rx,
+ int sdata2rx);
+
+/*
+ * Connect a DAC's input to a DAP
+ * (DAC outputs are selected by the DAP)
+ * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
+ * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
+ */
+extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
+
+#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
new file mode 100644
index 0000000..1730509
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -0,0 +1,502 @@
+/*
+ * tegra_i2s.c - Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@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/clk.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+
+#define DRV_NAME "tegra-i2s"
+
+static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
+{
+ __raw_writel(val, i2s->regs + reg);
+}
+
+static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
+{
+ return __raw_readl(i2s->regs + reg);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_i2s_show(struct seq_file *s, void *unused)
+{
+#define REG(r) { r, #r }
+ static const struct {
+ int offset;
+ const char *name;
+ } regs[] = {
+ REG(TEGRA_I2S_CTRL),
+ REG(TEGRA_I2S_STATUS),
+ REG(TEGRA_I2S_TIMING),
+ REG(TEGRA_I2S_FIFO_SCR),
+ REG(TEGRA_I2S_PCM_CTRL),
+ REG(TEGRA_I2S_NW_CTRL),
+ REG(TEGRA_I2S_TDM_CTRL),
+ REG(TEGRA_I2S_TDM_TX_RX_CTRL),
+ };
+#undef REG
+
+ struct tegra_i2s *i2s = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ u32 val = tegra_i2s_read(i2s, regs[i].offset);
+ seq_printf(s, "%s = %08x\n", regs[i].name, val);
+ }
+
+ return 0;
+}
+
+static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_i2s_show, inode->i_private);
+}
+
+static const struct file_operations tegra_i2s_debug_fops = {
+ .open = tegra_i2s_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
+{
+ char name[] = DRV_NAME ".0";
+
+ snprintf(name, sizeof(name), DRV_NAME".%1d", id);
+ i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
+ i2s, &tegra_i2s_debug_fops);
+}
+
+static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+ if (i2s->debug)
+ debugfs_remove(i2s->debug);
+}
+#else
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+{
+}
+
+static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+}
+#endif
+
+static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK |
+ TEGRA_I2S_CTRL_LRCK_MASK);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_i2s_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 tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 reg;
+ int ret, sample_size, srate, i2sclock, bitcnt;
+
+ i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
+ sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
+ sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
+ sample_size = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ srate = params_rate(params);
+
+ /* Final "* 2" required by Tegra hardware */
+ i2sclock = srate * params_channels(params) * sample_size * 2;
+
+ ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+ return -EINVAL;
+ reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (i2sclock % (2 * srate))
+ reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
+
+ tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
+
+ tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
+ TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
+ TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+
+ return 0;
+}
+
+static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
+{
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
+ tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
+{
+ i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
+ tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
+{
+ i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
+ tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
+{
+ i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
+ tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (!i2s->clk_refs)
+ clk_enable(i2s->clk_i2s);
+ i2s->clk_refs++;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra_i2s_start_playback(i2s);
+ else
+ tegra_i2s_start_capture(i2s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra_i2s_stop_playback(i2s);
+ else
+ tegra_i2s_stop_capture(i2s);
+ i2s->clk_refs--;
+ if (!i2s->clk_refs)
+ clk_disable(i2s->clk_i2s);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = &i2s->capture_dma_data;
+ dai->playback_dma_data = &i2s->playback_dma_data;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra_i2s_dai_ops = {
+ .set_fmt = tegra_i2s_set_fmt,
+ .hw_params = tegra_i2s_hw_params,
+ .trigger = tegra_i2s_trigger,
+};
+
+struct snd_soc_dai_driver tegra_i2s_dai[] = {
+ {
+ .name = DRV_NAME ".0",
+ .probe = tegra_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra_i2s_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = DRV_NAME ".1",
+ .probe = tegra_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra_i2s_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
+{
+ struct tegra_i2s * i2s;
+ char clk_name[12]; /* tegra-i2s.0 */
+ struct resource *mem, *memregion, *dmareq;
+ int ret;
+
+ if ((pdev->id < 0) ||
+ (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) {
+ dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME: Until a codec driver exists for the tegra DAS, hard-code a
+ * 1:1 mapping between audio controllers and audio ports.
+ */
+ ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id,
+ TEGRA_DAS_DAP_SEL_DAC1 + pdev->id);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAP connection\n");
+ return ret;
+ }
+ ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id,
+ TEGRA_DAS_DAC_SEL_DAP1 + pdev->id);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAC connection\n");
+ return ret;
+ }
+
+ i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL);
+ if (!i2s) {
+ dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
+ i2s->clk_i2s = clk_get_sys(clk_name, NULL);
+ if (IS_ERR_OR_NULL(i2s->clk_i2s)) {
+ pr_err("Can't retrieve i2s clock\n");
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err_free;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmareq) {
+ dev_err(&pdev->dev, "No DMA resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ memregion = request_mem_region(mem->start, resource_size(mem),
+ DRV_NAME);
+ if (!memregion) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_clk_put;
+ }
+
+ i2s->regs = ioremap(mem->start, resource_size(mem));
+ if (!i2s->regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
+ i2s->capture_dma_data.wrap = 4;
+ i2s->capture_dma_data.width = 32;
+ i2s->capture_dma_data.req_sel = dmareq->start;
+
+ i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
+ i2s->playback_dma_data.wrap = 4;
+ i2s->playback_dma_data.width = 32;
+ i2s->playback_dma_data.req_sel = dmareq->start;
+
+ i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
+
+ ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ tegra_i2s_debug_add(i2s, pdev->id);
+
+ return 0;
+
+err_unmap:
+ iounmap(i2s->regs);
+err_release:
+ release_mem_region(mem->start, resource_size(mem));
+err_clk_put:
+ clk_put(i2s->clk_i2s);
+err_free:
+ kfree(i2s);
+exit:
+ return ret;
+}
+
+static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
+{
+ struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ snd_soc_unregister_dai(&pdev->dev);
+
+ tegra_i2s_debug_remove(i2s);
+
+ iounmap(i2s->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(i2s->clk_i2s);
+
+ kfree(i2s);
+
+ return 0;
+}
+
+static struct platform_driver tegra_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_i2s_platform_probe,
+ .remove = __devexit_p(tegra_i2s_platform_remove),
+};
+
+static int __init snd_tegra_i2s_init(void)
+{
+ return platform_driver_register(&tegra_i2s_driver);
+}
+module_init(snd_tegra_i2s_init);
+
+static void __exit snd_tegra_i2s_exit(void)
+{
+ platform_driver_unregister(&tegra_i2s_driver);
+}
+module_exit(snd_tegra_i2s_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra I2S ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
new file mode 100644
index 0000000..2b38a09
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.h
@@ -0,0 +1,165 @@
+/*
+ * tegra_i2s.h - Definitions for Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@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
+ *
+ */
+
+#ifndef __TEGRA_I2S_H__
+#define __TEGRA_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
+
+#define TEGRA_I2S_CTRL 0x00
+#define TEGRA_I2S_STATUS 0x04
+#define TEGRA_I2S_TIMING 0x08
+#define TEGRA_I2S_FIFO_SCR 0x0c
+#define TEGRA_I2S_PCM_CTRL 0x10
+#define TEGRA_I2S_NW_CTRL 0x14
+#define TEGRA_I2S_TDM_CTRL 0x20
+#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24
+#define TEGRA_I2S_FIFO1 0x40
+#define TEGRA_I2S_FIFO2 0x80
+
+/* Fields in TEGRA_I2S_CTRL */
+
+#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30)
+#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29)
+#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28)
+#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27)
+#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26)
+#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25)
+
+#define TEGRA_I2S_LRCK_LEFT_LOW 0
+#define TEGRA_I2S_LRCK_RIGHT_LOW 1
+
+#define TEGRA_I2S_CTRL_LRCK_SHIFT 24
+#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA_I2S_BIT_FORMAT_I2S 0
+#define TEGRA_I2S_BIT_FORMAT_RJM 1
+#define TEGRA_I2S_BIT_FORMAT_LJM 2
+#define TEGRA_I2S_BIT_FORMAT_DSP 3
+
+#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10
+#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+
+#define TEGRA_I2S_BIT_SIZE_16 0
+#define TEGRA_I2S_BIT_SIZE_20 1
+#define TEGRA_I2S_BIT_SIZE_24 2
+#define TEGRA_I2S_BIT_SIZE_32 3
+
+#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8
+#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+
+#define TEGRA_I2S_FIFO_16_LSB 0
+#define TEGRA_I2S_FIFO_20_LSB 1
+#define TEGRA_I2S_FIFO_24_LSB 2
+#define TEGRA_I2S_FIFO_32 3
+#define TEGRA_I2S_FIFO_PACKED 7
+
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+
+#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3)
+#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2)
+#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1)
+#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0)
+
+/* Fields in TEGRA_I2S_STATUS */
+
+#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31)
+#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30)
+#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29)
+#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28)
+#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3)
+#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2)
+#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1)
+#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0)
+
+/* Fields in TEGRA_I2S_TIMING */
+
+#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA_I2S_FIFO_SCR */
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
+#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
+#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8)
+
+#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0
+#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
+#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
+#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+
+struct tegra_i2s {
+ struct clk *clk_i2s;
+ int clk_refs;
+ struct tegra_pcm_dma_params capture_dma_data;
+ struct tegra_pcm_dma_params playback_dma_data;
+ void __iomem *regs;
+ struct dentry *debug;
+ u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
new file mode 100644
index 0000000..663ea9f
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -0,0 +1,401 @@
+/*
+ * tegra_pcm.c - Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@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"
+
+static const struct snd_pcm_hardware tegra_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 = 2,
+ .channels_max = 2,
+ .period_bytes_min = 1024,
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 8,
+ .buffer_bytes_max = PAGE_SIZE * 8,
+ .fifo_size = 4,
+};
+
+static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
+{
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct tegra_dma_req *dma_req;
+ unsigned long addr;
+
+ dma_req = &prtd->dma_req[prtd->dma_req_idx];
+ prtd->dma_req_idx = 1 - prtd->dma_req_idx;
+
+ addr = buf->addr + prtd->dma_pos;
+ prtd->dma_pos += dma_req->size;
+ if (prtd->dma_pos >= prtd->dma_pos_end)
+ prtd->dma_pos = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_req->source_addr = addr;
+ else
+ dma_req->dest_addr = addr;
+
+ tegra_dma_enqueue_req(prtd->dma_chan, dma_req);
+}
+
+static void dma_complete_callback(struct tegra_dma_req *req)
+{
+ struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ spin_lock(&prtd->lock);
+
+ if (!prtd->running) {
+ spin_unlock(&prtd->lock);
+ return;
+ }
+
+ if (++prtd->period_index >= runtime->periods)
+ prtd->period_index = 0;
+
+ tegra_pcm_queue_dma(prtd);
+
+ spin_unlock(&prtd->lock);
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+ struct tegra_pcm_dma_params * dmap)
+{
+ req->complete = dma_complete_callback;
+ req->to_memory = false;
+ req->dest_addr = dmap->addr;
+ req->dest_wrap = dmap->wrap;
+ req->source_bus_width = 32;
+ req->source_wrap = 0;
+ req->dest_bus_width = dmap->width;
+ req->req_sel = dmap->req_sel;
+}
+
+static void setup_dma_rx_request(struct tegra_dma_req *req,
+ struct tegra_pcm_dma_params * dmap)
+{
+ req->complete = dma_complete_callback;
+ req->to_memory = true;
+ req->source_addr = dmap->addr;
+ req->dest_wrap = 0;
+ req->source_bus_width = dmap->width;
+ req->source_wrap = dmap->wrap;
+ req->dest_bus_width = 32;
+ req->req_sel = dmap->req_sel;
+}
+
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
+{
+ 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;
+
+ prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ prtd->substream = substream;
+
+ spin_lock_init(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ setup_dma_tx_request(&prtd->dma_req[0], dmap);
+ setup_dma_tx_request(&prtd->dma_req[1], dmap);
+ } else {
+ dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ setup_dma_rx_request(&prtd->dma_req[0], dmap);
+ setup_dma_rx_request(&prtd->dma_req[1], dmap);
+ }
+
+ prtd->dma_req[0].dev = prtd;
+ prtd->dma_req[1].dev = prtd;
+
+ prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+ if (IS_ERR(prtd->dma_chan)) {
+ ret = PTR_ERR(prtd->dma_chan);
+ goto err;
+ }
+
+ /* Set HW params now that initialization is complete */
+ snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ if (prtd->dma_chan) {
+ tegra_dma_free_channel(prtd->dma_chan);
+ }
+
+ kfree(prtd);
+
+ return ret;
+}
+
+static int tegra_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct tegra_runtime_data *prtd = runtime->private_data;
+
+ tegra_dma_free_channel(prtd->dma_chan);
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static 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;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ prtd->dma_req[0].size = params_period_bytes(params);
+ prtd->dma_req[1].size = prtd->dma_req[0].size;
+
+ return 0;
+}
+
+static 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)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct tegra_runtime_data *prtd = runtime->private_data;
+ unsigned long flags;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->dma_pos = 0;
+ prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
+ prtd->period_index = 0;
+ prtd->dma_req_idx = 0;
+ /* Fall-through */
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ 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);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->running = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
+ tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static 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;
+
+ return prtd->period_index * runtime->period_size;
+}
+
+
+static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops tegra_pcm_ops = {
+ .open = tegra_pcm_open,
+ .close = tegra_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = tegra_pcm_hw_params,
+ .hw_free = tegra_pcm_hw_free,
+ .trigger = tegra_pcm_trigger,
+ .pointer = tegra_pcm_pointer,
+ .mmap = tegra_pcm_mmap,
+};
+
+static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ 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);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+ if (!buf->area)
+ return;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+}
+
+static u64 tegra_dma_mask = DMA_BIT_MASK(32);
+
+static int tegra_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &tegra_dma_mask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->driver->playback.channels_min) {
+ ret = tegra_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto err;
+ }
+
+ if (dai->driver->capture.channels_min) {
+ ret = tegra_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto err_free_play;
+ }
+
+ return 0;
+
+err_free_play:
+ tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+err:
+ return ret;
+}
+
+static 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);
+}
+
+struct snd_soc_platform_driver tegra_pcm_platform = {
+ .ops = &tegra_pcm_ops,
+ .pcm_new = tegra_pcm_new,
+ .pcm_free = tegra_pcm_free,
+};
+
+static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
+}
+
+static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver tegra_pcm_driver = {
+ .driver = {
+ .name = "tegra-pcm-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_pcm_platform_probe,
+ .remove = __devexit_p(tegra_pcm_platform_remove),
+};
+
+static int __init snd_tegra_pcm_init(void)
+{
+ return platform_driver_register(&tegra_pcm_driver);
+}
+module_init(snd_tegra_pcm_init);
+
+static void __exit snd_tegra_pcm_exit(void)
+{
+ platform_driver_unregister(&tegra_pcm_driver);
+}
+module_exit(snd_tegra_pcm_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PCM ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
new file mode 100644
index 0000000..dbb9033
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -0,0 +1,55 @@
+/*
+ * tegra_pcm.h - Definitions for Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@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
+ *
+ */
+
+#ifndef __TEGRA_PCM_H__
+#define __TEGRA_PCM_H__
+
+#include <mach/dma.h>
+
+struct tegra_pcm_dma_params {
+ unsigned long addr;
+ unsigned long wrap;
+ unsigned long width;
+ unsigned long req_sel;
+};
+
+struct tegra_runtime_data {
+ struct snd_pcm_substream *substream;
+ spinlock_t lock;
+ int running;
+ int dma_pos;
+ int dma_pos_end;
+ int period_index;
+ int dma_req_idx;
+ struct tegra_dma_req dma_req[2];
+ struct tegra_dma_channel *dma_chan;
+};
+
+#endif