diff options
Diffstat (limited to 'sound/soc/tegra/tegra_transport.c')
-rw-r--r-- | sound/soc/tegra/tegra_transport.c | 945 |
1 files changed, 945 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra_transport.c b/sound/soc/tegra/tegra_transport.c new file mode 100644 index 000000000000..47b62c07f057 --- /dev/null +++ b/sound/soc/tegra/tegra_transport.c @@ -0,0 +1,945 @@ +/* + * sound/soc/tegra/alsa_transport.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "tegra_transport.h" +#include <linux/completion.h> + +#define transport_send_message(msg_type) \ + message.Semaphore = 0; \ + message.SendAck = 1; \ + status = NvRmTransportSendMsg(atrans->hRmTransport, \ + &message, \ + sizeof(msg_type), \ + TEGRA_TRANSPORT_SEND_TIMEOUT); \ + if (status != NvSuccess) { \ + snd_printk(KERN_ERR "NvRmTransportSendMsg failed!\n"); \ + goto EXIT_WITH_ERROR; \ + } \ + wait_for_completion(&comp); \ + + +static AlsaTransport* atrans = 0; + + +static NvAudioFxMixerHandle tegra_transport_mixer_open() +{ + NvError status = NvSuccess; + NvAudioFxMixerHandle hMixer = 0; + NvFxTransportMessageMixerOpen message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_MIXER_OPEN; + message.pPrivateData = (void*)∁ + 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; + + if ((prtd->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (prtd->play_sema != NULL)){ + NvOsSemaphoreSignal(prtd->play_sema); + } + else if(prtd->rec_sema != NULL){ + NvOsSemaphoreSignal(prtd->rec_sema); + } + } + break; + + case NvAudioFxEventStateChange: + break; + + default: + snd_printk(KERN_ERR"Unhandled event\n"); + break; + } + } + } + +EXIT: + return; +} + +GlobalFxList* tegra_audiofx_create_topology(NvAudioFxMixerHandle hMixer) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + GlobalFxList* pFxList = 0; + + memset(&connection, 0, sizeof(NvAudioFxConnectionDescriptor)); + pFxList = (GlobalFxList*)kzalloc(sizeof(GlobalFxList), GFP_KERNEL); + if (!pFxList) { + snd_printk(KERN_ERR "kzalloc failed!\n"); + goto EXIT_WITH_ERROR; + } + memset(pFxList, 0, sizeof(GlobalFxList)); + +#define audiofx_create_path(list_index, FxId) \ + pFxList->hFx[list_index] = tegra_transport_mixer_create_object(hMixer,FxId); \ + if(!pFxList->hFx[list_index]) { \ + snd_printk(KERN_ERR "audiofx_create_path failed!\n"); \ + goto EXIT_WITH_ERROR; \ + } + +#define audiofx_connect(src_index, sink_index) \ + connection.SourcePin = NvAudioFxSourcePin; \ + connection.SinkPin = NvAudioFxSinkPin; \ + connection.hSink = (NvAudioFxHandle)pFxList->hFx[sink_index]; \ + e = tegra_transport_set_property(pFxList->hFx[src_index], \ + NvAudioFxProperty_Attach, \ + sizeof(NvAudioFxConnectionDescriptor), \ + &connection); \ + if(e != NvSuccess) { \ + snd_printk(KERN_ERR "audiofx_connect failed!\n"); \ + } + + /* Default Playback Path */ + audiofx_create_path(GlobalFx_DefaultPlaybackMix, + NvAudioFxDefaultPlaybackMixId); + + audiofx_create_path(GlobalFx_DefaultPlaybackSplit, + NvAudioFxDefaultPlaybackSplitId); + + /* I2S Playback Path */ + audiofx_create_path(GlobalFx_I2sPlaybackMix, NvAudioFxI2sPlaybackMixId); + audiofx_create_path(GlobalFx_I2sPlaybackVolume, NvAudioFxVolumeId); + audiofx_create_path(GlobalFx_I2s, NvAudioFxI2sId); + + /* Default Record Path */ + audiofx_create_path(GlobalFx_DefaultRecordMix, + NvAudioFxDefaultRecordMixId); + + audiofx_create_path(GlobalFx_DefaultRecordSplit, + NvAudioFxDefaultRecordSplitId); + + /* I2S Record Path */ + audiofx_create_path(GlobalFx_I2sRecordVolume, NvAudioFxVolumeId); + audiofx_create_path(GlobalFx_I2sRecordSplit, NvAudioFxI2sRecordSplitId); + +#if (0) + /* Simple Playback */ + audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_I2s); +#else + /* Playback */ + audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_DefaultPlaybackSplit); + audiofx_connect(GlobalFx_DefaultPlaybackSplit, GlobalFx_I2sPlaybackMix); + + /* Wire SPDIF on HDMI connection and unwire it when not connected. */ + audiofx_connect(GlobalFx_I2sPlaybackMix, GlobalFx_I2sPlaybackVolume); + audiofx_connect(GlobalFx_I2sPlaybackVolume, GlobalFx_I2s); +#endif + + /*Record*/ + audiofx_connect(GlobalFx_I2s, GlobalFx_I2sRecordVolume); + audiofx_connect(GlobalFx_I2sRecordVolume, GlobalFx_I2sRecordSplit); + + audiofx_connect(GlobalFx_I2sRecordSplit, GlobalFx_DefaultRecordMix); + audiofx_connect(GlobalFx_DefaultRecordMix, GlobalFx_DefaultRecordSplit); + + goto EXIT; + +EXIT_WITH_ERROR: + /* tegra_audiofx_destroy_topology(pFxList); */ + pFxList = 0; + +EXIT: + return pFxList; +} + +void tegra_audiofx_destroy_topology(GlobalFxList* pFxList) +{ + NvS32 i; + + if (pFxList) { + for (i = 0; i < GlobalFx_Num; i++) { + if (pFxList->hFx[i]) { + /* + TODO + NvddkAudioFxMixerDestroyObject(pFxList->hFx[i]); + Add code to destory + */ + } + } + + kfree(pFxList); + } +} + +NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context) +{ + NvAudioFxMixerHandle m_hMixer = + (NvAudioFxMixerHandle)audio_context->mixer_handle; + + FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier; + NvRmDeviceHandle m_hRm = (NvRmDeviceHandle)audio_context->m_hRm; + NvError e = NvSuccess; + NvAudioFxNotifierConnectionDescriptor connectionDesciptor; + NvAudioFxMessage message; + + memset(&connectionDesciptor, + 0, + sizeof(NvAudioFxNotifierConnectionDescriptor)); + memset(&message, 0, sizeof(NvAudioFxMessage)); + + e = NvOsSemaphoreCreate(&m_FxNotifier->hTransportSemaphore, 0); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + m_FxNotifier->hNotifier = + (NvAudioFxNotifierHandle)tegra_transport_mixer_create_object( + m_hMixer, + NvAudioFxNotifierId); + if (!m_FxNotifier->hNotifier) { + snd_printk(KERN_ERR "transport_mixer_create_object failed!\n"); + goto EXIT_WITH_ERROR; + } + + e = NvRmTransportOpen(m_hRm, + 0, + m_FxNotifier->hTransportSemaphore, + &m_FxNotifier->hTransport); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvRmTransportOpen failed!\n"); + goto EXIT_WITH_ERROR; + } + + NvRmTransportGetPortName(m_FxNotifier->hTransport, + (NvU8*)&connectionDesciptor.PortName, + sizeof(NvU8) * 16); + + e = NvOsThreadCreate(tegra_audiofx_notifier_thread, + audio_context, + &m_FxNotifier->hTransportThread); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvOsThreadCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + e = tegra_transport_set_property( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier, + NvAudioFxNotifierProperty_Connect, + sizeof(NvAudioFxNotifierConnectionDescriptor), + &connectionDesciptor); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + tegra_audiofx_destroyfx(audio_context); + +EXIT: + return e; +} + +void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context) +{ + FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier; + + if (m_FxNotifier->Connected) { + m_FxNotifier->Exit = 1; + NvOsSemaphoreSignal(m_FxNotifier->hTransportSemaphore); + NvOsThreadJoin(m_FxNotifier->hTransportThread); + m_FxNotifier->hTransportThread = 0; + tegra_transport_set_property( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier, + NvAudioFxNotifierProperty_Disconnect, + 0, + 0); + } + + if (m_FxNotifier->hTransport) { + NvRmTransportClose(m_FxNotifier->hTransport); + m_FxNotifier->hTransport = 0; + } + + if (m_FxNotifier->hNotifier) { + tegra_transport_mixer_destroy_object( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier); + m_FxNotifier->hNotifier = 0; + } + + if (m_FxNotifier->hTransportSemaphore) { + NvOsSemaphoreDestroy(m_FxNotifier->hTransportSemaphore); + m_FxNotifier->hTransportSemaphore = 0; + } + + return; +} + +#define audiofx_create_object(path_object, FxId) \ + path_object = tegra_transport_mixer_create_object(hMixer, FxId); \ + if(!path_object) { \ + snd_printk(KERN_ERR "audiofx_create_object failed!"); \ + } + +#define audiofx_path_connect(path_object, sink_object) \ + connection.SourcePin = NvAudioFxSourcePin; \ + connection.SinkPin = NvAudioFxSinkPin; \ + connection.hSink = (NvAudioFxHandle)sink_object; \ + e = tegra_transport_set_property(path_object, \ + NvAudioFxProperty_Attach, \ + sizeof(NvAudioFxConnectionDescriptor), \ + &connection); \ + if(e != NvSuccess) { \ + snd_printk(KERN_ERR "audiofx_path_connect failed!"); \ + goto EXIT_WITH_ERROR; \ + } + +NvError tegra_audiofx_create_output(NvRmDeviceHandle hRmDevice, + NvAudioFxMixerHandle hMixer, + StandardPath* pPath) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + + /* Standard Output + [stream]->[SRC]->[Convert]->[Resize]->[Volume]->Default Output + */ + + memset(pPath, 0, sizeof(StandardPath)); + + audiofx_create_object(pPath->Stream, NvAudioFxStreamId); + audiofx_create_object(pPath->Src, NvAudioFxSrcId); + audiofx_create_object(pPath->Convert, NvAudioFxConvertId); + audiofx_create_object(pPath->Resize, NvAudioFxResizeId); + audiofx_create_object(pPath->Volume, NvAudioFxVolumeId); + + audiofx_path_connect(pPath->Stream, pPath->Src); + audiofx_path_connect(pPath->Src, pPath->Convert); + audiofx_path_connect(pPath->Convert, pPath->Resize); + audiofx_path_connect(pPath->Resize, pPath->Volume); + + connection.SourcePin = NvAudioFxSourcePin; + connection.hSink = 0; + connection.SinkPin = NvAudioFxSinkPin; + e = tegra_transport_set_property(pPath->Volume, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + + tegra_audiofx_destroy_output(pPath); + +EXIT: + + return e; +} + +NvError tegra_audiofx_destroy_output(StandardPath* pPath) +{ + if (pPath->Volume) { + tegra_transport_mixer_destroy_object(pPath->Volume); + pPath->Volume = 0; + } + + if (pPath->Resize) { + tegra_transport_mixer_destroy_object(pPath->Resize); + pPath->Resize = 0; + } + + if (pPath->Convert) { + tegra_transport_mixer_destroy_object(pPath->Convert); + pPath->Convert = 0; + } + + if (pPath->Src) { + tegra_transport_mixer_destroy_object(pPath->Src); + pPath->Src = 0; + } + + if (pPath->Stream) { + tegra_transport_mixer_destroy_object(pPath->Stream); + pPath->Stream = 0; + } + + return NvSuccess; +} + +NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice, + NvAudioFxMixerHandle hMixer, + StandardPath* pInput, + InputSelection InputSelect) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + + /* + Standard Input (record or loopback) + + +--------+ (2) +--------+ (3) +---------+ (4) +--------+ (5) + +--| Stream |<----| Resize |<-----| Convert |<-----| SRC |<--From I2S + | | | | |<--+ | (opt.) | | (opt.) | + | +--------+ +--------+ | +---------+ +--------+ + | (1) | + +------------------------------+ + + */ + + memset(pInput, 0, sizeof(StandardPath)); + + audiofx_create_object(pInput->Stream, NvAudioFxStreamId); + audiofx_create_object(pInput->Resize,NvAudioFxResizeId); + audiofx_create_object(pInput->Src,NvAudioFxSrcId); + audiofx_create_object(pInput->Convert,NvAudioFxConvertId); + + /* Wire 1 */ + connection.SourcePin = NvAudioFxSourcePin; + connection.hSink = (NvAudioFxHandle)pInput->Resize; + connection.SinkPin = NvAudioFxCopySinkPin; + e = tegra_transport_set_property(pInput->Stream, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + audiofx_path_connect(pInput->Resize, pInput->Stream); + + /* Wire 5 */ + connection.SourcePin = (InputSelect == NvAudioInputSelect_Record) ? + NvAudioFxSourcePin : NvAudioFxLoopbackPin; + connection.hSink = (NvAudioFxHandle)pInput->Resize; + connection.SinkPin = NvAudioFxSinkPin; + e = tegra_transport_set_property(0, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + + tegra_audiofx_destroy_input(pInput); + +EXIT: + + return e; +} + + +NvError tegra_audiofx_destroy_input(StandardPath* pInput) +{ + if (pInput->Src) { + tegra_transport_mixer_destroy_object(pInput->Src); + pInput->Src = 0; + } + + if (pInput->Convert) { + tegra_transport_mixer_destroy_object(pInput->Convert); + pInput->Convert = 0; + } + + if (pInput->Resize) { + tegra_transport_mixer_destroy_object(pInput->Resize); + pInput->Resize = 0; + } + + if (pInput->Stream) { + tegra_transport_mixer_destroy_object(pInput->Stream); + pInput->Stream = 0; + } + + return NvSuccess; +} + |