diff options
Diffstat (limited to 'sound/soc/tegra/tegra_pcm_rpc.c')
-rw-r--r-- | sound/soc/tegra/tegra_pcm_rpc.c | 850 |
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"); |