summaryrefslogtreecommitdiff
path: root/sound/soc/tegra/tegra_transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra_transport.c')
-rw-r--r--sound/soc/tegra/tegra_transport.c945
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*)&comp;
+ message.phMixer = &hMixer;
+
+ transport_send_message(NvFxTransportMessageMixerOpen);
+
+EXIT_WITH_ERROR:
+ return hMixer;
+}
+
+static void tegra_transport_mixer_close(NvAudioFxMixerHandle hMixer)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageMixerClose message;
+ struct completion comp;
+
+ init_completion(&comp);
+ if (hMixer == NULL) {
+ snd_printk(KERN_ERR "NULL NvAudioFxMixerHandle!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ message.Message = NVFXTRANSPORT_MESSAGE_MIXER_CLOSE;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+
+ transport_send_message(NvFxTransportMessageMixerClose);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvAudioFxObjectHandle tegra_transport_mixer_create_object(
+ NvAudioFxMixerHandle hMixer,
+ NvObjectId Id)
+{
+ NvError status = NvSuccess;
+ NvAudioFxObjectHandle hObject = 0;
+ NvFxTransportMessageCreateObject message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_CREATE_OBJECT;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+ message.Id = Id;
+ message.phObject = &hObject;
+
+ transport_send_message(NvFxTransportMessageCreateObject);
+
+EXIT_WITH_ERROR:
+ return hObject;
+}
+
+static void tegra_transport_mixer_destroy_object(NvAudioFxObjectHandle hObject)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageDestroyObject message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+
+ transport_send_message(NvFxTransportMessageDestroyObject);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvAudioFxMixBufferHandle tegra_transport_mixer_map_buffer(
+ NvAudioFxMixerHandle hMixer,
+ NvU32 NvRmMemHandleId,
+ NvU32 Offset,
+ NvU32 Size)
+{
+ NvError status = NvSuccess;
+ NvAudioFxMixBufferHandle mixer_buffer = 0;
+ NvFxTransportMessageMapBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_MAP_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hMixer = hMixer;
+ message.NvRmMemHandleId = NvRmMemHandleId;
+ message.Offset = Offset;
+ message.Size = Size;
+ message.phMixBuffer = &mixer_buffer;
+
+ transport_send_message(NvFxTransportMessageMapBuffer);
+EXIT_WITH_ERROR:
+ return mixer_buffer;
+}
+
+
+static void tegra_transport_mixer_unmap_buffer(
+ NvAudioFxMixBufferHandle mixer_buffer)
+{
+ NvError status = NvSuccess;
+ NvFxTransportMessageUnmapBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hMixBuffer = mixer_buffer;
+
+ transport_send_message(NvFxTransportMessageUnmapBuffer);
+
+EXIT_WITH_ERROR:
+ return;
+}
+
+static NvError tegra_transport_get_property(NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty)
+{
+ NvError status = NvSuccess;
+ NvError retStatus = NvSuccess;
+ NvFxTransportMessageGetProperty message;
+ struct completion comp;
+
+ if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) {
+ snd_printk(KERN_ERR "Property length too long!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_GET_PROPERTY;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+ message.Property = Property;
+ message.Size = Size;
+ message.pProperty = pProperty;
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageGetProperty);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static NvError tegra_transport_set_property(NvAudioFxObjectHandle hObject,
+ NvAudioFxProperty Property,
+ NvU32 Size,
+ void* pProperty)
+{
+ NvError status = NvSuccess;
+ NvError retStatus = NvSuccess;
+ NvFxTransportMessageSetProperty message;
+ struct completion comp;
+
+ if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) {
+ snd_printk(KERN_ERR "Property length too long!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_SET_PROPERTY;
+ message.pPrivateData = (void*)&comp;
+ message.hObject = hObject;
+ message.Property = Property;
+ message.Size = Size;
+ NvOsMemcpy(message.PropertyData, pProperty, Size);
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageSetProperty);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static NvError tegra_transport_stream_add_buffer(NvAudioFxStreamHandle hStream,
+ NvAudioFxBufferDescriptor* pDescriptor)
+{
+ NvError status = NvSuccess;
+ NvError retStatus;
+ NvFxTransportMessageStreamAddBuffer message;
+ struct completion comp;
+
+ init_completion(&comp);
+ message.Message = NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER;
+ message.pPrivateData = (void*)&comp;
+ message.hStream = hStream;
+ message.Descriptor = *pDescriptor;
+ message.pReturnError = &retStatus;
+
+ transport_send_message(NvFxTransportMessageStreamAddBuffer);
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ retStatus = status;
+
+EXIT:
+ return retStatus;
+}
+
+static void AlsaTransportServiceThread(void *arg)
+{
+ NvError status = NvSuccess;
+ static NvFxTransportMessageResultBuffer in;
+ NvU32 messageSize = 0;
+ int retry = 0;
+#define transport_complete(comp) \
+ complete((struct completion*)comp);
+
+ while (retry < 5 ) {
+ status = NvRmTransportConnect(atrans->hRmTransport,
+ TEGRA_TRANSPORT_CONNECT_TIMEOUT);
+ if (status == NvSuccess)
+ break;
+ retry++;
+ }
+
+ if (status) {
+ snd_printk(KERN_ERR "AlsaTransportServiceThread: Failed to \
+ connect to remote end. Giving up ! \n");
+ atrans->TransportConnected = 0;
+ return;
+ }
+
+ atrans->TransportConnected = 1;
+
+ while (!atrans->ShutDown) {
+ NvOsSemaphoreWait(atrans->hServiceSema);
+ if (atrans->ShutDown)
+ break;
+
+ status = NvRmTransportRecvMsg(atrans->hRmTransport,
+ &in,
+ sizeof(NvFxTransportMessageResultBuffer),
+ &messageSize);
+ if (status == NvSuccess) {
+ switch (in.Message.Message) {
+ case NVFXTRANSPORT_MESSAGE_MIXER_OPEN: {
+ *in.MixerOpen.phMixer = in.MixerOpen.hMixer;
+ transport_complete(in.MixerOpen.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_MIXER_CLOSE: {
+ transport_complete(in.MixerClose.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_CREATE_OBJECT: {
+ *in.CreateObject.phObject =
+ in.CreateObject.hObject;
+ transport_complete(in.CreateObject.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT: {
+ transport_complete(in.DestroyObject.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_MAP_BUFFER: {
+ *in.MapBuffer.phMixBuffer =
+ in.MapBuffer.hMixBuffer;
+ transport_complete(in.MapBuffer.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER: {
+ transport_complete(in.UnmapBuffer.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_GET_PROPERTY: {
+ *in.GetProperty.pReturnError =
+ in.GetProperty.ReturnError;
+ if (in.GetProperty.ReturnError == NvSuccess) {
+ NvOsMemcpy(in.GetProperty.pProperty,
+ in.GetProperty.PropertyData,
+ in.GetProperty.Size);
+ }
+ transport_complete(in.GetProperty.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_SET_PROPERTY: {
+ *in.SetProperty.pReturnError =
+ in.SetProperty.ReturnError;
+ transport_complete(in.SetProperty.pPrivateData);
+ }
+ break;
+
+ case NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER: {
+ *in.StreamAddBuffer.pReturnError =
+ in.StreamAddBuffer.ReturnError;
+ transport_complete(in.StreamAddBuffer.pPrivateData);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+}
+
+NvError tegra_transport_init(NvddkAudioFxFxnTable* xrt_fxns)
+{
+ NvError status = NvSuccess;
+
+ xrt_fxns->MixerOpen = tegra_transport_mixer_open;
+ xrt_fxns->MixerClose = tegra_transport_mixer_close;
+ xrt_fxns->MixerCreateObject = tegra_transport_mixer_create_object;
+ xrt_fxns->MixerDestroyObject = tegra_transport_mixer_destroy_object;
+ xrt_fxns->MixerMapBuffer = tegra_transport_mixer_map_buffer;
+ xrt_fxns->MixerUnmapBuffer = tegra_transport_mixer_unmap_buffer;
+ xrt_fxns->StreamAddBuffer = tegra_transport_stream_add_buffer;
+ xrt_fxns->GetProperty = tegra_transport_get_property;
+ xrt_fxns->SetProperty = tegra_transport_set_property;
+
+ /* Check if the FX Transport is already open.*/
+ if (atrans) {
+ spin_lock(&atrans->lock);
+ atrans->RefCount++;
+ spin_unlock(&atrans->lock);
+ goto EXIT;
+ }
+ /* Map a shared memory buffer.*/
+ atrans = (AlsaTransport*)kzalloc(sizeof(AlsaTransport),GFP_KERNEL);
+ if (!atrans) {
+ snd_printk(KERN_ERR "AlsaTransportInit kalloc failed! \n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ spin_lock_init(&atrans->lock);
+ memset(atrans, 0, sizeof(AlsaTransport));
+ spin_lock(&atrans->lock);
+ atrans->RefCount++;
+ spin_unlock(&atrans->lock);
+
+ atrans->hRmDevice = s_hRmGlobal;
+
+ status = NvOsSemaphoreCreate(&atrans->hServiceSema, 0);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ status = NvRmTransportOpen(atrans->hRmDevice,
+ "ALSA_TRANSPORT",
+ atrans->hServiceSema,
+ &atrans->hRmTransport);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportOpen failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ status = NvOsThreadCreate(AlsaTransportServiceThread,
+ NULL,
+ &atrans->hServiceThread);
+ if (status != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsThreadCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ while (!atrans->TransportConnected) {
+ NvOsThreadYield();
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ if (atrans) {
+ tegra_transport_deinit();
+ }
+
+EXIT:
+ return status;
+}
+
+void tegra_transport_deinit(void)
+{
+ if (!atrans)
+ goto EXIT;
+
+ spin_lock(&atrans->lock);
+ atrans->RefCount--;
+
+ if (atrans->RefCount > 0){
+ spin_unlock(&atrans->lock);
+ goto EXIT;
+ }
+ spin_unlock(&atrans->lock);
+
+ atrans->ShutDown = 1;
+
+ if (atrans->hRmTransport) {
+ NvRmTransportClose(atrans->hRmTransport);
+ atrans->hRmTransport = 0;
+ atrans->TransportConnected = 0;
+ }
+
+ if (atrans->hServiceThread) {
+ NvOsSemaphoreSignal(atrans->hServiceSema);
+ NvOsThreadJoin(atrans->hServiceThread);
+ atrans->hServiceThread = 0;
+ }
+
+ if (atrans->hServiceSema) {
+ NvOsSemaphoreDestroy(atrans->hServiceSema);
+ atrans->hServiceSema = 0;
+ }
+ atrans->hRmDevice = 0;
+ kfree(atrans);
+ atrans = 0;
+
+EXIT:
+ return;
+}
+
+static void tegra_audiofx_notifier_thread(void *arg)
+{
+ struct tegra_audio_data *audio_context = (struct tegra_audio_data *)arg;
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+ NvError e;
+ int retry = 0;
+ NvU32 messageSize;
+ NvAudioFxMessage* message =
+ (NvAudioFxMessage*)m_FxNotifier->RcvMessageBuffer;
+
+ while (retry < 5) {
+ e = NvRmTransportConnect(m_FxNotifier->hTransport, 5000);
+ if (e == NvSuccess)
+ break;
+
+ retry++;
+ }
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportConnect failed!\n");
+ m_FxNotifier->Connected = 0;
+ goto EXIT;
+ }
+
+ m_FxNotifier->Connected = 1;
+ while (1) {
+ NvOsSemaphoreWait(m_FxNotifier->hTransportSemaphore);
+ if (m_FxNotifier->Exit) {
+ break;
+ }
+
+ e = NvRmTransportRecvMsg(m_FxNotifier->hTransport,
+ message,
+ 256,
+ &messageSize);
+ if (e == NvSuccess) {
+ switch (message->Event) {
+ case NvAudioFxEventBufferDone:{
+ NvAudioFxBufferDoneMessage* bdm =
+ (NvAudioFxBufferDoneMessage*)message;
+ struct pcm_runtime_data* prtd =
+ (struct pcm_runtime_data*)bdm->m.pContext;
+
+ if ((prtd->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (prtd->play_sema != NULL)){
+ NvOsSemaphoreSignal(prtd->play_sema);
+ }
+ else if(prtd->rec_sema != NULL){
+ NvOsSemaphoreSignal(prtd->rec_sema);
+ }
+ }
+ break;
+
+ case NvAudioFxEventStateChange:
+ break;
+
+ default:
+ snd_printk(KERN_ERR"Unhandled event\n");
+ break;
+ }
+ }
+ }
+
+EXIT:
+ return;
+}
+
+GlobalFxList* tegra_audiofx_create_topology(NvAudioFxMixerHandle hMixer)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+ GlobalFxList* pFxList = 0;
+
+ memset(&connection, 0, sizeof(NvAudioFxConnectionDescriptor));
+ pFxList = (GlobalFxList*)kzalloc(sizeof(GlobalFxList), GFP_KERNEL);
+ if (!pFxList) {
+ snd_printk(KERN_ERR "kzalloc failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+ memset(pFxList, 0, sizeof(GlobalFxList));
+
+#define audiofx_create_path(list_index, FxId) \
+ pFxList->hFx[list_index] = tegra_transport_mixer_create_object(hMixer,FxId); \
+ if(!pFxList->hFx[list_index]) { \
+ snd_printk(KERN_ERR "audiofx_create_path failed!\n"); \
+ goto EXIT_WITH_ERROR; \
+ }
+
+#define audiofx_connect(src_index, sink_index) \
+ connection.SourcePin = NvAudioFxSourcePin; \
+ connection.SinkPin = NvAudioFxSinkPin; \
+ connection.hSink = (NvAudioFxHandle)pFxList->hFx[sink_index]; \
+ e = tegra_transport_set_property(pFxList->hFx[src_index], \
+ NvAudioFxProperty_Attach, \
+ sizeof(NvAudioFxConnectionDescriptor), \
+ &connection); \
+ if(e != NvSuccess) { \
+ snd_printk(KERN_ERR "audiofx_connect failed!\n"); \
+ }
+
+ /* Default Playback Path */
+ audiofx_create_path(GlobalFx_DefaultPlaybackMix,
+ NvAudioFxDefaultPlaybackMixId);
+
+ audiofx_create_path(GlobalFx_DefaultPlaybackSplit,
+ NvAudioFxDefaultPlaybackSplitId);
+
+ /* I2S Playback Path */
+ audiofx_create_path(GlobalFx_I2sPlaybackMix, NvAudioFxI2sPlaybackMixId);
+ audiofx_create_path(GlobalFx_I2sPlaybackVolume, NvAudioFxVolumeId);
+ audiofx_create_path(GlobalFx_I2s, NvAudioFxI2sId);
+
+ /* Default Record Path */
+ audiofx_create_path(GlobalFx_DefaultRecordMix,
+ NvAudioFxDefaultRecordMixId);
+
+ audiofx_create_path(GlobalFx_DefaultRecordSplit,
+ NvAudioFxDefaultRecordSplitId);
+
+ /* I2S Record Path */
+ audiofx_create_path(GlobalFx_I2sRecordVolume, NvAudioFxVolumeId);
+ audiofx_create_path(GlobalFx_I2sRecordSplit, NvAudioFxI2sRecordSplitId);
+
+#if (0)
+ /* Simple Playback */
+ audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_I2s);
+#else
+ /* Playback */
+ audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_DefaultPlaybackSplit);
+ audiofx_connect(GlobalFx_DefaultPlaybackSplit, GlobalFx_I2sPlaybackMix);
+
+ /* Wire SPDIF on HDMI connection and unwire it when not connected. */
+ audiofx_connect(GlobalFx_I2sPlaybackMix, GlobalFx_I2sPlaybackVolume);
+ audiofx_connect(GlobalFx_I2sPlaybackVolume, GlobalFx_I2s);
+#endif
+
+ /*Record*/
+ audiofx_connect(GlobalFx_I2s, GlobalFx_I2sRecordVolume);
+ audiofx_connect(GlobalFx_I2sRecordVolume, GlobalFx_I2sRecordSplit);
+
+ audiofx_connect(GlobalFx_I2sRecordSplit, GlobalFx_DefaultRecordMix);
+ audiofx_connect(GlobalFx_DefaultRecordMix, GlobalFx_DefaultRecordSplit);
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ /* tegra_audiofx_destroy_topology(pFxList); */
+ pFxList = 0;
+
+EXIT:
+ return pFxList;
+}
+
+void tegra_audiofx_destroy_topology(GlobalFxList* pFxList)
+{
+ NvS32 i;
+
+ if (pFxList) {
+ for (i = 0; i < GlobalFx_Num; i++) {
+ if (pFxList->hFx[i]) {
+ /*
+ TODO
+ NvddkAudioFxMixerDestroyObject(pFxList->hFx[i]);
+ Add code to destory
+ */
+ }
+ }
+
+ kfree(pFxList);
+ }
+}
+
+NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context)
+{
+ NvAudioFxMixerHandle m_hMixer =
+ (NvAudioFxMixerHandle)audio_context->mixer_handle;
+
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+ NvRmDeviceHandle m_hRm = (NvRmDeviceHandle)audio_context->m_hRm;
+ NvError e = NvSuccess;
+ NvAudioFxNotifierConnectionDescriptor connectionDesciptor;
+ NvAudioFxMessage message;
+
+ memset(&connectionDesciptor,
+ 0,
+ sizeof(NvAudioFxNotifierConnectionDescriptor));
+ memset(&message, 0, sizeof(NvAudioFxMessage));
+
+ e = NvOsSemaphoreCreate(&m_FxNotifier->hTransportSemaphore, 0);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ m_FxNotifier->hNotifier =
+ (NvAudioFxNotifierHandle)tegra_transport_mixer_create_object(
+ m_hMixer,
+ NvAudioFxNotifierId);
+ if (!m_FxNotifier->hNotifier) {
+ snd_printk(KERN_ERR "transport_mixer_create_object failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ e = NvRmTransportOpen(m_hRm,
+ 0,
+ m_FxNotifier->hTransportSemaphore,
+ &m_FxNotifier->hTransport);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvRmTransportOpen failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ NvRmTransportGetPortName(m_FxNotifier->hTransport,
+ (NvU8*)&connectionDesciptor.PortName,
+ sizeof(NvU8) * 16);
+
+ e = NvOsThreadCreate(tegra_audiofx_notifier_thread,
+ audio_context,
+ &m_FxNotifier->hTransportThread);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "NvOsThreadCreate failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ e = tegra_transport_set_property(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier,
+ NvAudioFxNotifierProperty_Connect,
+ sizeof(NvAudioFxNotifierConnectionDescriptor),
+ &connectionDesciptor);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+ tegra_audiofx_destroyfx(audio_context);
+
+EXIT:
+ return e;
+}
+
+void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context)
+{
+ FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;
+
+ if (m_FxNotifier->Connected) {
+ m_FxNotifier->Exit = 1;
+ NvOsSemaphoreSignal(m_FxNotifier->hTransportSemaphore);
+ NvOsThreadJoin(m_FxNotifier->hTransportThread);
+ m_FxNotifier->hTransportThread = 0;
+ tegra_transport_set_property(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier,
+ NvAudioFxNotifierProperty_Disconnect,
+ 0,
+ 0);
+ }
+
+ if (m_FxNotifier->hTransport) {
+ NvRmTransportClose(m_FxNotifier->hTransport);
+ m_FxNotifier->hTransport = 0;
+ }
+
+ if (m_FxNotifier->hNotifier) {
+ tegra_transport_mixer_destroy_object(
+ (NvAudioFxObjectHandle)m_FxNotifier->hNotifier);
+ m_FxNotifier->hNotifier = 0;
+ }
+
+ if (m_FxNotifier->hTransportSemaphore) {
+ NvOsSemaphoreDestroy(m_FxNotifier->hTransportSemaphore);
+ m_FxNotifier->hTransportSemaphore = 0;
+ }
+
+ return;
+}
+
+#define audiofx_create_object(path_object, FxId) \
+ path_object = tegra_transport_mixer_create_object(hMixer, FxId); \
+ if(!path_object) { \
+ snd_printk(KERN_ERR "audiofx_create_object failed!"); \
+ }
+
+#define audiofx_path_connect(path_object, sink_object) \
+ connection.SourcePin = NvAudioFxSourcePin; \
+ connection.SinkPin = NvAudioFxSinkPin; \
+ connection.hSink = (NvAudioFxHandle)sink_object; \
+ e = tegra_transport_set_property(path_object, \
+ NvAudioFxProperty_Attach, \
+ sizeof(NvAudioFxConnectionDescriptor), \
+ &connection); \
+ if(e != NvSuccess) { \
+ snd_printk(KERN_ERR "audiofx_path_connect failed!"); \
+ goto EXIT_WITH_ERROR; \
+ }
+
+NvError tegra_audiofx_create_output(NvRmDeviceHandle hRmDevice,
+ NvAudioFxMixerHandle hMixer,
+ StandardPath* pPath)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+
+ /* Standard Output
+ [stream]->[SRC]->[Convert]->[Resize]->[Volume]->Default Output
+ */
+
+ memset(pPath, 0, sizeof(StandardPath));
+
+ audiofx_create_object(pPath->Stream, NvAudioFxStreamId);
+ audiofx_create_object(pPath->Src, NvAudioFxSrcId);
+ audiofx_create_object(pPath->Convert, NvAudioFxConvertId);
+ audiofx_create_object(pPath->Resize, NvAudioFxResizeId);
+ audiofx_create_object(pPath->Volume, NvAudioFxVolumeId);
+
+ audiofx_path_connect(pPath->Stream, pPath->Src);
+ audiofx_path_connect(pPath->Src, pPath->Convert);
+ audiofx_path_connect(pPath->Convert, pPath->Resize);
+ audiofx_path_connect(pPath->Resize, pPath->Volume);
+
+ connection.SourcePin = NvAudioFxSourcePin;
+ connection.hSink = 0;
+ connection.SinkPin = NvAudioFxSinkPin;
+ e = tegra_transport_set_property(pPath->Volume,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ tegra_audiofx_destroy_output(pPath);
+
+EXIT:
+
+ return e;
+}
+
+NvError tegra_audiofx_destroy_output(StandardPath* pPath)
+{
+ if (pPath->Volume) {
+ tegra_transport_mixer_destroy_object(pPath->Volume);
+ pPath->Volume = 0;
+ }
+
+ if (pPath->Resize) {
+ tegra_transport_mixer_destroy_object(pPath->Resize);
+ pPath->Resize = 0;
+ }
+
+ if (pPath->Convert) {
+ tegra_transport_mixer_destroy_object(pPath->Convert);
+ pPath->Convert = 0;
+ }
+
+ if (pPath->Src) {
+ tegra_transport_mixer_destroy_object(pPath->Src);
+ pPath->Src = 0;
+ }
+
+ if (pPath->Stream) {
+ tegra_transport_mixer_destroy_object(pPath->Stream);
+ pPath->Stream = 0;
+ }
+
+ return NvSuccess;
+}
+
+NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice,
+ NvAudioFxMixerHandle hMixer,
+ StandardPath* pInput,
+ InputSelection InputSelect)
+{
+ NvError e = NvSuccess;
+ NvAudioFxConnectionDescriptor connection;
+
+ /*
+ Standard Input (record or loopback)
+
+ +--------+ (2) +--------+ (3) +---------+ (4) +--------+ (5)
+ +--| Stream |<----| Resize |<-----| Convert |<-----| SRC |<--From I2S
+ | | | | |<--+ | (opt.) | | (opt.) |
+ | +--------+ +--------+ | +---------+ +--------+
+ | (1) |
+ +------------------------------+
+
+ */
+
+ memset(pInput, 0, sizeof(StandardPath));
+
+ audiofx_create_object(pInput->Stream, NvAudioFxStreamId);
+ audiofx_create_object(pInput->Resize,NvAudioFxResizeId);
+ audiofx_create_object(pInput->Src,NvAudioFxSrcId);
+ audiofx_create_object(pInput->Convert,NvAudioFxConvertId);
+
+ /* Wire 1 */
+ connection.SourcePin = NvAudioFxSourcePin;
+ connection.hSink = (NvAudioFxHandle)pInput->Resize;
+ connection.SinkPin = NvAudioFxCopySinkPin;
+ e = tegra_transport_set_property(pInput->Stream,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ audiofx_path_connect(pInput->Resize, pInput->Stream);
+
+ /* Wire 5 */
+ connection.SourcePin = (InputSelect == NvAudioInputSelect_Record) ?
+ NvAudioFxSourcePin : NvAudioFxLoopbackPin;
+ connection.hSink = (NvAudioFxHandle)pInput->Resize;
+ connection.SinkPin = NvAudioFxSinkPin;
+ e = tegra_transport_set_property(0,
+ NvAudioFxProperty_Attach,
+ sizeof(NvAudioFxConnectionDescriptor),
+ &connection);
+ if (e != NvSuccess) {
+ snd_printk(KERN_ERR "tegra_transport_set_property failed!\n");
+ goto EXIT_WITH_ERROR;
+ }
+
+ goto EXIT;
+
+EXIT_WITH_ERROR:
+
+ tegra_audiofx_destroy_input(pInput);
+
+EXIT:
+
+ return e;
+}
+
+
+NvError tegra_audiofx_destroy_input(StandardPath* pInput)
+{
+ if (pInput->Src) {
+ tegra_transport_mixer_destroy_object(pInput->Src);
+ pInput->Src = 0;
+ }
+
+ if (pInput->Convert) {
+ tegra_transport_mixer_destroy_object(pInput->Convert);
+ pInput->Convert = 0;
+ }
+
+ if (pInput->Resize) {
+ tegra_transport_mixer_destroy_object(pInput->Resize);
+ pInput->Resize = 0;
+ }
+
+ if (pInput->Stream) {
+ tegra_transport_mixer_destroy_object(pInput->Stream);
+ pInput->Stream = 0;
+ }
+
+ return NvSuccess;
+}
+