summaryrefslogtreecommitdiff
path: root/sound/soc/tegra/tegra_pcm_rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra_pcm_rpc.c')
-rw-r--r--sound/soc/tegra/tegra_pcm_rpc.c850
1 files changed, 850 insertions, 0 deletions
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");