diff options
author | Gary King <gking@nvidia.com> | 2010-05-28 16:38:08 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-05-28 17:20:19 -0700 |
commit | 00686bd386d93d4b97016677f4cbea0045c07c8f (patch) | |
tree | e92f58c20691f65196b25230c7c4cb1131e9d52c /sound | |
parent | a4b16260c8dbc8d9834c15a6b983917a2f5776f0 (diff) |
[alsa] add ASoC device and stub codec for tegra SoCs
Change-Id: I34c6607d82e890d57180d4f97aa1537cf2aba4d8
Reviewed-on: http://git-master/r/1828
Reviewed-by: Gary King <gking@nvidia.com>
Tested-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/tegra/Kconfig | 20 | ||||
-rw-r--r-- | sound/soc/tegra/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_codec_rpc.c | 180 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_i2s.c | 109 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_pcm_rpc.c | 877 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_sndfx.h | 623 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_audio.c | 171 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_transport.c | 840 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_transport.h | 431 |
11 files changed, 3257 insertions, 0 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index b1749bc67979..5a7c4e7320b0 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -35,6 +35,7 @@ source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/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 0c5eac01bf2e..a36fd1476860 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -13,4 +13,5 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += s3c24xx/ 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/tegra/Kconfig b/sound/soc/tegra/Kconfig new file mode 100644 index 000000000000..db522f3f8265 --- /dev/null +++ b/sound/soc/tegra/Kconfig @@ -0,0 +1,20 @@ +config TEGRA_SND_SOC + tristate "Soc alsa driver for Tegra" + depends on ARCH_TEGRA + select TEGRA_PCM + select TEGRA_I2S + select TEGRA_GENERIC_CODEC + 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_GENERIC_CODEC + depends on TEGRA_SND_SOC + tristate "Tegra Generic Audio Codec" diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile new file mode 100644 index 000000000000..81ef7784d5de --- /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_GENERIC_CODEC) += tegra_soc_audio.o +obj-$(CONFIG_TEGRA_GENERIC_CODEC) += tegra_codec_rpc.o
\ No newline at end of file diff --git a/sound/soc/tegra/tegra_codec_rpc.c b/sound/soc/tegra/tegra_codec_rpc.c new file mode 100644 index 000000000000..ea97d0f91337 --- /dev/null +++ b/sound/soc/tegra/tegra_codec_rpc.c @@ -0,0 +1,180 @@ +/* + * sound/soc/tegra/tegra_codec_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 <linux/module.h> +#include <linux/moduleparam.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/initval.h> + +#include "tegra_transport.h" + +static int tegra_generic_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int tegra_generic_codec_mute(struct snd_soc_dai *dai, int mute) +{ + return 0; +} + +static int tegra_generic_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + return 0; +} + +static int tegra_generic_codec_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + return 0; +} + +static int tegra_generic_codec_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + return 0; +} + +static int tegra_generic_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + return 0; +} + +static struct snd_soc_dai_ops dit_stub_ops = { + .hw_params = tegra_generic_codec_hw_params, + .digital_mute = tegra_generic_codec_mute, + .set_fmt = tegra_generic_codec_set_dai_fmt, + .set_clkdiv = tegra_generic_codec_set_dai_clkdiv, + .set_pll = tegra_generic_codec_set_dai_pll, + .set_sysclk = tegra_generic_codec_set_dai_sysclk, +}; + +struct snd_soc_dai dit_stub_dai = { + .name = "tegra-codec-rpc", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TEGRA_SAMPLE_RATES, + .formats = TEGRA_SAMPLE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TEGRA_SAMPLE_RATES, + .formats = TEGRA_SAMPLE_FORMATS, + }, + .ops = &dit_stub_ops, +}; +EXPORT_SYMBOL_GPL(dit_stub_dai); + +static int __init dit_modinit(void) +{ + return snd_soc_register_dai(&dit_stub_dai); +} + +static void __exit dit_exit(void) +{ + snd_soc_unregister_dai(&dit_stub_dai); +} + +module_init(dit_modinit); +module_exit(dit_exit); + +static int codec_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (!socdev->card->codec) + return -ENOMEM; + + codec = socdev->card->codec; + mutex_init(&codec->mutex); + + codec->name = "tegra-generic-codec"; + codec->owner = THIS_MODULE; + codec->dai = &dit_stub_dai; + codec->num_dai = 1; + codec->write = NULL; + codec->read = NULL; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + /* Register PCMs. */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "codec: failed to create pcms\n"); + goto pcm_err; + } + /* Register Card. */ + ret = snd_soc_init_card(socdev); + if (ret < 0) { + printk(KERN_ERR "codec: failed to register card\n"); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); +pcm_err: + kfree(socdev->card->codec); + + return ret; +} + +static int codec_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (!codec) + return 0; + + snd_soc_free_pcms(socdev); + kfree(socdev->card->codec); + + return 0; +} + +#define codec_soc_suspend NULL +#define codec_soc_resume NULL + +struct snd_soc_codec_device soc_codec_dev_tegra_generic_codec = { + .probe = codec_soc_probe, + .remove = codec_soc_remove, + .suspend = codec_soc_suspend, + .resume = codec_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_tegra_generic_codec); + +/* Module information */ +MODULE_DESCRIPTION("Tegra Codec RPC Interface"); +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..2edb1033a399 --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.c @@ -0,0 +1,109 @@ +/* + * 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 11025: + case 16000: + case 22050: + case 24000: + 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; +} + +static struct snd_soc_dai_ops tegra_dai_ops = { + .hw_params = tegra_i2s_rpc_hw_params, +}; + +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_SAMPLE_RATES, + .formats = TEGRA_SAMPLE_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = TEGRA_SAMPLE_RATES, + .formats = TEGRA_SAMPLE_FORMATS, + }, + .ops = &tegra_dai_ops, +}; +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..6b486d10110a --- /dev/null +++ b/sound/soc/tegra/tegra_pcm_rpc.c @@ -0,0 +1,877 @@ +/* + * 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 | SNDRV_PCM_INFO_MMAP |\ + SNDRV_PCM_INFO_MMAP_VALID, + .rates = TEGRA_SAMPLE_RATES, + .formats = TEGRA_SAMPLE_FORMATS, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 32*1024, + .period_bytes_min = TEGRA_DEFAULT_BUFFER_SIZE, + .period_bytes_max = TEGRA_DEFAULT_BUFFER_SIZE, + .periods_min = 4, + .periods_max = 8, + .fifo_size = 8, +}; + + +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->thread_comp); + + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); + + for (;;) { + 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: + state = NvAudioFxState_Stop; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdoutpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + prtd->state = NVALSA_INVALID_STATE; + default: + ; + } + + if (kthread_should_stop()) + break; + + if ((prtd->audiofx_frames < runtime->control->appl_ptr) && + (state != SNDRV_PCM_TRIGGER_STOP)) { + memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); + + size = TEGRA_DEFAULT_BUFFER_SIZE; + if ((offset + size) > rtbuffersize) { + 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; + abd.Format.ValidBitsPerSample = 0; + + e = tegra_snd_cx->xrt_fxn.StreamAddBuffer( + (NvAudioFxStreamHandle)prtd->stdoutpath->Stream, + &abd); + buffer_in_queue++; + offset += size; + if (offset >= rtbuffersize) + offset =0; + + prtd->audiofx_frames += bytes_to_frames(runtime, + size); + } + + if (buffer_in_queue == 0) { + DEFINE_WAIT(wq); + prepare_to_wait(&prtd->buf_wait, &wq, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&prtd->buf_wait, &wq); + continue; + } + + if ((buffer_to_prime == buffer_in_queue) || + (prtd->audiofx_frames >= runtime->control->appl_ptr)) { + down(&prtd->buf_done_sem); + + buffer_in_queue--; + + if ((frames_to_bytes(runtime, prtd->cur_pos) + + TEGRA_DEFAULT_BUFFER_SIZE) > rtbuffersize) { + size = rtbuffersize - + frames_to_bytes(runtime, prtd->cur_pos); + } else { + size = TEGRA_DEFAULT_BUFFER_SIZE; + } + + prtd->cur_pos += bytes_to_frames(runtime, 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; + } + } + } + while (buffer_in_queue > 0) { + down(&prtd->buf_done_sem); + buffer_in_queue--; + } + 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->thread_comp); + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); + + while (!prtd->shutdown_thrd ) { + 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.Format.ValidBitsPerSample = 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 > 0) { + down(&prtd->buf_done_sem); + 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: + ; + } + + if ((state == NvAudioFxState_Run) && + (buffer_in_queue < buffer_to_prime)) { + memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); + + size = TEGRA_DEFAULT_BUFFER_SIZE; + if ((offset + size) > rtbuffersize) { + 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) && + ((runtime->status->hw_ptr - runtime->control->appl_ptr) < + (runtime->buffer_size -runtime->period_size))) { + down(&prtd->buf_done_sem); + + buffer_in_queue--; + + if ((frames_to_bytes(runtime, prtd->cur_pos) + + TEGRA_DEFAULT_BUFFER_SIZE) > rtbuffersize) { + size = rtbuffersize - + frames_to_bytes(runtime, prtd->cur_pos); + } else { + size = TEGRA_DEFAULT_BUFFER_SIZE; + } + + prtd->cur_pos += bytes_to_frames(runtime, 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; + } + } + } +EXIT: + + while (!kthread_should_stop()) { + } + + return 0; +} + +static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct pcm_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + int state = prtd->state; + + prtd->state = cmd; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->cur_pos = 0; + prtd->last_pos = 0; + prtd->audiofx_frames = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->timeout = PLAY_TIMEOUT; + else + prtd->timeout = REC_TIMEOUT; + + if (state == NVALSA_INVALID_STATE) + complete(&prtd->thread_comp); + else + up(&prtd->buf_done_sem); + 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"); + + prtd->state = SNDRV_PCM_TRIGGER_STOP; + + if (completion_done(&prtd->thread_comp) == 0) + complete(&prtd->thread_comp); + + wake_up_all(&prtd->buf_wait); + + if (prtd->play_thread) + kthread_stop(prtd->play_thread); + + if (prtd->rec_thread) + kthread_stop(prtd->rec_thread); + + prtd->shutdown_thrd = 1; + up(&prtd->buf_done_sem); + + 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; + prtd->shutdown_thrd = 0; + + ret = init_mixer(substream); + if (ret) + goto fail; + + init_completion(&prtd->thread_comp); + init_waitqueue_head(&prtd->buf_wait); + sema_init(&prtd->buf_done_sem, 0); + + 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); + + 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); + + 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 int tegra_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + int err = 0; + int size = 0; + char *vmalloc_area_ptr = NULL; + unsigned long start = 0; + unsigned long pfn = 0; + + start = vma->vm_start; + vmalloc_area_ptr = substream->dma_buffer.area; + size = vma->vm_end - vma->vm_start; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + while (size > 0) { + pfn = vmalloc_to_pfn(vmalloc_area_ptr); + err = io_remap_pfn_range(vma, start, pfn, + PAGE_SIZE, vma->vm_page_prot); + if (err < 0) { + snd_printk(KERN_ERR "io_remap_pfn_range failed \n"); + return err; + } + start += PAGE_SIZE; + vmalloc_area_ptr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return err; +} + +static int tegra_pcm_ack(struct snd_pcm_substream *substream) +{ + struct pcm_runtime_data *prtd = substream->runtime->private_data; + + wake_up(&prtd->buf_wait); + 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, + .mmap = tegra_pcm_mmap, + .ack = tegra_pcm_ack, +}; + +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, + PAGE_SIZE, + 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..5b59b7587a82 --- /dev/null +++ b/sound/soc/tegra/tegra_sndfx.h @@ -0,0 +1,623 @@ +/* + * 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 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 (0x11100000) +#define NvAudioFxSpdifId (0x11100001) +#define NvAudioFxSilenceId (0x11100002) +#define NvAudioFxI2s1Id (0x11110000) +#define NvAudioFxI2s2Id (0x11110001) +#define NvAudioFxPlaybackMixId (0x11300000) +#define NvAudioFxPlaybackSplitId (0x11300001) +#define NvAudioFxRecordMixId (0x11300002) +#define NvAudioFxRecordSplitId (0x11300003) +#define NvAudioFxSpdifPlaybackMixId (0x11300004) +#define NvAudioFxSpdifRecordSplitId (0x11300005) +#define NvAudioFxSpdifLoopbackSplitId (0x11300006) +#define NvAudioFxSpdifVolumeId (0x1130000c) +#define NvAudioFxMusicMixId (0x11300007) +#define NvAudioFxMusicSplitId (0x11300008) +#define NvAudioFxRingtoneMixId (0x11300009) +#define NvAudioFxMusicVolumeId (0x1130000a) +#define NvAudioFxRingtoneVolumeId (0x1130000b) +#define NvAudioFxI2s1PlaybackMixId (0x11310000) +#define NvAudioFxI2s1RecordSplitId (0x11310001) +#define NvAudioFxI2s1LoopbackSplitId (0x11310002) +#define NvAudioFxI2s1VolumeId (0x11310003) +#define NvAudioFxI2s2PlaybackMixId (0x11311000) +#define NvAudioFxI2s2RecordSplitId (0x11311001) +#define NvAudioFxI2s2LoopbackSplitId (0x11311002) +#define NvAudioFxI2s2VolumeId (0x11311003) + +// +// 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 NvS32 NvAudioFxProperty; +#define NvAudioFxProperty_Attach (0x1000) +#define NvAudioFxProperty_Detach (0x1010) +#define NvAudioFxProperty_Format (0x1020) +#define NvAudioFxProperty_Method (0x1030) +#define NvAudioFxProperty_PowerState (0x1040) +#define NvAudioFxProperty_SampleRate (0x1050) +#define NvAudioFxProperty_State (0x1060) +#define NvAudioFxPinProperty_Format (0x2000) +#define NvAudioFxDrcProperty_Drc (0x3000) +#define NvAudioFxEqProperty_Eq (0x4000) +#define NvAudioFxI2sProperty_AllocChannel (0x4a01) +#define NvAudioFxI2sProperty_InputAvailable (0x4a02) +#define NvAudioFxI2sProperty_InputDisable (0x4a04) +#define NvAudioFxI2sProperty_InputEnable (0x4a03) +#define NvAudioFxI2sProperty_InputSelect (0x4a05) +#define NvAudioFxI2sProperty_OutputAvailable (0x4a06) +#define NvAudioFxI2sProperty_OutputDisable (0x4a08) +#define NvAudioFxI2sProperty_OutputEnable (0x4a07) +#define NvAudioFxI2sProperty_OutputSelect (0x4a09) +#define NvAudioFxI2sProperty_IoDeviceVolume (0x4a0a) +#define NvAudioFxIoProperty_AddEvent (0x5000) +#define NvAudioFxIoProperty_Position (0x5010) +#define NvAudioFxIoProperty_RemoveEvent (0x5020) +#define NvAudioFxIoProperty_SetMappedPositionBuffer (0x5030) +#define NvAudioFxMixerProperty_ProcessBufferSize (0x6000) +#define NvAudioFxMixerProperty_ModeAvailable (0x6001) +#define NvAudioFxMixerProperty_ModeDisable (0x6002) +#define NvAudioFxMixerProperty_ModeEnable (0x6003) +#define NvAudioFxMixerProperty_ModeSelect (0x6004) +#define NvAudioFxMixProperty_Headroom (0x7000) +#define NvAudioFxNotifierProperty_Connect (0x8000) +#define NvAudioFxNotifierProperty_Disconnect (0x8010) +#define NvAudioFxPeqProperty_Peq (0x9000) +#define NvAudioFxResizeProperty_OutputSize (0xa000) +#define NvAudioFxSpreaderProperty_Spreader (0xb000) +#define NvAudioFxSrcProperty_SampleRateShift (0xc000) +#define NvAudioFxVolumeProperty_Ramping (0xd000) +#define NvAudioFxVolumeProperty_Volume (0xd010) + +// Description of the NvAudioFxProperty_Attach and +// NvAudioFxProperty_Detach properties. + +typedef struct NvAudioFxConnectionDescriptorRec +{ + NvAudioFxHandle hSource; + 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; + NvU32 ValidBitsPerSample; +} NvAudioFxFormat; + +// Description of the Mode property. + +typedef NvS32 NvAudioFxMode; +#define NvAudioFxMode_Normal (0x0) +#define NvAudioFxMode_Bluetooth (0x1) +#define NvAudioFxMode_Ringtone (0x2) +#define NvAudioFxMode_InCall (0x4) +#define NvAudioFxMode_Radio (0x8) +#define NvAudioFxMode_All (0xffffffff) + +// Description of the position property. + +typedef NvU64 NvAudioFxPosition; + +// Description of supported power states in AudioFx. + +typedef NvS32 NvAudioFxPowerState; +#define NvAudioFxPowerState_Low (0x1000) +#define NvAudioFxPowerState_High (0x1010) +#define NvAudioFxPowerState_Full (0x1020) +#define NvAudioFxPowerState_Ready (0x1030) +#define NvAudioFxPowerState_Off (0x1040) + +// Description of the supported priority states. + +typedef NvS32 NvAudioFxPriority; +#define NvAudioFxPriority_Normal (0x1000) +#define NvAudioFxPriority_Medium (0x1010) +#define NvAudioFxPriority_High (0x1020) +#define NvAudioFxPriority_Critical (0x1030) + +// Description of the NvAudioFxProperty_State property. + +typedef NvS32 NvAudioFxState; +#define NvAudioFxState_Uninitialized (0x1000) +#define NvAudioFxState_Initialized (0x1010) +#define NvAudioFxState_Stop (0x1020) +#define NvAudioFxState_Run (0x1030) +#define NvAudioFxState_Pause (0x1040) +#define NvAudioFxState_Disable (0x1050) +#define NvAudioFxState_EndOfStream (0x1060) +#define NvAudioFxState_Locked (0x1070) + +// 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) + +// AudioFx Default Volume Settings +#define NvAudioFxVolumeDefault (256) +#define NvAudioFxVolumeMax (1024) + +typedef struct NvAudioFxEqDescriptorRec +{ + NvS32 dBGain[NvAudioFxEqNumChannels][NvAudioFxEqNumFilters]; + +} NvAudioFxEqDescriptor; + +// Audio Spreader information. + +typedef struct NvAudioFxSpreaderDescriptorRec +{ + NvU32 SpeakerWidth; +} NvAudioFxSpreaderDescriptor; + +typedef enum +{ + NvAudioFxIirFilter_Undefined, + NvAudioFxIirFilter_Bandpass, + NvAudioFxIirFilter_Highpass, + NvAudioFxIirFilter_Lowpass, + NvAudioFxIirFilter_Num, + NvAudioFxIirFilter_Force32 = 0x7FFFFFFF +} NvAudioFxIirFilter; + +// IO Devices + +typedef NvS32 NvAudioFxIoDevice; + +// Default is configurable based on the device. +#define NvAudioFxIoDevice_Default (0x0) +#define NvAudioFxIoDevice_All (0xffffffff) + +// Inputs +#define NvAudioFxIoDevice_BuiltInMic (0x1) +#define NvAudioFxIoDevice_Mic (0x2) +#define NvAudioFxIoDevice_LineIn (0x4) + +// Outputs +#define NvAudioFxIoDevice_BuiltInSpeaker (0x100) +#define NvAudioFxIoDevice_EarSpeaker (0x200) +#define NvAudioFxIoDevice_LineOut (0x400) +#define NvAudioFxIoDevice_HeadphoneOut (0x800) + +// Both +#define NvAudioFxIoDevice_Aux (0x10000) +#define NvAudioFxIoDevice_Phone (0x20000) +#define NvAudioFxIoDevice_Radio (0x40000) +#define NvAudioFxIoDevice_Bluetooth (0x80000) + +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 IoDeviceVolume property. + +typedef struct NvAudioFxIoDeviceVolumeDescriptorRec +{ + NvAudioFxIoDevice IoDevice; + NvAudioFxVolumeDescriptor Volume; +} NvAudioFxIoDeviceVolumeDescriptor; + +// 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 NvAudioFxI2sProperty_AllocChannel property. + +typedef struct NvAudioFxI2sChannelDescriptorRec +{ + NvAudioFxPin Pin; + NvU32 Id; +} NvAudioFxI2sChannelDescriptor; + +// 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 NvAudioFxEventIoChange (0x20) +#define NvAudioFxEventControlChange (0x40) +#define NvAudioFxEventAll (0xffffffff) + +typedef struct NvAudioFxMessageRec +{ + NvAudioFxEvent Event; + NvAudioFxHandle hFx; + void* pContext; +} NvAudioFxMessage; + +typedef struct NvAudioFxBufferDoneMessageRec +{ + NvAudioFxMessage m; + NvAudioFxPosition Position; +} NvAudioFxBufferDoneMessage; + +typedef struct NvAudioFxControlChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxProperty Property; +} NvAudioFxControlChangeMessage; + +typedef struct NvAudioFxIoDeviceControlChangeMessageRec +{ + NvAudioFxControlChangeMessage m; + NvAudioFxIoDevice IoDevice; +} NvAudioFxIoDeviceControlChangeMessage; + +typedef struct NvAudioFxIoDeviceVolumeControlChangeMessageRec +{ + NvAudioFxControlChangeMessage m; + NvAudioFxIoDeviceVolumeDescriptor idv; +} NvAudioFxIoDeviceVolumeControlChangeMessage; + +typedef struct NvAudioFxModeControlChangeMessageRec +{ + NvAudioFxControlChangeMessage m; + NvAudioFxMode Mode; +} NvAudioFxModeControlChangeMessage; + +typedef struct NvAudioFxStateChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxState State; +} NvAudioFxStateChangeMessage; + +typedef struct NvAudioFxFormatChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxFormat Format; +} NvAudioFxFormatChangeMessage; + +typedef struct NvAudioFxPowerStateChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxPowerState PowerState; +} NvAudioFxPowerStateChangeMessage; + +typedef struct NvAudioFxIoChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxProperty Property; + NvAudioFxConnectionDescriptor Connection; +} NvAudioFxIoChangeMessage; + + /** + * @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_soc_audio.c b/sound/soc/tegra/tegra_soc_audio.c new file mode 100644 index 000000000000..19ca788d86fd --- /dev/null +++ b/sound/soc/tegra/tegra_soc_audio.c @@ -0,0 +1,171 @@ +/* + * sound/soc/tegra/tegra_whistler.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. + */ + +#define NV_DEBUG 0 + +#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 "tegra_transport.h" + +struct codec_setup_data { + unsigned dem0_pin; + unsigned dem1_pin; + unsigned pdad_pin; + unsigned pdda_pin; +}; + +extern struct snd_soc_codec_device soc_codec_dev_tegra_generic_codec; +extern struct snd_soc_dai dit_stub_dai; + +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; + + 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 = { + .name = "tegra-generic-codec", + .stream_name = "tegra-codec-rpc", + .cpu_dai = &tegra_i2s_rpc_dai, + .codec_dai = &dit_stub_dai, + .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 snd_soc_device tegra_whistler_snd_devdata = { + .card = &tegra_whistler, + .codec_dev = &soc_codec_dev_tegra_generic_codec, +}; + +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"); diff --git a/sound/soc/tegra/tegra_transport.c b/sound/soc/tegra/tegra_transport.c new file mode 100644 index 000000000000..b7c8e71d1992 --- /dev/null +++ b/sound/soc/tegra/tegra_transport.c @@ -0,0 +1,840 @@ +/* + * sound/soc/tegra/tegra_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(void) +{ + NvError status = NvSuccess; + NvAudioFxMixerHandle hMixer = 0; + NvFxTransportMessageMixerOpen message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_MIXER_OPEN; + message.pPrivateData = (void*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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*)∁ + 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; + + up(&prtd->buf_done_sem); + } + break; + + case NvAudioFxEventStateChange: + break; + + default: + snd_printk(KERN_ERR"Unhandled event\n"); + break; + } + } + } + +EXIT: + return; +} + +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.hSource = (NvAudioFxHandle)path_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.hSource = (NvAudioFxHandle)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.hSource = (NvAudioFxHandle)(pInput->Stream); + 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); + audiofx_path_connect(pInput->Convert, pInput->Resize); + audiofx_path_connect(pInput->Src, pInput->Convert); + + /* Wire 5 */ + connection.hSource = 0; + connection.SourcePin = (InputSelect == NvAudioInputSelect_Record) ? + NvAudioFxSourcePin : NvAudioFxLoopbackPin; + connection.hSink = (NvAudioFxHandle)pInput->Src; + 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..0093fef6383b --- /dev/null +++ b/sound/soc/tegra/tegra_transport.h @@ -0,0 +1,431 @@ +/* + * sound/soc/tegra/tegra_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 8192 +#define NVALSA_INVALID_STATE -1 +#define TEGRA_SAMPLE_RATES (SNDRV_PCM_RATE_8000_48000) +#define TEGRA_SAMPLE_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_BE) + +#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; + int shutdown_thrd; + unsigned int audiofx_frames; + struct completion thread_comp; + wait_queue_head_t buf_wait; + struct semaphore buf_done_sem; + StandardPath* stdoutpath; + StandardPath* stdinpath; + u64 cur_pos; + u64 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 |