summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVijay Mali <vmali@nvidia.com>2010-02-01 20:19:16 +0530
committerGary King <gking@nvidia.com>2010-02-05 19:37:16 -0800
commit987ff3f1e54a39614d331943ec19764306fc154f (patch)
tree0db3e59a42a432906c24c26034295d45857276e1
parent17d14e6752a0eb7c53e8fa35f53af0cc338a0f92 (diff)
[ALSA] Tegra SoC support
Add audio SoC support for the NVIDIA Tegra SoC and the Harmony and Whistler development platforms. - single-link rpc dai driver to audio processor - basic machine driver for harmony and whistler platforms supports basic playback and record. Change-Id: Ifad3b503f1bfa0000440c9473f0a932f406a7731
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/tegra/Kconfig33
-rw-r--r--sound/soc/tegra/Makefile4
-rw-r--r--sound/soc/tegra/tegra_harmony.c181
-rw-r--r--sound/soc/tegra/tegra_i2s.c107
-rw-r--r--sound/soc/tegra/tegra_pcm_rpc.c850
-rw-r--r--sound/soc/tegra/tegra_sndfx.h541
-rw-r--r--sound/soc/tegra/tegra_transport.c945
-rw-r--r--sound/soc/tegra/tegra_transport.h426
-rw-r--r--sound/soc/tegra/tegra_whistler.c180
11 files changed, 3269 insertions, 0 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index ef025c66cc66..dd1458462870 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -32,6 +32,7 @@ source "sound/soc/omap/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
+source "sound/soc/tegra/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 86a9b1f5b0f3..dfbeab6ffcd4 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += sh/
+obj-$(CONFIG_SND_SOC) += tegra/
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
new file mode 100644
index 000000000000..e41122bd05dd
--- /dev/null
+++ b/sound/soc/tegra/Kconfig
@@ -0,0 +1,33 @@
+config TEGRA_SND_SOC
+ tristate "Soc alsa driver for Tegra"
+ depends on ARCH_TEGRA
+ select TEGRA_PCM
+ select TEGRA_I2S
+ help
+ Say Y to support ALSA SoC for TEGRA
+
+config TEGRA_PCM
+ depends on TEGRA_SND_SOC
+ tristate "Tegra pcm callbacks"
+
+config TEGRA_I2S
+ depends on TEGRA_SND_SOC
+ tristate "Tegra I2S"
+
+config TEGRA_SND_WHISTLER
+ tristate "SoC alsa driver for whistler"
+ depends on TEGRA_SND_SOC && TEGRA_ODM_WHISTLER
+ select SND_SOC_WM8753
+
+config SND_SOC_WM8753
+ depends on TEGRA_SND_WHISTLER
+ tristate "audio codec WM8753"
+
+config TEGRA_SND_HARMONY
+ tristate "SoC alsa driver for harmony"
+ depends on TEGRA_SND_SOC && TEGRA_ODM_HARMONY
+ select SND_SOC_WM8903
+
+config SND_SOC_WM8903
+ depends on TEGRA_SND_HARMONY
+ tristate "audio codec WM8903"
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
new file mode 100644
index 000000000000..53665b7552df
--- /dev/null
+++ b/sound/soc/tegra/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_TEGRA_PCM) += tegra_pcm_rpc.o
+obj-$(CONFIG_TEGRA_I2S) += tegra_i2s.o tegra_transport.o
+obj-$(CONFIG_TEGRA_SND_WHISTLER) += tegra_whistler.o
+obj-$(CONFIG_TEGRA_SND_HARMONY) += tegra_harmony.o
diff --git a/sound/soc/tegra/tegra_harmony.c b/sound/soc/tegra/tegra_harmony.c
new file mode 100644
index 000000000000..8e6bc97bf5e8
--- /dev/null
+++ b/sound/soc/tegra/tegra_harmony.c
@@ -0,0 +1,181 @@
+/*
+ * sound/soc/tegra/tegra_harmony.c
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/clk.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <linux/io.h>
+#include "nvodm_query_discovery.h"
+
+#include "../codecs/wm8903.h"
+#include "../codecs/wm8753.h"
+
+#include "tegra_transport.h"
+
+static struct platform_device *tegra_snd_device;
+NvU64 codec_guid;
+
+#define NVODM_CODEC_MAX_CLOCKS 3
+
+static unsigned int clock_frequencies[NVODM_CODEC_MAX_CLOCKS];
+
+static int set_clock_source_on_codec(NvU64 codec_guid,int IsEnable)
+{
+ const NvOdmPeripheralConnectivity *p_connectivity = NULL;
+ unsigned int clock_instances[NVODM_CODEC_MAX_CLOCKS];
+ unsigned int num_clocks;
+
+ p_connectivity = NvOdmPeripheralGetGuid(codec_guid);
+ if (p_connectivity == NULL)
+ return NV_FALSE;
+
+ if (IsEnable) {
+ if (!NvOdmExternalClockConfig(codec_guid, NV_FALSE,
+ clock_instances,
+ clock_frequencies, &num_clocks))
+ return NV_FALSE;
+ } else {
+ if (!NvOdmExternalClockConfig(codec_guid,
+ NV_TRUE,
+ clock_instances,
+ clock_frequencies,
+ &num_clocks));
+ return NV_FALSE;
+ }
+ return NV_TRUE;
+}
+
+static int tegra_harmony_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ int err;
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S | \
+ SND_SOC_DAIFMT_NB_IF | \
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai,
+ 0,
+ clock_frequencies[0]*1000,
+ SND_SOC_CLOCK_IN);
+
+ if (err<0) {
+ return err;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops tegra_harmony_hifi_ops = {
+ .hw_params = tegra_harmony_hifi_hw_params,
+};
+
+static int tegra_wm8903_init(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+extern struct snd_soc_dai tegra_i2s_rpc_dai;
+extern struct snd_soc_platform tegra_soc_platform;
+
+static struct snd_soc_dai_link tegra_harmony_dai = {
+ /* Hifi Playback - for similatious use with voice below */
+ .name = "WM8903",
+ .stream_name = "WM8903 HiFi",
+ .cpu_dai = &tegra_i2s_rpc_dai,
+ .codec_dai = &wm8903_dai,
+ .init = tegra_wm8903_init,
+ .ops = &tegra_harmony_hifi_ops,
+};
+
+static struct snd_soc_card tegra_harmony = {
+ .name = "tegra",
+ .platform = &tegra_soc_platform,
+ .dai_link = &tegra_harmony_dai,
+ .num_links = 1,
+};
+
+struct harmony_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+static struct snd_soc_device tegra_harmony_snd_devdata = {
+ .card = &tegra_harmony,
+ .codec_dev = &soc_codec_dev_wm8903,
+};
+
+static int __init tegra_soc_init(void)
+{
+ int ret;
+ tegra_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!tegra_snd_device)
+ return -ENOMEM;
+
+ codec_guid = NV_ODM_GUID('w','o','l','f','8','9','0','3');
+ platform_set_drvdata(tegra_snd_device, &tegra_harmony_snd_devdata);
+ tegra_harmony_snd_devdata.dev = &tegra_snd_device->dev;
+
+ ret = platform_device_add(tegra_snd_device);
+ if (ret) {
+ snd_printk(KERN_ERR "tegra audio device could not be added \n");
+ platform_device_put(tegra_snd_device);
+ return ret;
+ }
+
+ set_clock_source_on_codec(codec_guid,NV_TRUE);
+ if (ret != 0)
+ platform_device_unregister(tegra_snd_device);
+
+ return ret;
+}
+
+static void __exit tegra_soc_exit(void)
+{
+ set_clock_source_on_codec(codec_guid,0);
+ platform_device_unregister(tegra_snd_device);
+}
+
+module_init(tegra_soc_init);
+module_exit(tegra_soc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Tegra SoC Sound");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
new file mode 100644
index 000000000000..6e672347ae5e
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -0,0 +1,107 @@
+/*
+ * sound/soc/tegra/tegra_i2s.c
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "mach/nvrm_linux.h"
+#include "nvrm_memmgr.h"
+#include "nvassert.h"
+
+#include "tegra_transport.h"
+
+extern struct snd_soc_dai tegra_i2s_rpc_dai;
+
+static int tegra_i2s_rpc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ switch (params_rate(params)) {
+ case 8000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tegra_i2s_rpc_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#define TEGRA_I2S_RATES (SNDRV_PCM_RATE_8000_96000)
+
+struct snd_soc_dai tegra_i2s_rpc_dai = {
+ .name = "tegra-i2s-rpc",
+ .id = 0,
+ .probe = tegra_i2s_rpc_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TEGRA_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TEGRA_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE,},
+ .ops = {
+ .hw_params = tegra_i2s_rpc_hw_params,
+ },
+};
+EXPORT_SYMBOL_GPL(tegra_i2s_rpc_dai);
+
+static int __init tegra_i2s_rpc_init(void)
+{
+ return snd_soc_register_dai(&tegra_i2s_rpc_dai);
+}
+module_init(tegra_i2s_rpc_init);
+
+static void __exit tegra_i2s_rpc_exit(void)
+{
+ snd_soc_unregister_dai(&tegra_i2s_rpc_dai);
+}
+module_exit(tegra_i2s_rpc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Tegra I2S RPC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_pcm_rpc.c b/sound/soc/tegra/tegra_pcm_rpc.c
new file mode 100644
index 000000000000..a913bbf9c12c
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm_rpc.c
@@ -0,0 +1,850 @@
+/*
+ * sound/soc/tegra/tegra_pcm_rpc.c
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "tegra_transport.h"
+static struct tegra_audio_data* tegra_snd_cx = NULL;
+
+static const struct snd_pcm_hardware tegra_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 16*1024,
+ .period_bytes_min = 4*1024,
+ .period_bytes_max = 4*1024,
+ .periods_min = 4,
+ .periods_max = 4,
+ .fifo_size = 4,
+};
+
+
+static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ 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_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int play_thread( void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd = substream->runtime->private_data;
+ NvError e;
+ int size;
+ int offset = 0;
+ int period_offset = 0;
+ int rtbuffersize = 0;
+ int buffer_to_prime = 0, buffer_in_queue = 0;
+ NvAudioFxBufferDescriptor abd;
+ NvAudioFxState state = NVALSA_INVALID_STATE;
+
+ wait_for_completion(&prtd->play_comp);
+
+ rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size);
+ buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE);
+
+ while (1) {
+ switch (prtd->state) {
+ case SNDRV_PCM_TRIGGER_START:
+ state = NvAudioFxState_Run;
+ tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdoutpath->Stream,
+ NvAudioFxProperty_State,
+ sizeof(NvAudioFxState),
+ &state);
+ prtd->state = NVALSA_INVALID_STATE;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ while (buffer_in_queue > 0) {
+ e = NvOsSemaphoreWaitTimeout(
+ prtd->play_sema, prtd->timeout);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "Sema Wait Fail\n");
+ }
+ buffer_in_queue--;
+ }
+ state = NvAudioFxState_Stop;
+ tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdoutpath->Stream,
+ NvAudioFxProperty_State,
+ sizeof(NvAudioFxState),
+ &state);
+ prtd->state = NVALSA_INVALID_STATE;
+ break;
+ default:
+ ;
+ }
+
+ if (kthread_should_stop())
+ goto EXIT;
+
+ memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor));
+
+ if(rtbuffersize > (offset + TEGRA_DEFAULT_BUFFER_SIZE)) {
+ size = TEGRA_DEFAULT_BUFFER_SIZE;
+ } else {
+ size = rtbuffersize - offset;
+ }
+
+ abd.hMixBuffer = prtd->mixer_buffer;
+ abd.Offset = offset;
+ abd.Size = size;
+ abd.Format.FormatTag = 1;
+ abd.Format.SampleRate = runtime->rate;
+ abd.Format.BitsPerSample = runtime->sample_bits;
+ abd.Format.Channels = runtime->channels;
+ abd.Format.ChannelMask = 0;
+
+ e = tegra_snd_cx->xrt_fxn.StreamAddBuffer(
+ (NvAudioFxStreamHandle)prtd->stdoutpath->Stream, &abd);
+ buffer_in_queue++;
+ offset += size;
+ if (offset >= rtbuffersize)
+ offset =0;
+
+ if (buffer_to_prime == buffer_in_queue) {
+ e = NvOsSemaphoreWaitTimeout(prtd->play_sema,
+ prtd->timeout);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "sema wait Fail\n");
+ return -ETIMEDOUT;
+ }
+
+ buffer_in_queue--;
+
+ prtd->cur_pos += bytes_to_frames(runtime,
+ TEGRA_DEFAULT_BUFFER_SIZE);
+
+ if (prtd->cur_pos < prtd->last_pos) {
+ period_offset = (runtime->buffer_size +
+ prtd->cur_pos) - prtd->last_pos;
+
+ } else {
+ period_offset = prtd->cur_pos - prtd->last_pos;
+ }
+
+ if (period_offset >= runtime->period_size) {
+ prtd->last_pos = prtd->cur_pos;
+ snd_pcm_period_elapsed(substream);
+ }
+
+ if (prtd->cur_pos >= runtime->buffer_size) {
+ prtd->cur_pos -= runtime->buffer_size;
+ }
+
+ }
+ }
+ snd_printk(KERN_ERR "play_thread: return -EFAULT \n");
+ return -EFAULT;
+EXIT:
+ NvOsSemaphoreWaitTimeout(prtd->play_sema, prtd->timeout);
+ return 0;
+}
+
+static int rec_thread( void *arg )
+{
+ struct snd_pcm_substream *substream = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd = substream->runtime->private_data;
+ NvError e;
+ int size;
+ int offset = 0;
+ int period_offset = 0;
+ int rtbuffersize = 0;
+ int buffer_to_prime = 0, buffer_in_queue = 0;
+ NvAudioFxBufferDescriptor abd;
+ NvAudioFxState state = NVALSA_INVALID_STATE;
+ NvAudioFxPinFormatDescriptor pin_format;
+
+ wait_for_completion(&prtd->rec_comp);
+ rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size);
+ buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE);
+
+ while (1) {
+ switch (prtd->state) {
+ case SNDRV_PCM_TRIGGER_START:
+ pin_format.Format.FormatTag = 1;
+ pin_format.Format.SampleRate = runtime->rate;
+ pin_format.Format.BitsPerSample = runtime->sample_bits;
+ pin_format.Format.Channels = runtime->channels;
+ pin_format.Format.ChannelMask = 0;
+ pin_format.Pin = NvAudioFxSourcePin;
+
+ e = tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdinpath->Convert,
+ NvAudioFxPinProperty_Format,
+ sizeof(NvAudioFxPinFormatDescriptor),
+ &pin_format);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR"set_property failed!\n");
+ }
+
+ e = tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdinpath->Src,
+ NvAudioFxProperty_SampleRate,
+ sizeof(NvS32),
+ &pin_format.Format.SampleRate);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "set_property failed!\n");
+ }
+
+ state = NvAudioFxState_Run;
+ tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdinpath->Stream,
+ NvAudioFxProperty_State,
+ sizeof(NvAudioFxState),
+ &state);
+ prtd->state = NVALSA_INVALID_STATE;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ while (buffer_in_queue) {
+ e = NvOsSemaphoreWaitTimeout(
+ prtd->rec_sema, prtd->timeout);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "Sema Wait Fail\n");
+ }
+ buffer_in_queue--;
+ }
+
+ state = NvAudioFxState_Stop;
+ tegra_snd_cx->xrt_fxn.SetProperty(
+ prtd->stdinpath->Stream,
+ NvAudioFxProperty_State,
+ sizeof(NvAudioFxState),
+ &state);
+ prtd->state = NVALSA_INVALID_STATE;
+ goto EXIT;
+ default:
+ ;
+ }
+
+ memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor));
+
+ if(rtbuffersize > (offset + TEGRA_DEFAULT_BUFFER_SIZE)) {
+ size = TEGRA_DEFAULT_BUFFER_SIZE;
+ } else {
+ size = rtbuffersize - offset;
+ }
+
+ abd.hMixBuffer = prtd->mixer_buffer;
+ abd.Offset = offset;
+ abd.Size = size;
+ abd.Format.FormatTag = 1;
+ abd.Format.SampleRate = runtime->rate;
+ abd.Format.BitsPerSample = runtime->sample_bits;
+ abd.Format.Channels = runtime->channels;
+ abd.Format.ChannelMask = 0;
+
+ e = tegra_snd_cx->xrt_fxn.StreamAddBuffer(
+ (NvAudioFxStreamHandle)prtd->stdinpath->Stream, &abd);
+ buffer_in_queue++;
+ offset += size;
+
+ if (offset >= rtbuffersize)
+ offset =0;
+
+ if (buffer_to_prime == buffer_in_queue) {
+ e = NvOsSemaphoreWaitTimeout(prtd->rec_sema,
+ prtd->timeout);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "sema wait Fail\n");
+ return -ETIMEDOUT;
+ }
+
+ buffer_in_queue--;
+
+ prtd->cur_pos += bytes_to_frames(
+ runtime,
+ TEGRA_DEFAULT_BUFFER_SIZE);
+
+ if (prtd->cur_pos < prtd->last_pos) {
+ period_offset = (runtime->buffer_size +
+ prtd->cur_pos) - prtd->last_pos;
+ } else {
+ period_offset = prtd->cur_pos - prtd->last_pos;
+ }
+
+ if (period_offset >= runtime->period_size) {
+ prtd->last_pos = prtd->cur_pos;
+ snd_pcm_period_elapsed(substream);
+ }
+
+ if (prtd->cur_pos >= runtime->buffer_size) {
+ prtd->cur_pos -= runtime->buffer_size;
+ }
+ }
+ }
+ snd_printk(KERN_ERR "rec_thread: return -EFAULT \n");
+ return -EFAULT;
+EXIT:
+ while(!kthread_should_stop()){
+ }
+
+ return 0;
+}
+
+static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd = substream->runtime->private_data;
+ int size;
+ int ret = 0;
+ int state = prtd->state;
+
+ prtd->state = cmd;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ size=frames_to_bytes(runtime, runtime->period_size);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->timeout = PLAY_TIMEOUT;
+ if(state == NVALSA_INVALID_STATE)
+ complete(&prtd->play_comp);
+ else
+ NvOsSemaphoreSignal(prtd->play_sema);
+ } else {
+ prtd->timeout = REC_TIMEOUT;
+ if(state == NVALSA_INVALID_STATE)
+ complete(&prtd->rec_comp);
+ else
+ NvOsSemaphoreSignal(prtd->rec_sema);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->timeout = PLAY_TIMEOUT;
+ }else{
+ prtd->timeout = REC_TIMEOUT;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->timeout = NV_WAIT_INFINITE;
+ break;
+ default:
+ prtd->state = state;
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static snd_pcm_uframes_t
+tegra_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd = runtime->private_data;
+ int size;
+
+ size = prtd->last_pos;
+ if (size >= runtime->buffer_size) {
+ prtd->last_pos = size - runtime->buffer_size;
+ size = 0;
+ }
+
+ return (size);
+}
+
+static int init_mixer(struct snd_pcm_substream *substream)
+{
+ NvError e = NvSuccess;
+ int ret = 0;
+
+ if (!tegra_snd_cx->mixer_handle) {
+ mutex_lock(&tegra_snd_cx->lock);
+ e = tegra_transport_init(&tegra_snd_cx->xrt_fxn);
+ mutex_unlock(&tegra_snd_cx->lock);
+
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_init failed \n");
+ return -EFAULT;
+ }
+
+ tegra_snd_cx->mixer_handle =
+ tegra_snd_cx->xrt_fxn.MixerOpen();
+
+ if (!tegra_snd_cx->mixer_handle) {
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ e = tegra_audiofx_createfx(tegra_snd_cx);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_audiofx_createfx failed \n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ tegra_snd_cx->mixer_buffer[0] =
+ tegra_snd_cx->xrt_fxn.MixerMapBuffer(
+ tegra_snd_cx->mixer_handle,
+ NvRmMemGetId(tegra_snd_cx->mem_handle[0]),
+ 0,
+ tegra_snd_cx->mapped_buf_size);
+
+ if (!tegra_snd_cx->mixer_buffer[0]) {
+ snd_printk(KERN_ERR"TransportMixerMapBuffer failed!\n");
+ }
+
+ tegra_snd_cx->mixer_buffer[1] =
+ tegra_snd_cx->xrt_fxn.MixerMapBuffer(
+ tegra_snd_cx->mixer_handle,
+ NvRmMemGetId(tegra_snd_cx->mem_handle[1]),
+ 0,
+ tegra_snd_cx->mapped_buf_size);
+
+ if (!tegra_snd_cx->mixer_buffer[1]) {
+ snd_printk(KERN_ERR"TransportMixerMapBuffer failed!\n");
+ }
+ }
+
+ return 0;
+fail:
+ snd_printk(KERN_ERR "init mixer failed \n");
+ if (tegra_snd_cx->mixer_handle) {
+ tegra_audiofx_destroyfx(tegra_snd_cx);
+
+ if (tegra_snd_cx->mixer_handle) {
+ tegra_snd_cx->xrt_fxn.MixerClose(
+ tegra_snd_cx->mixer_handle);
+ }
+ }
+ mutex_lock(&tegra_snd_cx->lock);
+ tegra_transport_deinit();
+ mutex_unlock(&tegra_snd_cx->lock);
+
+ return ret;
+}
+
+static int pcm_common_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd = runtime->private_data;
+ NvAudioFxMessage message;
+ NvError e;
+
+ if (!prtd)
+ snd_printk(KERN_ERR "pcm_close called with prtd = NULL\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (prtd->play_sema)
+ NvOsSemaphoreSignal(prtd->play_sema);
+
+ if (prtd->play_thread)
+ kthread_stop(prtd->play_thread);
+
+ if (prtd->play_sema){
+ NvOsSemaphoreDestroy(prtd->play_sema);
+ prtd->play_sema = NULL;
+ }
+
+ } else {
+
+ if (prtd->rec_sema)
+ NvOsSemaphoreSignal(prtd->rec_sema);
+
+ if (prtd->rec_thread)
+ kthread_stop(prtd->rec_thread);
+
+ if (prtd->rec_sema){
+ NvOsSemaphoreDestroy(prtd->rec_sema);
+ prtd->rec_sema = NULL;
+ }
+ }
+
+ if (tegra_snd_cx->m_FxNotifier.Event & NvAudioFxEventBufferDone) {
+
+ memset(&message, 0, sizeof(NvAudioFxMessage));
+ message.Event = NvAudioFxEventBufferDone;
+ message.pContext = NULL;
+ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream;
+ else
+ message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream;
+
+ e = tegra_snd_cx->xrt_fxn.SetProperty(
+ (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier,
+ NvAudioFxIoProperty_RemoveEvent,
+ sizeof(NvAudioFxMessage),
+ &message);
+
+ tegra_snd_cx->m_FxNotifier.Event &= ~(NvAudioFxEventBufferDone);
+ }
+
+ if (prtd->stdoutpath) {
+ tegra_audiofx_destroy_output(prtd->stdoutpath);
+ kfree(prtd->stdoutpath);
+ }
+
+ if (prtd->stdinpath) {
+ tegra_audiofx_destroy_input(prtd->stdinpath);
+ kfree(prtd->stdinpath);
+ }
+
+ if (prtd)
+ kfree(prtd);
+
+ return 0;
+}
+
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_runtime_data *prtd;
+ int ret = 0;
+ NvError e = NvSuccess;
+ NvAudioFxMessage message;
+
+ prtd = kzalloc(sizeof(struct pcm_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+ spin_lock_init(&prtd->lock);
+ prtd->timeout = INIT_TIMEOUT;
+ prtd->stdoutpath = 0;
+ prtd->stdinpath = 0;
+ prtd->state = NVALSA_INVALID_STATE;
+ prtd->stream = substream->stream;
+
+ ret = init_mixer(substream);
+ if (ret)
+ goto fail;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
+ prtd->mixer_buffer = tegra_snd_cx->mixer_buffer[0];
+ prtd->stdoutpath = (StandardPath*)kzalloc(sizeof(StandardPath),
+ GFP_KERNEL);
+ if (prtd->stdoutpath == NULL) {
+ snd_printk(KERN_ERR "pcm_open kzalloc failed \n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ e = tegra_audiofx_create_output(tegra_snd_cx->m_hRm,
+ tegra_snd_cx->mixer_handle,
+ prtd->stdoutpath);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "audiofx_create_output failed \n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ memset(&message, 0, sizeof(NvAudioFxMessage));
+ message.Event = NvAudioFxEventBufferDone;
+ message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream;
+ message.pContext = prtd;
+
+ e = tegra_snd_cx->xrt_fxn.SetProperty(
+ (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier,
+ NvAudioFxIoProperty_AddEvent,
+ sizeof(NvAudioFxMessage),
+ &message);
+
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "TransportSetProperty failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone);
+ e = NvOsSemaphoreCreate(&prtd->play_sema, 0);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "play_sema creation failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ init_completion(&prtd->play_comp);
+ prtd->play_thread = kthread_run(play_thread,
+ substream,
+ "%sthread",
+ "play");
+ if (IS_ERR(prtd->play_thread)) {
+ snd_printk(KERN_ERR "KTHREAD RUN FAIL\n");
+ ret = PTR_ERR(prtd->play_thread);
+ goto fail;
+ }
+ } else {
+ prtd->mixer_buffer = tegra_snd_cx->mixer_buffer[1];
+ prtd->stdinpath = (StandardPath*)kzalloc(sizeof(StandardPath),
+ GFP_KERNEL);
+ if (prtd->stdinpath == NULL) {
+ snd_printk(KERN_ERR "pcm_open kzalloc failed \n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ e = tegra_audiofx_create_input(tegra_snd_cx->m_hRm,
+ tegra_snd_cx->mixer_handle,
+ prtd->stdinpath,
+ NvAudioInputSelect_Record);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "audiofx_create_input failed \n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ memset(&message, 0, sizeof(NvAudioFxMessage));
+ message.Event = NvAudioFxEventBufferDone;
+ message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream;
+ message.pContext = prtd;
+
+ e = tegra_snd_cx->xrt_fxn.SetProperty(
+ (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier,
+ NvAudioFxIoProperty_AddEvent,
+ sizeof(NvAudioFxMessage),
+ &message);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "TransportSetProperty failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone);
+
+ e = NvOsSemaphoreCreate(&prtd->rec_sema, 0);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "rec_sema creation failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ init_completion(&prtd->rec_comp);
+ prtd->rec_thread = kthread_run(rec_thread,
+ substream,
+ "%sthread",
+ "rec" );
+ if (IS_ERR(prtd->rec_thread)) {
+ snd_printk(KERN_ERR "Kthread Run Fail\n");
+ ret = PTR_ERR(prtd->rec_thread);
+ goto fail;
+ }
+ }
+ return ret;
+fail:
+ snd_printk(KERN_ERR "tegra_pcm_open - failed \n");
+ pcm_common_close(substream);
+ return ret;
+}
+
+static int tegra_pcm_close(struct snd_pcm_substream *substream)
+{
+ pcm_common_close(substream);
+ return 0;
+}
+
+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,
+ .prepare = tegra_pcm_prepare,
+ .trigger = tegra_pcm_trigger,
+ .pointer = tegra_pcm_pointer,
+};
+
+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;
+ void *virt_buf_ptr = NULL;
+ NvRmPhysAddr phy_address;
+ int ret = 0;
+ NvError e;
+ e = NvRmMemHandleCreate(tegra_snd_cx->m_hRm,
+ &tegra_snd_cx->mem_handle[stream],
+ size);
+
+ if (e == NvSuccess) {
+ e = NvRmMemAlloc(tegra_snd_cx->mem_handle[stream],
+ NULL,
+ 0,
+ 0x4,
+ NvOsMemAttribute_Uncached);
+ }
+
+ if (e == NvSuccess) {
+ phy_address = (NvU32)(NvRmMemPin(tegra_snd_cx->mem_handle[stream]));
+ }
+
+ if (e != NvSuccess) {
+ NvRmMemHandleFree(tegra_snd_cx->mem_handle[stream]);
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ e = NvRmMemMap(tegra_snd_cx->mem_handle[stream],
+ 0,
+ size,
+ NVOS_MEM_READ_WRITE,
+ (void**)&virt_buf_ptr);
+
+ if (e != NvSuccess) {
+ NvRmMemHandleFree(tegra_snd_cx->mem_handle[stream]);
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ tegra_snd_cx->mapped_buf_size = size;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = virt_buf_ptr;
+ buf->bytes = size;
+ buf->addr = phy_address;
+end:
+ return ret;
+}
+
+static void DestroyMemoryHandle(NvRmMemHandle hMemHandle)
+{
+ if (hMemHandle != NULL) {
+ NvRmMemUnpin(hMemHandle);
+ NvRmMemHandleFree(hMemHandle);
+ }
+}
+
+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 (tegra_snd_cx->mixer_buffer[stream])
+ tegra_snd_cx->xrt_fxn.MixerUnmapBuffer(
+ tegra_snd_cx->mixer_buffer[stream]);
+
+ NvRmMemUnmap(tegra_snd_cx->mem_handle[stream],buf->area,buf->bytes);
+ DestroyMemoryHandle(tegra_snd_cx->mem_handle[stream]);
+}
+
+static void tegra_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ buf = &substream->dma_buffer;
+ if (!buf) {
+ continue;
+ }
+ tegra_pcm_deallocate_dma_buffer(pcm ,stream);
+ }
+}
+
+
+static int tegra_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ tegra_snd_cx = kzalloc(sizeof(struct tegra_audio_data),
+ GFP_KERNEL);
+ if (tegra_snd_cx == NULL)
+ return -ENOMEM;
+
+ tegra_snd_cx->m_hRm = s_hRmGlobal;
+ mutex_init(&tegra_snd_cx->lock);
+
+ if (dai->playback.channels_min) {
+ ret = tegra_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ if (dai->capture.channels_min) {
+ ret = tegra_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ return 0;
+
+out:
+ if (tegra_snd_cx)
+ kfree(tegra_snd_cx);
+ snd_printk(KERN_ERR "pcm_new failed\n");
+ return ret;
+}
+
+static int tegra_pcm_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int tegra_pcm_remove(struct platform_device *pdev)
+{
+ if (tegra_snd_cx)
+ kfree(tegra_snd_cx);
+
+ return 0;
+}
+
+struct snd_soc_platform tegra_soc_platform = {
+ .name = "tegra-audio",
+ .probe = tegra_pcm_probe,
+ .remove = tegra_pcm_remove,
+ .pcm_ops = &tegra_pcm_ops,
+ .pcm_new = tegra_pcm_new,
+ .pcm_free = tegra_pcm_free_dma_buffers,
+};
+
+EXPORT_SYMBOL_GPL(tegra_soc_platform);
+
+static int __init tegra_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&tegra_soc_platform);
+}
+
+module_init(tegra_soc_platform_init);
+
+static void __exit tegra_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&tegra_soc_platform);
+}
+
+module_exit(tegra_soc_platform_exit);
+
+MODULE_DESCRIPTION("Tegra PCM RPC PCM output");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_sndfx.h b/sound/soc/tegra/tegra_sndfx.h
new file mode 100644
index 000000000000..1e9bb82940a4
--- /dev/null
+++ b/sound/soc/tegra/tegra_sndfx.h
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef INCLUDED_nvddk_audiofx_H
+#define INCLUDED_nvddk_audiofx_H
+
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#include "nvrm_module.h"
+#include "nvrm_transport.h"
+#include "nvrm_init.h"
+
+/** @file
+ * @brief <b>NVIDIA Driver Development Kit: NvAudioFx APIs</b>
+ *
+ * @b Description: Declares Interface for NvAudioFx APIs.
+ */
+
+/**
+ * @brief API Object Handles.
+ *
+ * NvAudioFxObjectHandle is the base handle for every type in the API.
+ */
+
+typedef struct NvAudioFxObjectRec *NvAudioFxObjectHandle;
+
+typedef struct NvAudioFxRec *NvAudioFxHandle;
+
+typedef struct NvAudioFxIoRec *NvAudioFxIoHandle;
+
+typedef struct NvAudioFxMixBufferRec *NvAudioFxMixBufferHandle;
+
+typedef struct NvAudioFxMixerRec *NvAudioFxMixerHandle;
+
+typedef struct NvAudioFxNotifierRec *NvAudioFxNotifierHandle;
+
+typedef struct NvAudioFxStreamRec *NvAudioFxStreamHandle;
+
+/**
+ * @brief Object IDs.
+ */
+
+typedef NvS32 NvObjectId;
+#define NvObjectNullId (0x0)
+#define NvAudioFxObjectId (0x10000000)
+#define NvAudioFxId (0x11000000)
+#define NvAudioFxIoId (0x11100000)
+#define NvAudioFxPluginId (0x11200000)
+#define NvAudioFxMixBufferId (0x12000000)
+#define NvAudioFx3dGroupId (0x14000000)
+#define NvAudioFxNotifierId (0x30000000)
+#define NvAudioFxConvertId (0x11000001)
+#define NvAudioFxDrcId (0x11000002)
+#define NvAudioFxEqId (0x11000003)
+#define NvAudioFxMixerId (0x11000004)
+#define NvAudioFxMixId (0x11000005)
+#define NvAudioFxPeqId (0x11000006)
+#define NvAudioFxResizeId (0x11000007)
+#define NvAudioFxSplitId (0x11000008)
+#define NvAudioFxSpreaderId (0x11000009)
+#define NvAudioFxSrcId (0x1100000a)
+#define NvAudioFxSwitchId (0x1100000b)
+#define NvAudioFxVolumeId (0x1100000c)
+#define NvAudioFxStreamId (0x11100001)
+#define NvAudioFxI2sId (0x11100002)
+#define NvAudioFxI2s2Id (0x11100002)
+#define NvAudioFxSpdifId (0x11100003)
+#define NvAudioFxDefaultPlaybackMixId (0x11300001)
+#define NvAudioFxDefaultPlaybackSplitId (0x11300002)
+#define NvAudioFxDefaultRecordMixId (0x11300003)
+#define NvAudioFxDefaultRecordSplitId (0x11300004)
+#define NvAudioFxI2sPlaybackMixId (0x11300005)
+#define NvAudioFxI2sRecordSplitId (0x11300006)
+#define NvAudioFxI2sLoopbackSplitId (0x11300007)
+#define NvAudioFxI2s2PlaybackMixId (0x11300008)
+#define NvAudioFxI2s2RecordSplitId (0x11300009)
+#define NvAudioFxI2s2LoopbackSplitId (0x1130000a)
+#define NvAudioFxSpdifPlaybackMixId (0x1130000b)
+#define NvAudioFxSpdifRecordSplitId (0x1130000c)
+#define NvAudioFxSpdifLoopbackSplitId (0x1130000d)
+#define NvAudioFxMusicMixId (0x1130000e)
+#define NvAudioFxMusicSplitId (0x1130000f)
+#define NvAudioFxPhoneMixId (0x11300010)
+#define NvAudioFxPhoneSplitId (0x11300011)
+
+//
+// Mixer
+// +------+
+// | |
+// | FX |--@ ScratchSource ------------+
+// | | |
+// +------+ |
+//
+// Source Sink
+// +------+ +------+
+// Copy Sink @--| |--@ Loopback Copy Sink @--| |--@ Loopback
+// | FX | | FX |
+// ----> Sink @--| |--@ Source ------> Sink @--| |--@ Source ---->
+// +------+ +------+
+//
+
+typedef NvS32 NvAudioFxPin;
+#define NvAudioFxScratchSourcePin (-4)
+#define NvAudioFxLoopbackPin (-3)
+#define NvAudioFxCopySinkPin (-2)
+#define NvAudioFxInvalidPin (-1)
+#define NvAudioFxSinkPin (0)
+#define NvAudioFxSourcePin (1)
+
+typedef enum
+{
+ NvAudioFxProperty_Attach,
+ NvAudioFxProperty_Detach,
+ NvAudioFxProperty_Format,
+ NvAudioFxProperty_Method,
+ NvAudioFxProperty_SampleRate,
+ NvAudioFxProperty_State,
+ NvAudioFxPinProperty_Format,
+ NvAudioFxDrcProperty_Drc,
+ NvAudioFxEqProperty_Eq,
+ NvAudioFxIoProperty_AddEvent,
+ NvAudioFxIoProperty_Position,
+ NvAudioFxIoProperty_RemoveEvent,
+ NvAudioFxIoProperty_SetMappedPositionBuffer,
+ NvAudioFxMixerProperty_ProcessBufferSize,
+ NvAudioFxMixProperty_Headroom,
+ NvAudioFxNotifierProperty_Connect,
+ NvAudioFxNotifierProperty_Disconnect,
+ NvAudioFxPeqProperty_Peq,
+ NvAudioFxResizeProperty_OutputSize,
+ NvAudioFxSpreaderProperty_Spreader,
+ NvAudioFxSrcProperty_SampleRateShift,
+ NvAudioFxVolumeProperty_Ramping,
+ NvAudioFxVolumeProperty_Volume,
+ NvAudioFxProperty_Num,
+ NvAudioFxProperty_Force32 = 0x7FFFFFFF
+} NvAudioFxProperty;
+
+// Description of the NvAudioFxProperty_Attach and
+// NvAudioFxProperty_Detach properties.
+
+typedef struct NvAudioFxConnectionDescriptorRec
+{
+ NvAudioFxPin SourcePin;
+ NvAudioFxHandle hSink;
+ NvAudioFxPin SinkPin;
+} NvAudioFxConnectionDescriptor;
+
+// Audio format information of the stream or buffer.
+
+typedef struct NvAudioFxFormatRec
+{
+ NvU32 FormatTag;
+ NvU32 SampleRate;
+ NvU32 Channels;
+ NvU32 BitsPerSample;
+ NvU32 ChannelMask;
+} NvAudioFxFormat;
+
+// Description of the NvAudioFxProperty_Method property.
+
+typedef enum
+{
+ NvAudioFxMethod_Sliced,
+ NvAudioFxMethod_Unsliced,
+ NvAudioFxMethod_Num,
+ NvAudioFxMethod_Force32 = 0x7FFFFFFF
+} NvAudioFxMethod;
+
+// Description of the position property.
+
+typedef NvU64 NvAudioFxPosition;
+
+// Description of supported power states in AudioFx.
+
+typedef enum
+{
+ NvAudioFxPowerState_Low,
+ NvAudioFxPowerState_High,
+ NvAudioFxPowerState_Full,
+ NvAudioFxPowerState_Ready,
+ NvAudioFxPowerState_Off,
+ NvAudioFxPowerState_Num,
+ NvAudioFxPowerState_Force32 = 0x7FFFFFFF
+} NvAudioFxPowerState;
+
+// Description of the supported priority states.
+
+typedef enum
+{
+ NvAudioFxPriority_Normal,
+ NvAudioFxPriority_Medium,
+ NvAudioFxPriority_High,
+ NvAudioFxPriority_Critical,
+ NvAudioFxPriority_Num,
+ NvAudioFxPriority_Force32 = 0x7FFFFFFF
+} NvAudioFxPriority;
+
+// Description of the NvAudioFxProperty_State property.
+
+typedef enum
+{
+ NvAudioFxState_Uninitialized,
+ NvAudioFxState_Initialized,
+ NvAudioFxState_Stop,
+ NvAudioFxState_Run,
+ NvAudioFxState_Pause,
+ NvAudioFxState_Disable,
+ NvAudioFxState_Num,
+ NvAudioFxState_Force32 = 0x7FFFFFFF
+} NvAudioFxState;
+
+// Audio DRC information.
+
+typedef struct NvAudioFxDrcDescriptorRec
+{
+ NvS32 EnableDrc;
+ NvS32 NoiseGateThreshold;
+ NvS32 LowerCompThreshold;
+ NvS32 UpperCompThreshold;
+ NvS32 ClippingThreshold;
+} NvAudioFxDrcDescriptor;
+
+// Audio EQ information.
+#define NvAudioFxEqNumFilters (5)
+#define NvAudioFxEqNumChannels (2)
+
+typedef struct NvAudioFxEqDescriptorRec
+{
+ NvS32 dBGain[NvAudioFxEqNumChannels][NvAudioFxEqNumFilters];
+
+}NvAudioFxEqDescriptor;
+
+// Audio Spreader information.
+
+typedef struct NvAudioFxSpreaderDescriptorRec
+{
+ NvU32 SpeakerWidth;
+} NvAudioFxSpreaderDescriptor;
+
+// Parameteric EQ Filter types
+
+typedef enum
+{
+ NvAudioFxIirFilter_Undefined,
+ NvAudioFxIirFilter_Bandpass,
+ NvAudioFxIirFilter_Highpass,
+ NvAudioFxIirFilter_Lowpass,
+ NvAudioFxIirFilter_Num,
+ NvAudioFxIirFilter_Force32 = 0x7FFFFFFF
+} NvAudioFxIirFilter;
+
+typedef struct NvAudioFxPeqDescriptorRec
+{
+ NvU32 Enable;
+ NvU32 FilterType[NvAudioFxIirFilter_Num];
+ NvS32 CenterFrequency[NvAudioFxIirFilter_Num];
+ NvS32 Bandwidth[NvAudioFxIirFilter_Num];
+ NvS32 dBGain[NvAudioFxIirFilter_Num];
+
+}NvAudioFxPeqDescriptor;
+
+// Audio pin-specific format information of the stream or buffer.
+
+typedef struct NvAudioFxPinFormatDescriptorRec
+{
+ NvAudioFxFormat Format;
+ NvAudioFxPin Pin;
+} NvAudioFxPinFormatDescriptor;
+
+// Audio volume information of the stream or buffer.
+
+typedef struct NvAudioFxVolumeDescriptorRec
+{
+ NvS32 LeftVolume;
+ NvS32 RightVolume;
+ NvU32 Mute;
+} NvAudioFxVolumeDescriptor;
+
+// Description of the NvAudioFxVoiceProperty_SetMappedPositionBuffer property.
+
+typedef struct NvAudioFxMappedBufferDescriptorRec
+{
+ NvAudioFxMixBufferHandle hMixBuffer;
+ NvU32 Offset;
+} NvAudioFxMappedBufferDescriptor;
+
+ /**
+ * @brief Defines the structure for adding a buffer to a stream.
+ */
+
+typedef struct NvAudioFxBufferDescriptorRec
+{
+
+ // A pointer which may be used for already mapped buffers.
+ void* pAddr;
+
+ // A physical address for accessing the buffer.
+ NvRmPhysAddr PhysicalAddr;
+
+ // The MixBuffer handle returned from NvAudioFxMixerMapBuffer.
+ NvAudioFxMixBufferHandle hMixBuffer;
+
+ // Buffer offset in bytes.
+ NvU32 Offset;
+
+ // Buffer size in bytes.
+ NvU32 Size;
+
+ // Size of non-Zero Fill data in buffer, usually same as Size
+ NvU32 ValidSize;
+
+ // The buffer format information.
+ NvAudioFxFormat Format;
+} NvAudioFxBufferDescriptor;
+
+// Description of the NvAudioFxNotifierProperty_Connect property.
+
+typedef struct NvAudioFxNotifierConnectionDescriptorRec
+{
+ NvU8 PortName[16];
+} NvAudioFxNotifierConnectionDescriptor;
+
+// Description of the NvAudioFxProperty_AddEvent and
+// NvAudioFxProperty_RemoveEvent properties.
+
+typedef NvS32 NvAudioFxEvent;
+#define NvAudioFxEventBufferDone (0x1)
+#define NvAudioFxEventStateChange (0x2)
+#define NvAudioFxEventFormatChange (0x4)
+#define NvAudioFxEventEndOfStream (0x8)
+#define NvAudioFxEventPowerStateChange (0x10)
+#define NvAudioFxEventAll (0xffffffff)
+
+typedef struct NvAudioFxMessageRec
+{
+ NvAudioFxEvent Event;
+ NvAudioFxHandle hFx;
+ void* pContext;
+} NvAudioFxMessage;
+
+typedef struct NvAudioFxBufferDoneMessageRec
+{
+ NvAudioFxMessage m;
+ NvAudioFxPosition Position;
+} NvAudioFxBufferDoneMessage;
+
+typedef struct NvAudioFxStateChangeMessageRec
+{
+ NvAudioFxMessage m;
+ NvAudioFxState State;
+} NvAudioFxStateChangeMessage;
+
+typedef struct NvAudioFxFormatChangeMessageRec
+{
+ NvAudioFxMessage m;
+ NvAudioFxFormat Format;
+} NvAudioFxFormatChangeMessage;
+
+typedef struct NvAudioFxPowerStateChangeMessageRec
+{
+ NvAudioFxMessage m;
+ NvAudioFxPowerState PowerState;
+} NvAudioFxPowerStateChangeMessage;
+
+ /**
+ * @brief Initializes and opens the AudioFX Mixer.
+ *
+ * @retval NvAudioFxMixerHandle A non-NULL value will be returned if the mixer
+ * is successfully opened.
+ */
+
+ NvAudioFxMixerHandle NvddkAudioFxMixerOpen(
+ void );
+
+ /**
+ * @brief Closes the AudioFX Mixer. This function frees the resources associated
+ * with the Mixer handle and cannot fail.
+ *
+ * @param hMixer A handle from NvAudioFxMixerOpen(). If hMixer is NULL or
+ * invalid, this API does nothing.
+ */
+
+ void NvddkAudioFxMixerClose(
+ NvAudioFxMixerHandle hMixer );
+
+ /**
+ * @brief Creates and initializes an AudioFX object.
+ *
+ * @param hMixer A handle from NvAudioFxMixerOpen(). If hMixer is NULL the
+ * object will be created by the Global Mixer. A NULL handle should only be
+ * used from the driver and not a normal client application.
+ * @param Id The ID of the object to create.
+ *
+ * @retval NvAudioFxObjectHandle A non-NULL value will be returned if the
+ * object is successfully created.
+ */
+
+ NvAudioFxObjectHandle NvddkAudioFxMixerCreateObject(
+ NvAudioFxMixerHandle hMixer,
+ NvObjectId Id );
+
+ /**
+ * @brief Destroys the AudioFX object.
+ *
+ * @param hObject A handle from NvAudioFxMixerCreateObject. If hObject is
+ * NULL, this API does nothing.
+ */
+
+ void NvddkAudioFxMixerDestroyObject(
+ NvAudioFxObjectHandle hObject );
+
+ /**
+ * @brief Maps a buffer to AudioFX address space.
+ */
+
+ NvAudioFxMixBufferHandle NvddkAudioFxMixerMapBuffer(
+ NvAudioFxMixerHandle hMixer,
+ NvU32 NvRmMemHandleId,
+ NvU32 Offset,
+ NvU32 Size );
+
+ /**
+ * @brief Unmaps a buffer from the AudioFX address space.
+ */
+
+ void NvddkAudioFxMixerUnmapBuffer(
+ NvAudioFxMixBufferHandle hMixBuffer );
+
+ /**
+ * @brief Adds a buffer to a Stream object.
+ *
+ * @param hStream Handle to the Stream object to add the buffer.
+ * @param pDescriptor Description of the buffer to add to the stream.
+ *
+ * @retval NvSuccess Indicates the operation succeeded.
+ */
+
+ NvError NvddkAudioFxStreamAddBuffer(
+ NvAudioFxStreamHandle hStream,
+ NvAudioFxBufferDescriptor * pDescriptor );
+
+/**
+ * @brief Get the property value from an AudioFX object.
+ *
+ * @param hObject Handle of the object to get the property value.
+ * @param Property The property of interest.
+ * @param Size Size of the descriptor.
+ * @param pDescriptor Holds the value of the current property.
+ *
+ * @retval NvSuccess Indicates the operation succeeded.
+ */
+
+ NvError NvddkAudioFxGetProperty(
+ NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty );
+
+/**
+ * @brief Set a property value on an AudioFX object.
+ *
+ * @param hObject Handle of the object to set the property on.
+ * @param Property The property of interest.
+ * @param Size Size of the property descriptor.
+ * @param pProperty Holds the value of the property to set.
+ *
+ * @retval NvSuccess Indicates the operation succeeded.
+ */
+
+ NvError NvddkAudioFxSetProperty(
+ NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty );
+
+/**
+ * @brief AVP API Function Table
+ */
+
+typedef struct NvddkAudioFxFxnTableRec
+{
+ NvAudioFxMixerHandle (*MixerOpen)(void);
+ void (*MixerClose)(NvAudioFxMixerHandle hMixer);
+ NvAudioFxObjectHandle (*MixerCreateObject)(NvAudioFxMixerHandle hMixer, NvObjectId Id);
+ void (*MixerDestroyObject)(NvAudioFxObjectHandle hObject);
+ NvAudioFxMixBufferHandle (*MixerMapBuffer)(NvAudioFxMixerHandle hMixer, NvU32 NvRmMemHandleId, NvU32 Offset, NvU32 Size);
+ void (*MixerUnmapBuffer)(NvAudioFxMixBufferHandle hMixBuffer);
+ NvError (*StreamAddBuffer)(NvAudioFxStreamHandle hStream, NvAudioFxBufferDescriptor* pDescriptor);
+ NvError (*GetProperty)(NvAudioFxObjectHandle hObject, NvAudioFxProperty Property, NvU32 Size, void* pProperty);
+ NvError (*SetProperty)(NvAudioFxObjectHandle hObject, NvAudioFxProperty Property, NvU32 Size, void* pProperty);
+
+} NvddkAudioFxFxnTable;
+
+static const NvU32 NvddkAudioFxFxnTableId = 0x6e766178; // 'nvax'
+
+typedef enum
+{
+ NvAudioFxIoctl_Generic = 5020,
+ NvAudioFxIoctl_ForceWord = 0x7FFFFFFF,
+
+} NvAudioFxIoctl;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/sound/soc/tegra/tegra_transport.c b/sound/soc/tegra/tegra_transport.c
new file mode 100644
index 000000000000..47b62c07f057
--- /dev/null
+++ b/sound/soc/tegra/tegra_transport.c
@@ -0,0 +1,945 @@
+/*
+ * sound/soc/tegra/alsa_transport.c
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "tegra_transport.h"
+#include <linux/completion.h>
+
+#define transport_send_message(msg_type) \
+ message.Semaphore = 0; \
+ message.SendAck = 1; \
+ status = NvRmTransportSendMsg(atrans->hRmTransport, \
+ &message, \
+ sizeof(msg_type), \
+ TEGRA_TRANSPORT_SEND_TIMEOUT); \
+ if (status != NvSuccess) { \
+ snd_printk(KERN_ERR "NvRmTransportSendMsg failed!\n"); \
+ goto EXIT_WITH_ERROR; \
+ } \
+ wait_for_completion(&comp); \
+
+
+static AlsaTransport* atrans = 0;
+
+
+static NvAudioFxMixerHandle tegra_transport_mixer_open()
+{
+ NvError status = NvSuccess;
+ NvAudioFxMixerHandle hMixer = 0;
+ NvFxTransportMessageMixerOpen message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_MIXER_OPEN;
+ message.pPrivateData = (void*)&comp;
+ message.phMixer = &hMixer;
+
+ transport_send_message(NvFxTransportMessageMixerOpen);
+
+EXIT_WITH_ERROR:
+ return hMixer;
+}
+
+static void tegra_transport_mixer_close(NvAudioFxMixerHandle hMixer)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageMixerClose message;
+ struct completion comp;
+
+ init_completion(&comp);
+ if (hMixer == NULL) {
+ snd_printk(KERN_ERR "NULL NvAudioFxMixerHandle!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ message.Message = NVFXTRANSPORT_MESSAGE_MIXER_CLOSE;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+
+ transport_send_message(NvFxTransportMessageMixerClose);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvAudioFxObjectHandle tegra_transport_mixer_create_object(
+ NvAudioFxMixerHandle hMixer,
+ NvObjectId Id)
+{
+ NvError status = NvSuccess;
+ NvAudioFxObjectHandle hObject = 0;
+ NvFxTransportMessageCreateObject message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_CREATE_OBJECT;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+ message.Id = Id;
+ message.phObject = &hObject;
+
+ transport_send_message(NvFxTransportMessageCreateObject);
+
+EXIT_WITH_ERROR:
+ return hObject;
+}
+
+static void tegra_transport_mixer_destroy_object(NvAudioFxObjectHandle hObject)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageDestroyObject message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+
+ transport_send_message(NvFxTransportMessageDestroyObject);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvAudioFxMixBufferHandle tegra_transport_mixer_map_buffer(
+ NvAudioFxMixerHandle hMixer,
+ NvU32 NvRmMemHandleId,
+ NvU32 Offset,
+ NvU32 Size)
+{
+ NvError status = NvSuccess;
+ NvAudioFxMixBufferHandle mixer_buffer = 0;
+ NvFxTransportMessageMapBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_MAP_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+ message.NvRmMemHandleId = NvRmMemHandleId;
+ message.Offset = Offset;
+ message.Size = Size;
+ message.phMixBuffer = &mixer_buffer;
+
+ transport_send_message(NvFxTransportMessageMapBuffer);
+EXIT_WITH_ERROR:
+ return mixer_buffer;
+}
+
+
+static void tegra_transport_mixer_unmap_buffer(
+ NvAudioFxMixBufferHandle mixer_buffer)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageUnmapBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hMixBuffer = mixer_buffer;
+
+ transport_send_message(NvFxTransportMessageUnmapBuffer);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvError tegra_transport_get_property(NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty)
+{
+ NvError status = NvSuccess;
+ NvError retStatus = NvSuccess;
+ NvFxTransportMessageGetProperty message;
+ struct completion comp;
+
+ if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) {
+ snd_printk(KERN_ERR "Property length too long!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_GET_PROPERTY;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+ message.Property = Property;
+ message.Size = Size;
+ message.pProperty = pProperty;
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageGetProperty);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static NvError tegra_transport_set_property(NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty)
+{
+ NvError status = NvSuccess;
+ NvError retStatus = NvSuccess;
+ NvFxTransportMessageSetProperty message;
+ struct completion comp;
+
+ if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) {
+ snd_printk(KERN_ERR "Property length too long!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_SET_PROPERTY;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+ message.Property = Property;
+ message.Size = Size;
+ NvOsMemcpy(message.PropertyData, pProperty, Size);
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageSetProperty);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static NvError tegra_transport_stream_add_buffer(NvAudioFxStreamHandle hStream,
+ NvAudioFxBufferDescriptor* pDescriptor)
+{
+ NvError status = NvSuccess;
+ NvError retStatus;
+ NvFxTransportMessageStreamAddBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hStream = hStream;
+ message.Descriptor = *pDescriptor;
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageStreamAddBuffer);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static void AlsaTransportServiceThread(void *arg)
+{
+ NvError status = NvSuccess;
+ static NvFxTransportMessageResultBuffer in;
+ NvU32 messageSize = 0;
+ int retry = 0;
+#define transport_complete(comp) \
+ complete((struct completion*)comp);
+
+ while (retry < 5 ) {
+ status = NvRmTransportConnect(atrans->hRmTransport,
+ TEGRA_TRANSPORT_CONNECT_TIMEOUT);
+ if (status == NvSuccess)
+ break;
+ retry++;
+ }
+
+ if (status) {
+ snd_printk(KERN_ERR "AlsaTransportServiceThread: Failed to \
+ connect to remote end. Giving up ! \n");
+ atrans->TransportConnected = 0;
+ return;
+ }
+
+ atrans->TransportConnected = 1;
+
+ while (!atrans->ShutDown) {
+ NvOsSemaphoreWait(atrans->hServiceSema);
+ if (atrans->ShutDown)
+ break;
+
+ status = NvRmTransportRecvMsg(atrans->hRmTransport,
+ &in,
+ sizeof(NvFxTransportMessageResultBuffer),
+ &messageSize);
+ if (status == NvSuccess) {
+ switch (in.Message.Message) {
+ case NVFXTRANSPORT_MESSAGE_MIXER_OPEN: {
+ *in.MixerOpen.phMixer = in.MixerOpen.hMixer;
+ transport_complete(in.MixerOpen.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_MIXER_CLOSE: {
+ transport_complete(in.MixerClose.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_CREATE_OBJECT: {
+ *in.CreateObject.phObject =
+ in.CreateObject.hObject;
+ transport_complete(in.CreateObject.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT: {
+ transport_complete(in.DestroyObject.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_MAP_BUFFER: {
+ *in.MapBuffer.phMixBuffer =
+ in.MapBuffer.hMixBuffer;
+ transport_complete(in.MapBuffer.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER: {
+ transport_complete(in.UnmapBuffer.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_GET_PROPERTY: {
+ *in.GetProperty.pReturnError =
+ in.GetProperty.ReturnError;
+ if (in.GetProperty.ReturnError == NvSuccess) {
+ NvOsMemcpy(in.GetProperty.pProperty,
+ in.GetProperty.PropertyData,
+ in.GetProperty.Size);
+ }
+ transport_complete(in.GetProperty.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_SET_PROPERTY: {
+ *in.SetProperty.pReturnError =
+ in.SetProperty.ReturnError;
+ transport_complete(in.SetProperty.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER: {
+ *in.StreamAddBuffer.pReturnError =
+ in.StreamAddBuffer.ReturnError;
+ transport_complete(in.StreamAddBuffer.pPrivateData);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+}
+
+NvError tegra_transport_init(NvddkAudioFxFxnTable* xrt_fxns)
+{
+ NvError status = NvSuccess;
+
+ xrt_fxns->MixerOpen = tegra_transport_mixer_open;
+ xrt_fxns->MixerClose = tegra_transport_mixer_close;
+ xrt_fxns->MixerCreateObject = tegra_transport_mixer_create_object;
+ xrt_fxns->MixerDestroyObject = tegra_transport_mixer_destroy_object;
+ xrt_fxns->MixerMapBuffer = tegra_transport_mixer_map_buffer;
+ xrt_fxns->MixerUnmapBuffer = tegra_transport_mixer_unmap_buffer;
+ xrt_fxns->StreamAddBuffer = tegra_transport_stream_add_buffer;
+ xrt_fxns->GetProperty = tegra_transport_get_property;
+ xrt_fxns->SetProperty = tegra_transport_set_property;
+
+ /* Check if the FX Transport is already open.*/
+ if (atrans) {
+ spin_lock(&atrans->lock);
+ atrans->RefCount++;
+ spin_unlock(&atrans->lock);
+ goto EXIT;
+ }
+ /* Map a shared memory buffer.*/
+ atrans = (AlsaTransport*)kzalloc(sizeof(AlsaTransport),GFP_KERNEL);
+ if (!atrans) {
+ snd_printk(KERN_ERR "AlsaTransportInit kalloc failed! \n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ spin_lock_init(&atrans->lock);
+ memset(atrans, 0, sizeof(AlsaTransport));
+ spin_lock(&atrans->lock);
+ atrans->RefCount++;
+ spin_unlock(&atrans->lock);
+
+ atrans->hRmDevice = s_hRmGlobal;
+
+ status = NvOsSemaphoreCreate(&atrans->hServiceSema, 0);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ status = NvRmTransportOpen(atrans->hRmDevice,
+ "ALSA_TRANSPORT",
+ atrans->hServiceSema,
+ &atrans->hRmTransport);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportOpen failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ status = NvOsThreadCreate(AlsaTransportServiceThread,
+ NULL,
+ &atrans->hServiceThread);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsThreadCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ while (!atrans->TransportConnected) {
+ NvOsThreadYield();
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ if (atrans) {
+ tegra_transport_deinit();
+ }
+
+EXIT:
+ return status;
+}
+
+void tegra_transport_deinit(void)
+{
+ if (!atrans)
+ goto EXIT;
+
+ spin_lock(&atrans->lock);
+ atrans->RefCount--;
+
+ if (atrans->RefCount > 0){
+ spin_unlock(&atrans->lock);
+ goto EXIT;
+ }
+ spin_unlock(&atrans->lock);
+
+ atrans->ShutDown = 1;
+
+ if (atrans->hRmTransport) {
+ NvRmTransportClose(atrans->hRmTransport);
+ atrans->hRmTransport = 0;
+ atrans->TransportConnected = 0;
+ }
+
+ if (atrans->hServiceThread) {
+ NvOsSemaphoreSignal(atrans->hServiceSema);
+ NvOsThreadJoin(atrans->hServiceThread);
+ atrans->hServiceThread = 0;
+ }
+
+ if (atrans->hServiceSema) {
+ NvOsSemaphoreDestroy(atrans->hServiceSema);
+ atrans->hServiceSema = 0;
+ }
+ atrans->hRmDevice = 0;
+ kfree(atrans);
+ atrans = 0;
+
+EXIT:
+ return;
+}
+
+static void tegra_audiofx_notifier_thread(void *arg)
+{
+ struct tegra_audio_data *audio_context = (struct tegra_audio_data *)arg;
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+ NvError e;
+ int retry = 0;
+ NvU32 messageSize;
+ NvAudioFxMessage* message =
+ (NvAudioFxMessage*)m_FxNotifier->RcvMessageBuffer;
+
+ while (retry < 5) {
+ e = NvRmTransportConnect(m_FxNotifier->hTransport, 5000);
+ if (e == NvSuccess)
+ break;
+
+ retry++;
+ }
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportConnect failed!\n");
+ m_FxNotifier->Connected = 0;
+ goto EXIT;
+ }
+
+ m_FxNotifier->Connected = 1;
+ while (1) {
+ NvOsSemaphoreWait(m_FxNotifier->hTransportSemaphore);
+ if (m_FxNotifier->Exit) {
+ break;
+ }
+
+ e = NvRmTransportRecvMsg(m_FxNotifier->hTransport,
+ message,
+ 256,
+ &messageSize);
+ if (e == NvSuccess) {
+ switch (message->Event) {
+ case NvAudioFxEventBufferDone:{
+ NvAudioFxBufferDoneMessage* bdm =
+ (NvAudioFxBufferDoneMessage*)message;
+ struct pcm_runtime_data* prtd =
+ (struct pcm_runtime_data*)bdm->m.pContext;
+
+ if ((prtd->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (prtd->play_sema != NULL)){
+ NvOsSemaphoreSignal(prtd->play_sema);
+ }
+ else if(prtd->rec_sema != NULL){
+ NvOsSemaphoreSignal(prtd->rec_sema);
+ }
+ }
+ break;
+
+ case NvAudioFxEventStateChange:
+ break;
+
+ default:
+ snd_printk(KERN_ERR"Unhandled event\n");
+ break;
+ }
+ }
+ }
+
+EXIT:
+ return;
+}
+
+GlobalFxList* tegra_audiofx_create_topology(NvAudioFxMixerHandle hMixer)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+ GlobalFxList* pFxList = 0;
+
+ memset(&connection, 0, sizeof(NvAudioFxConnectionDescriptor));
+ pFxList = (GlobalFxList*)kzalloc(sizeof(GlobalFxList), GFP_KERNEL);
+ if (!pFxList) {
+ snd_printk(KERN_ERR "kzalloc failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+ memset(pFxList, 0, sizeof(GlobalFxList));
+
+#define audiofx_create_path(list_index, FxId) \
+ pFxList->hFx[list_index] = tegra_transport_mixer_create_object(hMixer,FxId); \
+ if(!pFxList->hFx[list_index]) { \
+ snd_printk(KERN_ERR "audiofx_create_path failed!\n"); \
+ goto EXIT_WITH_ERROR; \
+ }
+
+#define audiofx_connect(src_index, sink_index) \
+ connection.SourcePin = NvAudioFxSourcePin; \
+ connection.SinkPin = NvAudioFxSinkPin; \
+ connection.hSink = (NvAudioFxHandle)pFxList->hFx[sink_index]; \
+ e = tegra_transport_set_property(pFxList->hFx[src_index], \
+ NvAudioFxProperty_Attach, \
+ sizeof(NvAudioFxConnectionDescriptor), \
+ &connection); \
+ if(e != NvSuccess) { \
+ snd_printk(KERN_ERR "audiofx_connect failed!\n"); \
+ }
+
+ /* Default Playback Path */
+ audiofx_create_path(GlobalFx_DefaultPlaybackMix,
+ NvAudioFxDefaultPlaybackMixId);
+
+ audiofx_create_path(GlobalFx_DefaultPlaybackSplit,
+ NvAudioFxDefaultPlaybackSplitId);
+
+ /* I2S Playback Path */
+ audiofx_create_path(GlobalFx_I2sPlaybackMix, NvAudioFxI2sPlaybackMixId);
+ audiofx_create_path(GlobalFx_I2sPlaybackVolume, NvAudioFxVolumeId);
+ audiofx_create_path(GlobalFx_I2s, NvAudioFxI2sId);
+
+ /* Default Record Path */
+ audiofx_create_path(GlobalFx_DefaultRecordMix,
+ NvAudioFxDefaultRecordMixId);
+
+ audiofx_create_path(GlobalFx_DefaultRecordSplit,
+ NvAudioFxDefaultRecordSplitId);
+
+ /* I2S Record Path */
+ audiofx_create_path(GlobalFx_I2sRecordVolume, NvAudioFxVolumeId);
+ audiofx_create_path(GlobalFx_I2sRecordSplit, NvAudioFxI2sRecordSplitId);
+
+#if (0)
+ /* Simple Playback */
+ audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_I2s);
+#else
+ /* Playback */
+ audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_DefaultPlaybackSplit);
+ audiofx_connect(GlobalFx_DefaultPlaybackSplit, GlobalFx_I2sPlaybackMix);
+
+ /* Wire SPDIF on HDMI connection and unwire it when not connected. */
+ audiofx_connect(GlobalFx_I2sPlaybackMix, GlobalFx_I2sPlaybackVolume);
+ audiofx_connect(GlobalFx_I2sPlaybackVolume, GlobalFx_I2s);
+#endif
+
+ /*Record*/
+ audiofx_connect(GlobalFx_I2s, GlobalFx_I2sRecordVolume);
+ audiofx_connect(GlobalFx_I2sRecordVolume, GlobalFx_I2sRecordSplit);
+
+ audiofx_connect(GlobalFx_I2sRecordSplit, GlobalFx_DefaultRecordMix);
+ audiofx_connect(GlobalFx_DefaultRecordMix, GlobalFx_DefaultRecordSplit);
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ /* tegra_audiofx_destroy_topology(pFxList); */
+ pFxList = 0;
+
+EXIT:
+ return pFxList;
+}
+
+void tegra_audiofx_destroy_topology(GlobalFxList* pFxList)
+{
+ NvS32 i;
+
+ if (pFxList) {
+ for (i = 0; i < GlobalFx_Num; i++) {
+ if (pFxList->hFx[i]) {
+ /*
+ TODO
+ NvddkAudioFxMixerDestroyObject(pFxList->hFx[i]);
+ Add code to destory
+ */
+ }
+ }
+
+ kfree(pFxList);
+ }
+}
+
+NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context)
+{
+ NvAudioFxMixerHandle m_hMixer =
+ (NvAudioFxMixerHandle)audio_context->mixer_handle;
+
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+ NvRmDeviceHandle m_hRm = (NvRmDeviceHandle)audio_context->m_hRm;
+ NvError e = NvSuccess;
+ NvAudioFxNotifierConnectionDescriptor connectionDesciptor;
+ NvAudioFxMessage message;
+
+ memset(&connectionDesciptor,
+ 0,
+ sizeof(NvAudioFxNotifierConnectionDescriptor));
+ memset(&message, 0, sizeof(NvAudioFxMessage));
+
+ e = NvOsSemaphoreCreate(&m_FxNotifier->hTransportSemaphore, 0);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ m_FxNotifier->hNotifier =
+ (NvAudioFxNotifierHandle)tegra_transport_mixer_create_object(
+ m_hMixer,
+ NvAudioFxNotifierId);
+ if (!m_FxNotifier->hNotifier) {
+ snd_printk(KERN_ERR "transport_mixer_create_object failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ e = NvRmTransportOpen(m_hRm,
+ 0,
+ m_FxNotifier->hTransportSemaphore,
+ &m_FxNotifier->hTransport);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportOpen failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ NvRmTransportGetPortName(m_FxNotifier->hTransport,
+ (NvU8*)&connectionDesciptor.PortName,
+ sizeof(NvU8) * 16);
+
+ e = NvOsThreadCreate(tegra_audiofx_notifier_thread,
+ audio_context,
+ &m_FxNotifier->hTransportThread);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsThreadCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ e = tegra_transport_set_property(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier,
+ NvAudioFxNotifierProperty_Connect,
+ sizeof(NvAudioFxNotifierConnectionDescriptor),
+ &connectionDesciptor);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ tegra_audiofx_destroyfx(audio_context);
+
+EXIT:
+ return e;
+}
+
+void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context)
+{
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+
+ if (m_FxNotifier->Connected) {
+ m_FxNotifier->Exit = 1;
+ NvOsSemaphoreSignal(m_FxNotifier->hTransportSemaphore);
+ NvOsThreadJoin(m_FxNotifier->hTransportThread);
+ m_FxNotifier->hTransportThread = 0;
+ tegra_transport_set_property(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier,
+ NvAudioFxNotifierProperty_Disconnect,
+ 0,
+ 0);
+ }
+
+ if (m_FxNotifier->hTransport) {
+ NvRmTransportClose(m_FxNotifier->hTransport);
+ m_FxNotifier->hTransport = 0;
+ }
+
+ if (m_FxNotifier->hNotifier) {
+ tegra_transport_mixer_destroy_object(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier);
+ m_FxNotifier->hNotifier = 0;
+ }
+
+ if (m_FxNotifier->hTransportSemaphore) {
+ NvOsSemaphoreDestroy(m_FxNotifier->hTransportSemaphore);
+ m_FxNotifier->hTransportSemaphore = 0;
+ }
+
+ return;
+}
+
+#define audiofx_create_object(path_object, FxId) \
+ path_object = tegra_transport_mixer_create_object(hMixer, FxId); \
+ if(!path_object) { \
+ snd_printk(KERN_ERR "audiofx_create_object failed!"); \
+ }
+
+#define audiofx_path_connect(path_object, sink_object) \
+ connection.SourcePin = NvAudioFxSourcePin; \
+ connection.SinkPin = NvAudioFxSinkPin; \
+ connection.hSink = (NvAudioFxHandle)sink_object; \
+ e = tegra_transport_set_property(path_object, \
+ NvAudioFxProperty_Attach, \
+ sizeof(NvAudioFxConnectionDescriptor), \
+ &connection); \
+ if(e != NvSuccess) { \
+ snd_printk(KERN_ERR "audiofx_path_connect failed!"); \
+ goto EXIT_WITH_ERROR; \
+ }
+
+NvError tegra_audiofx_create_output(NvRmDeviceHandle hRmDevice,
+ NvAudioFxMixerHandle hMixer,
+ StandardPath* pPath)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+
+ /* Standard Output
+ [stream]->[SRC]->[Convert]->[Resize]->[Volume]->Default Output
+ */
+
+ memset(pPath, 0, sizeof(StandardPath));
+
+ audiofx_create_object(pPath->Stream, NvAudioFxStreamId);
+ audiofx_create_object(pPath->Src, NvAudioFxSrcId);
+ audiofx_create_object(pPath->Convert, NvAudioFxConvertId);
+ audiofx_create_object(pPath->Resize, NvAudioFxResizeId);
+ audiofx_create_object(pPath->Volume, NvAudioFxVolumeId);
+
+ audiofx_path_connect(pPath->Stream, pPath->Src);
+ audiofx_path_connect(pPath->Src, pPath->Convert);
+ audiofx_path_connect(pPath->Convert, pPath->Resize);
+ audiofx_path_connect(pPath->Resize, pPath->Volume);
+
+ connection.SourcePin = NvAudioFxSourcePin;
+ connection.hSink = 0;
+ connection.SinkPin = NvAudioFxSinkPin;
+ e = tegra_transport_set_property(pPath->Volume,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ tegra_audiofx_destroy_output(pPath);
+
+EXIT:
+
+ return e;
+}
+
+NvError tegra_audiofx_destroy_output(StandardPath* pPath)
+{
+ if (pPath->Volume) {
+ tegra_transport_mixer_destroy_object(pPath->Volume);
+ pPath->Volume = 0;
+ }
+
+ if (pPath->Resize) {
+ tegra_transport_mixer_destroy_object(pPath->Resize);
+ pPath->Resize = 0;
+ }
+
+ if (pPath->Convert) {
+ tegra_transport_mixer_destroy_object(pPath->Convert);
+ pPath->Convert = 0;
+ }
+
+ if (pPath->Src) {
+ tegra_transport_mixer_destroy_object(pPath->Src);
+ pPath->Src = 0;
+ }
+
+ if (pPath->Stream) {
+ tegra_transport_mixer_destroy_object(pPath->Stream);
+ pPath->Stream = 0;
+ }
+
+ return NvSuccess;
+}
+
+NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice,
+ NvAudioFxMixerHandle hMixer,
+ StandardPath* pInput,
+ InputSelection InputSelect)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+
+ /*
+ Standard Input (record or loopback)
+
+ +--------+ (2) +--------+ (3) +---------+ (4) +--------+ (5)
+ +--| Stream |<----| Resize |<-----| Convert |<-----| SRC |<--From I2S
+ | | | | |<--+ | (opt.) | | (opt.) |
+ | +--------+ +--------+ | +---------+ +--------+
+ | (1) |
+ +------------------------------+
+
+ */
+
+ memset(pInput, 0, sizeof(StandardPath));
+
+ audiofx_create_object(pInput->Stream, NvAudioFxStreamId);
+ audiofx_create_object(pInput->Resize,NvAudioFxResizeId);
+ audiofx_create_object(pInput->Src,NvAudioFxSrcId);
+ audiofx_create_object(pInput->Convert,NvAudioFxConvertId);
+
+ /* Wire 1 */
+ connection.SourcePin = NvAudioFxSourcePin;
+ connection.hSink = (NvAudioFxHandle)pInput->Resize;
+ connection.SinkPin = NvAudioFxCopySinkPin;
+ e = tegra_transport_set_property(pInput->Stream,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ audiofx_path_connect(pInput->Resize, pInput->Stream);
+
+ /* Wire 5 */
+ connection.SourcePin = (InputSelect == NvAudioInputSelect_Record) ?
+ NvAudioFxSourcePin : NvAudioFxLoopbackPin;
+ connection.hSink = (NvAudioFxHandle)pInput->Resize;
+ connection.SinkPin = NvAudioFxSinkPin;
+ e = tegra_transport_set_property(0,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ tegra_audiofx_destroy_input(pInput);
+
+EXIT:
+
+ return e;
+}
+
+
+NvError tegra_audiofx_destroy_input(StandardPath* pInput)
+{
+ if (pInput->Src) {
+ tegra_transport_mixer_destroy_object(pInput->Src);
+ pInput->Src = 0;
+ }
+
+ if (pInput->Convert) {
+ tegra_transport_mixer_destroy_object(pInput->Convert);
+ pInput->Convert = 0;
+ }
+
+ if (pInput->Resize) {
+ tegra_transport_mixer_destroy_object(pInput->Resize);
+ pInput->Resize = 0;
+ }
+
+ if (pInput->Stream) {
+ tegra_transport_mixer_destroy_object(pInput->Stream);
+ pInput->Stream = 0;
+ }
+
+ return NvSuccess;
+}
+
diff --git a/sound/soc/tegra/tegra_transport.h b/sound/soc/tegra/tegra_transport.h
new file mode 100644
index 000000000000..8af6ac30424c
--- /dev/null
+++ b/sound/soc/tegra/tegra_transport.h
@@ -0,0 +1,426 @@
+/*
+ * sound/soc/tegra/alsa_transport.h
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _TEGRA_TRANSPORT_H_
+#define _TEGRA_TRANSPORT_H_
+
+#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 <linux/kthread.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/mutex.h>
+
+#include <mach/nvrm_linux.h>
+#include "nvrm_memmgr.h"
+#include "nvassert.h"
+#include "nvrm_transport.h"
+
+#include "tegra_sndfx.h"
+
+
+#define INIT_TIMEOUT 5000
+#define PLAY_TIMEOUT 5000
+#define REC_TIMEOUT 5000
+#define WHISTLER_CODEC_ADDRESS 0x1a
+#define WHISTLER_CODEC_BUS 0
+#define NVALSA_BUFFER_COUNT 1
+#define TEGRA_DEFAULT_BUFFER_SIZE 4096
+#define NVALSA_INVALID_STATE -1
+
+#define TEGRA_TRANSPORT_SEND_TIMEOUT 5000
+#define TEGRA_TRANSPORT_CONNECT_TIMEOUT 60000
+#define FXTRANSPORT_MSG_BUFFER_SIZE 256
+#define FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE FXTRANSPORT_MSG_BUFFER_SIZE - 32
+#define FXTRANSPORT_MSG_RESULT_DATA_SIZE FXTRANSPORT_MSG_BUFFER_SIZE - 32
+
+typedef enum {
+ NVFXTRANSPORT_MESSAGE_MIXER_OPEN = 0,
+ NVFXTRANSPORT_MESSAGE_MIXER_CLOSE,
+ NVFXTRANSPORT_MESSAGE_CREATE_OBJECT,
+ NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT,
+ NVFXTRANSPORT_MESSAGE_MAP_BUFFER,
+ NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER,
+ NVFXTRANSPORT_MESSAGE_GET_PROPERTY,
+ NVFXTRANSPORT_MESSAGE_SET_PROPERTY,
+ NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER,
+
+ NVFXTRANSPORT_MESSAGE_Force32 = 0x7FFFFFFF
+
+} NVFXTRANSPORT_MESSAGE;
+
+
+typedef struct NvFxTransportMessageRec {
+#define NVFXTRANSPORT_MESSAGE_VARS \
+ NVFXTRANSPORT_MESSAGE Message; \
+ NvOsSemaphoreHandle Semaphore; \
+ void* pPrivateData; \
+ NvU32 SendAck
+
+ NVFXTRANSPORT_MESSAGE_VARS;
+
+} NvFxTransportMessage;
+
+typedef struct NvFxTransportMessageResultRec {
+#define NVFXTRANSPORT_MESSAGE_RESULT_VARS \
+ NVFXTRANSPORT_MESSAGE Message; \
+ NvOsSemaphoreHandle Semaphore; \
+ void* pPrivateData
+
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+
+} NvFxTransportMessageResult;
+
+
+typedef struct NvFxTransportMessageMixerOpenRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxMixerHandle* phMixer;
+
+} NvFxTransportMessageMixerOpen;
+
+typedef struct NvFxTransportMessageResultMixerOpenRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvAudioFxMixerHandle hMixer;
+ NvAudioFxMixerHandle* phMixer;
+
+} NvFxTransportMessageResultMixerOpen;
+
+
+typedef struct NvFxTransportMessageMixerCloseRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxMixerHandle hMixer;
+
+} NvFxTransportMessageMixerClose;
+
+typedef NvFxTransportMessageResult NvFxTransportMessageResultMixerClose;
+
+
+typedef struct NvFxTransportMessageCreateObjectRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxMixerHandle hMixer;
+ NvObjectId Id;
+
+ NvAudioFxObjectHandle* phObject;
+
+} NvFxTransportMessageCreateObject;
+
+typedef struct NvFxTransportMessageResultCreateObjectRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvAudioFxObjectHandle hObject;
+ NvAudioFxObjectHandle* phObject;
+
+} NvFxTransportMessageResultCreateObject;
+
+
+typedef struct NvFxTransportMessageDestroyObjectRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxObjectHandle hObject;
+
+} NvFxTransportMessageDestroyObject;
+
+typedef NvFxTransportMessageResult NvFxTransportMessageResultDestroyObject;
+
+
+typedef struct NvFxTransportMessageMapBufferRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxMixerHandle hMixer;
+ NvU32 NvRmMemHandleId;
+ NvU32 Offset;
+ NvU32 Size;
+
+ NvAudioFxMixBufferHandle* phMixBuffer;
+
+} NvFxTransportMessageMapBuffer;
+
+typedef struct NvFxTransportMessageResultMapBufferRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvAudioFxMixBufferHandle hMixBuffer;
+ NvAudioFxMixBufferHandle* phMixBuffer;
+
+} NvFxTransportMessageResultMapBuffer;
+
+
+typedef struct NvFxTransportMessageUnmapBufferRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxMixBufferHandle hMixBuffer;
+
+} NvFxTransportMessageUnmapBuffer;
+
+typedef NvFxTransportMessageResult NvFxTransportMessageResultUnmapBuffer;
+
+
+typedef struct NvFxTransportMessageGetPropertyRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxObjectHandle hObject;
+ NvAudioFxProperty Property;
+ NvU32 Size;
+
+ void* pProperty;
+ NvError* pReturnError;
+
+} NvFxTransportMessageGetProperty;
+
+typedef struct NvFxTransportMessageResultGetPropertyRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvU32 Size;
+ NvError ReturnError;
+ void* pProperty;
+ NvError* pReturnError;
+ NvU8 PropertyData[FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE];
+
+} NvFxTransportMessageResultGetProperty;
+
+
+typedef struct NvFxTransportMessageSetPropertyRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxObjectHandle hObject;
+ NvAudioFxProperty Property;
+ NvU32 Size;
+ NvError* pReturnError;
+ NvU8 PropertyData[FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE];
+} NvFxTransportMessageSetProperty;
+
+typedef struct NvFxTransportMessageResultSetPropertyRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvError ReturnError;
+ NvError* pReturnError;
+
+} NvFxTransportMessageResultSetProperty;
+
+
+typedef struct NvFxTransportMessageStreamAddBufferRec {
+ NVFXTRANSPORT_MESSAGE_VARS;
+ NvAudioFxStreamHandle hStream;
+ NvAudioFxBufferDescriptor Descriptor;
+
+ NvError* pReturnError;
+
+} NvFxTransportMessageStreamAddBuffer;
+
+typedef struct NvFxTransportMessageResultStreamAddBufferRec {
+ NVFXTRANSPORT_MESSAGE_RESULT_VARS;
+ NvError ReturnError;
+ NvError* pReturnError;
+
+} NvFxTransportMessageResultStreamAddBuffer;
+
+typedef union NvFxTranspportMessageBuffer {
+ NvFxTransportMessage Message;
+ NvFxTransportMessageMixerOpen MixerOpen;
+ NvFxTransportMessageMixerClose MixerClose;
+ NvFxTransportMessageCreateObject CreateObject;
+ NvFxTransportMessageDestroyObject DestroyObject;
+ NvFxTransportMessageMapBuffer MapBuffer;
+ NvFxTransportMessageUnmapBuffer UnmapBuffer;
+ NvFxTransportMessageGetProperty GetProperty;
+ NvFxTransportMessageSetProperty SetProperty;
+ NvFxTransportMessageStreamAddBuffer StreamAddBuffer;
+
+} NvFxTransportMessageBuffer;
+
+typedef union NvFxTranspportMessageResultBuffer {
+ NvFxTransportMessageResult Message;
+ NvFxTransportMessageResultMixerOpen MixerOpen;
+ NvFxTransportMessageResultMixerClose MixerClose;
+ NvFxTransportMessageResultCreateObject CreateObject;
+ NvFxTransportMessageResultDestroyObject DestroyObject;
+ NvFxTransportMessageResultMapBuffer MapBuffer;
+ NvFxTransportMessageResultUnmapBuffer UnmapBuffer;
+ NvFxTransportMessageResultGetProperty GetProperty;
+ NvFxTransportMessageResultSetProperty SetProperty;
+ NvFxTransportMessageResultStreamAddBuffer StreamAddBuffer;
+
+} NvFxTransportMessageResultBuffer;
+
+typedef struct AlsaTransportRec {
+ NvddkAudioFxFxnTable* hFxnTable;
+ NvOsThreadHandle hServiceThread;
+ NvOsSemaphoreHandle hServiceSema;
+
+ NvRmDeviceHandle hRmDevice;
+ NvRmTransportHandle hRmTransport;
+
+ volatile NvU32 TransportConnected;
+ NvU32 RefCount;
+ NvU32 ShutDown;
+ spinlock_t lock;
+
+} AlsaTransport;
+
+
+enum {
+ /* Default Playback Path*/
+ GlobalFx_DefaultPlaybackMix = 0,
+ GlobalFx_DefaultPlaybackSplit,
+
+ /* I2S Playback Path*/
+ GlobalFx_I2sPlaybackMix,
+ GlobalFx_I2sPlaybackVolume,
+ GlobalFx_I2s,
+
+ /* I2S2 Playback Path*/
+ GlobalFx_I2s2PlaybackMix,
+ GlobalFx_I2s2PlaybackVolume,
+ GlobalFx_I2s2,
+
+ /* SPDIF Playback Path*/
+ GlobalFx_SpdifPlaybackMix,
+ GlobalFx_SpdifPlaybackVolume,
+ GlobalFx_Spdif,
+
+
+ /* Default Record Path*/
+ GlobalFx_DefaultRecordMix,
+ GlobalFx_DefaultRecordSplit,
+
+ /* I2S Record Path*/
+ GlobalFx_I2sRecordVolume,
+ GlobalFx_I2sRecordSplit,
+
+ /* I2S2 Record Path*/
+ GlobalFx_I2s2RecordVolume,
+ GlobalFx_I2s2RecordSplit,
+
+ /* SPDIF Record Path*/
+ GlobalFx_SpdifRecordVolume,
+ GlobalFx_SpdifRecordSplit,
+
+
+ /* Loopbacks*/
+ GlobalFx_I2sLoopbackSplit,
+ GlobalFx_I2s2LoopbackSplit,
+ GlobalFx_SpdifLoopbackSplit,
+
+
+ /* Music Path*/
+ GlobalFx_MusicMix,
+ GlobalFx_MusicEq,
+ GlobalFx_MusicDrc,
+ GlobalFx_MusicSpreader,
+ GlobalFx_MusicPeq,
+ GlobalFx_MusicVolume,
+ GlobalFx_MusicSplit,
+
+
+ /* Phone Path*/
+ GlobalFx_PhoneMix,
+ GlobalFx_PhoneSplit,
+
+ GlobalFx_Num
+
+};
+
+typedef struct GlobalFxListRec {
+ /* Default Playback Path*/
+ NvAudioFxObjectHandle hFx[GlobalFx_Num];
+} GlobalFxList;
+
+
+typedef struct FxNotifierRec {
+ NvAudioFxNotifierHandle hNotifier;
+ NvRmTransportHandle hTransport;
+ NvOsSemaphoreHandle hTransportSemaphore;
+ NvOsThreadHandle hTransportThread;
+
+ NvU32 Exit;
+ NvU32 Connected;
+ NvU8 RcvMessageBuffer[256];
+
+ NvAudioFxEvent Event;
+
+} FxNotifier;
+
+typedef enum {
+ NvAudioInputSelect_Record = 0,
+ NvAudioInputSelect_Loopback
+
+} InputSelection;
+
+typedef struct StandardPathRec {
+ NvAudioFxObjectHandle Stream;
+ NvAudioFxObjectHandle Src;
+ NvAudioFxObjectHandle Convert;
+ NvAudioFxObjectHandle Volume;
+ NvAudioFxObjectHandle Resize;
+
+ /*NvAudioFxVolumeDescriptor VolumeDesc;*/
+ /*NvU32 VolumeRamping;*/
+ /*StandardPosition StandardPosition;*/
+
+} StandardPath;
+
+typedef struct NvAudioBufferRec {
+ NvRmMemHandle hRmMem;
+ NvAudioFxMixBufferHandle hMixBuffer;
+ void* pVirtAddr;
+ NvU32 Size;
+} NvAudioBuffer;
+
+struct pcm_runtime_data {
+ spinlock_t lock;
+ struct task_struct *play_thread,*rec_thread;
+ int timeout;
+ int state;
+ int stream;
+ NvOsSemaphoreHandle play_sema,rec_sema;
+ struct completion play_comp;
+ struct completion rec_comp;
+ StandardPath* stdoutpath;
+ StandardPath* stdinpath;
+ NvU64 cur_pos;
+ NvU64 last_pos;
+ NvAudioFxMixBufferHandle mixer_buffer;
+};
+
+struct tegra_audio_data {
+ NvddkAudioFxFxnTable xrt_fxn;
+ NvAudioFxMixerHandle mixer_handle;
+ FxNotifier m_FxNotifier;
+ NvRmDeviceHandle m_hRm;
+ unsigned int mapped_buf_size;
+ NvAudioFxMixBufferHandle mixer_buffer[2];
+ NvRmMemHandle mem_handle[2];
+ struct mutex lock;
+};
+
+
+NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context);
+void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context);
+NvError tegra_audiofx_create_output(NvRmDeviceHandle,
+ NvAudioFxMixerHandle,
+ StandardPath*);
+NvError tegra_audiofx_destroy_output(StandardPath* pPath);
+NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice,
+ NvAudioFxMixerHandle hMixer,
+ StandardPath* pPath,
+ InputSelection InputSelect);
+NvError tegra_audiofx_destroy_input(StandardPath* pPath);
+NvError tegra_transport_init(NvddkAudioFxFxnTable* FxTransportFxFxnTable);
+void tegra_transport_deinit(void);
+
+#endif
diff --git a/sound/soc/tegra/tegra_whistler.c b/sound/soc/tegra/tegra_whistler.c
new file mode 100644
index 000000000000..bed84bb5b3f0
--- /dev/null
+++ b/sound/soc/tegra/tegra_whistler.c
@@ -0,0 +1,180 @@
+/*
+ * sound/soc/tegra/tegra_harmony.c
+ *
+ * ALSA SOC driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/clk.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <linux/io.h>
+#include "nvodm_query_discovery.h"
+#include "../codecs/wm8753.h"
+#include "tegra_transport.h"
+
+static struct platform_device *tegra_snd_device;
+NvU64 codec_guid;
+
+#define NVODM_CODEC_MAX_CLOCKS 3
+
+static unsigned int clock_frequencies[NVODM_CODEC_MAX_CLOCKS];
+
+static int set_clock_source_on_codec(NvU64 codec_guid,int IsEnable)
+{
+ const NvOdmPeripheralConnectivity *p_connectivity = NULL;
+ unsigned int clock_instances[NVODM_CODEC_MAX_CLOCKS];
+ unsigned int num_clocks;
+ p_connectivity = NvOdmPeripheralGetGuid(codec_guid);
+ if (p_connectivity == NULL)
+ return NV_FALSE;
+
+ if (IsEnable) {
+ if (!NvOdmExternalClockConfig(codec_guid, NV_FALSE,
+ clock_instances,
+ clock_frequencies, &num_clocks))
+ return NV_FALSE;
+ } else {
+ if (!NvOdmExternalClockConfig(codec_guid,
+ NV_TRUE,
+ clock_instances,
+ clock_frequencies,
+ &num_clocks));
+ return NV_FALSE;
+ }
+ return NV_TRUE;
+}
+
+static int tegra_whistler_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* Set codec DAI configuration */
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai,SND_SOC_DAIFMT_I2S);
+
+ if (ret < 0)
+ return ret;
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ WM8753_MCLK,
+ clock_frequencies[0] * 1000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ /* Disabling use of PLL1 since we are using only hifi for now
+ We may need it when we use voice */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_whistler_hifi_ops = {
+ .hw_params = tegra_whistler_hifi_hw_params,
+};
+
+static int tegra_wm8753_init(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+extern struct snd_soc_dai tegra_i2s_rpc_dai;
+extern struct snd_soc_platform tegra_soc_platform;
+
+static struct snd_soc_dai_link tegra_whistler_dai = {
+ /* Hifi Playback only for the time being */
+ .name = "WM8753",
+ .stream_name = "WM8753 HiFi",
+ .cpu_dai = &tegra_i2s_rpc_dai,
+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+ .init = tegra_wm8753_init,
+ .ops = &tegra_whistler_hifi_ops,
+};
+
+static struct snd_soc_card tegra_whistler = {
+ .name = "tegra",
+ .platform = &tegra_soc_platform,
+ .dai_link = &tegra_whistler_dai,
+ .num_links = 1,
+};
+
+static struct wm8753_setup_data whistler_setup_data = {
+ .i2c_bus = WHISTLER_CODEC_BUS,
+ .i2c_address = WHISTLER_CODEC_ADDRESS,
+};
+
+static struct snd_soc_device tegra_whistler_snd_devdata = {
+ .card = &tegra_whistler,
+ .codec_dev = &soc_codec_dev_wm8753,
+ .codec_data = &whistler_setup_data,
+};
+
+static int __init tegra_soc_init(void)
+{
+ int ret;
+ tegra_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!tegra_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(tegra_snd_device, &tegra_whistler_snd_devdata);
+ tegra_whistler_snd_devdata.dev = &tegra_snd_device->dev;
+
+ ret = platform_device_add(tegra_snd_device);
+ if (ret) {
+ snd_printk(KERN_ERR "tegra audio device could not be added \n");
+ platform_device_put(tegra_snd_device);
+ return ret;
+ }
+
+ codec_guid = NV_ODM_GUID('w','o','l','f','8','7','5','3');
+
+ set_clock_source_on_codec(codec_guid,NV_TRUE);
+ if (ret != 0)
+ platform_device_unregister(tegra_snd_device);
+
+ return ret;
+}
+
+static void __exit tegra_soc_exit(void)
+{
+ set_clock_source_on_codec(codec_guid,0);
+ platform_device_unregister(tegra_snd_device);
+}
+
+module_init(tegra_soc_init);
+module_exit(tegra_soc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Tegra SoC Sound");
+MODULE_LICENSE("GPL");