audio: hal: Add Hal extension for transcode loopback usecase

Adding HAL extension for transcode hw loopback
Userspace qahwi APIs introduced to exercise this extension

Change-Id: Iffae3f6936c2519f0c5349c2430eb598b97c3364
diff --git a/configure.ac b/configure.ac
index 6a0e9c2..e3d1bc0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -111,6 +111,7 @@
 AM_CONDITIONAL([AUDIO_IP_HDLR], [test x$AUDIO_FEATURE_IP_HDLR_ENABLED = xtrue])
 AM_CONDITIONAL([SPLIT_A2DP], [test x$AUDIO_FEATURE_ENABLED_SPLIT_A2DP = xtrue])
 AM_CONDITIONAL([QAF_SUPPORT], [test x$AUDIO_FEATURE_ENABLED_QAF = xtrue])
+AM_CONDITIONAL([AUDIO_HW_LOOPBACK], [test x$AUDIO_FEATURE_ENABLED_AUDIO_HW_LOOPBACK = xtrue])
 
 AC_CONFIG_FILES([ \
         Makefile \
diff --git a/hal/Makefile.am b/hal/Makefile.am
index bc71c50..2c811f6 100644
--- a/hal/Makefile.am
+++ b/hal/Makefile.am
@@ -177,6 +177,11 @@
 c_sources += audio_extn/qaf.c
 endif
 
+if AUDIO_HW_LOOPBACK
+AM_CFLAGS += -DAUDIO_HW_LOOPBACK_ENABLED
+c_sources += audio_extn/hw_loopback.c
+endif
+
 h_sources = audio_extn/audio_defs.h \
             audio_extn/audio_extn.h \
             audio_hw.h \
diff --git a/hal/audio_extn/adsp_hdlr.c b/hal/audio_extn/adsp_hdlr.c
index 436da96..3b073c3 100644
--- a/hal/audio_extn/adsp_hdlr.c
+++ b/hal/audio_extn/adsp_hdlr.c
@@ -423,6 +423,7 @@
     pthread_mutex_lock(&adsp_hdlr_inst->event_list_lock);
     if (list_empty(&adsp_hdlr_inst->event_list)) {
         ALOGD("%s: event list is empty", __func__);
+        pthread_mutex_unlock(&adsp_hdlr_inst->event_list_lock);
         return 0;
     }
     list_for_each_safe(node, tempnode, &adsp_hdlr_inst->event_list) {
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 7c86d18..ff35374 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -884,4 +884,55 @@
 int audio_extn_utils_set_channel_map(
             struct stream_out *out,
             struct audio_out_channel_map_param *channel_map_param);
+#ifdef AUDIO_HW_LOOPBACK_ENABLED
+/* API to create audio patch */
+int audio_extn_hw_loopback_create_audio_patch(struct audio_hw_device *dev,
+                                     unsigned int num_sources,
+                                     const struct audio_port_config *sources,
+                                     unsigned int num_sinks,
+                                     const struct audio_port_config *sinks,
+                                     audio_patch_handle_t *handle);
+/* API to release audio patch */
+int audio_extn_hw_loopback_release_audio_patch(struct audio_hw_device *dev,
+                                             audio_patch_handle_t handle);
+
+int audio_extn_hw_loopback_set_audio_port_config(struct audio_hw_device *dev,
+                                    const struct audio_port_config *config);
+int audio_extn_hw_loopback_get_audio_port(struct audio_hw_device *dev,
+                                    struct audio_port *port_in);
+int audio_extn_loopback_init(struct audio_device *adev);
+void audio_extn_loopback_deinit(struct audio_device *adev);
+#else
+static int __unused audio_extn_hw_loopback_create_audio_patch(struct audio_hw_device *dev __unused,
+                                     unsigned int num_sources __unused,
+                                     const struct audio_port_config *sources __unused,
+                                     unsigned int num_sinks __unused,
+                                     const struct audio_port_config *sinks __unused,
+                                     audio_patch_handle_t *handle __unused)
+{
+    return -ENOSYS;
+}
+static int __unused audio_extn_hw_loopback_release_audio_patch(struct audio_hw_device *dev __unused,
+                                             audio_patch_handle_t handle __unused)
+{
+    return -ENOSYS;
+}
+static int __unused audio_extn_hw_loopback_set_audio_port_config(struct audio_hw_device *dev __unused,
+                                    const struct audio_port_config *config __unused)
+{
+    return -ENOSYS;
+}
+static int __unused audio_extn_hw_loopback_get_audio_port(struct audio_hw_device *dev __unused,
+                                    struct audio_port *port_in __unused)
+{
+    return -ENOSYS;
+}
+static int __unused audio_extn_loopback_init(struct audio_device *adev __unused)
+{
+    return -ENOSYS;
+}
+static void __unused audio_extn_loopback_deinit(struct audio_device *adev __unused)
+{
+}
+#endif
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/hw_loopback.c b/hal/audio_extn/hw_loopback.c
new file mode 100644
index 0000000..ddca6b8
--- /dev/null
+++ b/hal/audio_extn/hw_loopback.c
@@ -0,0 +1,722 @@
+/*
+* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define LOG_TAG "audio_hw_loopback"
+/*#define LOG_NDEBUG 0*/
+/*#define VERY_VERY_VERBOSE_LOGGING*/
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+#define MAX_NUM_PATCHES 1
+#define MAX_NUM_HW_LOOPBACK_PATCHES 1
+#define PATCH_HANDLE_INVALID 0xFFFF
+#define MAX_SOURCE_PORTS_PER_PATCH 1
+#define MAX_SINK_PORTS_PER_PATCH 1
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <sys/resource.h>
+#include <sys/prctl.h>
+#include <cutils/properties.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include "audio_utils/primitives.h"
+#include "audio_hw.h"
+#include "platform_api.h"
+#include <platform.h>
+#include <system/thread_defs.h>
+#include <cutils/sched_policy.h>
+#include "audio_extn.h"
+#include "sound/compress_params.h"
+#include <system/audio.h>
+
+/*
+* Unique patch handle ID = (unique_patch_handle_type << 8 | patch_handle_num)
+* Eg : HDMI_IN_SPKR_OUT handles can be 0x1000, 0x1001 and so on..
+*/
+typedef enum patch_handle_type {
+    AUDIO_PATCH_HDMI_IN_SPKR_OUT=0x10,
+    AUDIO_PATCH_SPDIF_IN_SPKR_OUT,
+    AUDIO_PATCH_MIC_IN_SPKR_OUT,
+    AUDIO_PATCH_MIC_IN_HDMI_OUT
+} patch_handle_type_t;
+
+typedef enum patch_state {
+    PATCH_INACTIVE,// Patch is not created yet
+    PATCH_CREATED, // Patch created but not in running state yet, probably due
+                   // to lack of proper port config
+    PATCH_RUNNING, // Patch in running state, moves to this state when patch
+                   // created and proper port config is available
+} patch_state_t;
+
+typedef struct loopback_patch {
+    audio_patch_handle_t patch_handle_id;            /* patch unique ID */
+    struct audio_port_config loopback_source;        /* Source port config */
+    struct audio_port_config loopback_sink;          /* Source port config */
+    struct compress *source_stream;                  /* Source stream */
+    struct compress *sink_stream;                    /* Source stream */
+    struct stream_inout patch_stream;                /* InOut type stream */
+    patch_state_t patch_state;                       /* Patch operation state */
+} loopback_patch_t;
+
+typedef struct patch_db_struct {
+    int32_t num_patches;
+    loopback_patch_t loopback_patch[MAX_NUM_PATCHES];
+} patch_db_t;
+
+typedef struct audio_loopback {
+    struct audio_device *adev;
+    patch_db_t patch_db;
+    audio_usecase_t uc_id;
+    usecase_type_t  uc_type;
+    pthread_mutex_t lock;
+} audio_loopback_t;
+
+typedef struct port_info {
+    audio_port_handle_t      id;                /* port unique ID */
+    audio_port_role_t        role;              /* sink or source */
+    audio_port_type_t        type;              /* device, mix ... */
+} port_info_t;
+
+/* Audio loopback module struct */
+static audio_loopback_t *audio_loopback_mod = NULL;
+
+uint32_t format_to_bitwidth(audio_format_t format)
+{
+    switch (format) {
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return 16;
+        case AUDIO_FORMAT_PCM_8_BIT:
+            return 8;
+        case AUDIO_FORMAT_PCM_32_BIT:
+            return 32;
+        case AUDIO_FORMAT_PCM_8_24_BIT:
+            return 32;
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+            return 24;
+        default:
+            return 16;
+    }
+}
+
+/* Initialize patch database */
+int init_patch_database(patch_db_t* patch_db)
+{
+    int patch_init_rc = 0, patch_num=0;
+    patch_db->num_patches = 0;
+    for (patch_num=0;patch_num < MAX_NUM_PATCHES;patch_num++) {
+        patch_db->loopback_patch[patch_num].patch_handle_id = (int32_t)
+        PATCH_HANDLE_INVALID;
+    }
+    return patch_init_rc;
+}
+
+/* Get patch type based on source and sink ports configuration */
+/* Only ports of type 'DEVICE' are supported */
+patch_handle_type_t get_loopback_patch_type(loopback_patch_t*  loopback_patch)
+{
+    bool is_source_hdmi=false, is_sink_spkr=false;
+    if (loopback_patch->patch_handle_id != PATCH_HANDLE_INVALID) {
+        ALOGE("%s, Patch handle already exists", __func__);
+        return loopback_patch->patch_handle_id;
+    }
+
+    if (loopback_patch->loopback_source.role == AUDIO_PORT_ROLE_SOURCE) {
+        switch (loopback_patch->loopback_source.type) {
+            case AUDIO_PORT_TYPE_DEVICE :
+                if ((loopback_patch->loopback_source.config_mask &
+                   AUDIO_PORT_CONFIG_FORMAT) && (loopback_patch->loopback_source.ext.device.type & AUDIO_DEVICE_IN_HDMI)) {
+                       switch (loopback_patch->loopback_source.format) {
+                           case AUDIO_FORMAT_PCM:
+                           case AUDIO_FORMAT_PCM_16_BIT:
+                           case AUDIO_FORMAT_PCM_8_24_BIT:
+                           case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+                           case AUDIO_FORMAT_IEC61937:
+                           case AUDIO_FORMAT_AC3:
+                           case AUDIO_FORMAT_E_AC3:
+                              is_source_hdmi = true;
+                           break;
+                }
+            }
+            break;
+            default :
+            break;
+            //Unsupported as of now, need to extend for other source types
+        }
+    }
+    if (loopback_patch->loopback_sink.role == AUDIO_PORT_ROLE_SINK) {
+        switch (loopback_patch->loopback_sink.type) {
+            case AUDIO_PORT_TYPE_DEVICE :
+                if ((loopback_patch->loopback_sink.config_mask &
+                    AUDIO_PORT_CONFIG_FORMAT) &&
+                    (loopback_patch->loopback_sink.ext.device.type &
+                     AUDIO_DEVICE_OUT_SPEAKER)) {
+                       switch (loopback_patch->loopback_sink.format) {
+                           case AUDIO_FORMAT_PCM:
+                           case AUDIO_FORMAT_PCM_16_BIT:
+                           case AUDIO_FORMAT_PCM_32_BIT:
+                           case AUDIO_FORMAT_PCM_8_24_BIT:
+                           case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+                              is_sink_spkr = true;
+			      break;
+                           default:
+                              break;
+                    }
+                }
+                break;
+            default :
+                break;
+                //Unsupported as of now, need to extend for other sink types
+        }
+    }
+    if (is_source_hdmi && is_sink_spkr) {
+        return AUDIO_PATCH_HDMI_IN_SPKR_OUT;
+    }
+    ALOGE("%s, Unsupported source or sink port config", __func__);
+    return loopback_patch->patch_handle_id;
+}
+
+/* Releases an existing loopback session */
+/* Conditions : Session setup goes bad or actual session teardown */
+int32_t release_loopback_session(loopback_patch_t *active_loopback_patch)
+{
+    int32_t ret = 0;
+    struct audio_usecase *uc_info;
+    struct audio_device *adev = audio_loopback_mod->adev;
+
+    /* 1. Close the PCM devices */
+    if (active_loopback_patch->source_stream) {
+        compress_close(active_loopback_patch->source_stream);
+        active_loopback_patch->source_stream = NULL;
+    } else {
+        ALOGE("%s: Failed to close loopback stream in capture path",
+            __func__);
+    }
+    if (active_loopback_patch->sink_stream) {
+        compress_close(active_loopback_patch->sink_stream);
+        active_loopback_patch->sink_stream = NULL;
+    } else {
+        ALOGE("%s: Failed to close loopback stream in playback path",
+            __func__);
+    }
+
+    uc_info = get_usecase_from_list(adev, audio_loopback_mod->uc_id);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the loopback usecase (%d) in the list",
+            __func__, active_loopback_patch->patch_handle_id);
+        return -EINVAL;
+    }
+
+    active_loopback_patch->patch_state = PATCH_INACTIVE;
+
+    /* 2. Get and set stream specific mixer controls */
+    disable_audio_route(adev, uc_info);
+
+    /* 3. Disable the rx and tx devices */
+    disable_snd_device(adev, uc_info->out_snd_device);
+    disable_snd_device(adev, uc_info->in_snd_device);
+
+    list_remove(&uc_info->list);
+    free(uc_info);
+
+    ALOGD("%s: Release loopback session exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+/* Create a loopback session based on active loopback patch selected */
+int create_loopback_session(loopback_patch_t *active_loopback_patch)
+{
+    int32_t ret = 0, bits_per_sample;
+    struct audio_usecase *uc_info;
+    int32_t pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
+    char dummy_write_buf[64];
+    struct audio_device *adev = audio_loopback_mod->adev;
+    struct compr_config source_config, sink_config;
+    struct snd_codec codec;
+    struct audio_port_config *source_patch_config = &active_loopback_patch->
+                                                    loopback_source;
+    struct audio_port_config *sink_patch_config = &active_loopback_patch->
+                                                    loopback_sink;
+
+    ALOGD("%s: Create loopback session begin", __func__);
+
+    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+
+    if (!uc_info) {
+        ALOGE("%s: Failure to open loopback session", __func__);
+        return -ENOMEM;
+    }
+
+    uc_info->id = USECASE_AUDIO_TRANSCODE_LOOPBACK;
+    uc_info->type = audio_loopback_mod->uc_type;
+    uc_info->stream.inout = &active_loopback_patch->patch_stream;
+    uc_info->devices = active_loopback_patch->patch_stream.out_config.devices;
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &uc_info->list);
+
+    select_devices(adev, uc_info->id);
+
+    pcm_dev_asm_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+    pcm_dev_asm_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+
+    if (pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
+        ALOGE("%s: Invalid PCM devices (asm: rx %d tx %d) for the usecase(%d)",
+            __func__, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id, uc_info->id);
+        ret = -EIO;
+        goto exit;
+    }
+
+    ALOGD("%s: LOOPBACK PCM devices (rx: %d tx: %d) usecase(%d)",
+        __func__, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id, uc_info->id);
+
+    if (source_patch_config->format == AUDIO_FORMAT_IEC61937) {
+        // This is needed to set a known format to DSP and handle
+        // any format change via ADSP event
+        codec.id = AUDIO_FORMAT_AC3;
+    }
+
+    /* Set config for compress stream open in capture path */
+    codec.id = get_snd_codec_id(source_patch_config->format);
+    codec.ch_in = audio_channel_count_from_out_mask(source_patch_config->
+                                                    channel_mask);
+    codec.ch_out = 2; // Irrelevant for loopback case in this direction
+    codec.sample_rate = source_patch_config->sample_rate;
+    codec.format = hal_format_to_alsa(source_patch_config->format);
+    source_config.fragment_size = 1024;
+    source_config.fragments = 1;
+    source_config.codec = &codec;
+
+    /* Open compress stream in capture path */
+    active_loopback_patch->source_stream = compress_open(adev->snd_card,
+                        pcm_dev_asm_tx_id, COMPRESS_OUT, &source_config);
+    if (active_loopback_patch->source_stream && !is_compress_ready(
+        active_loopback_patch->source_stream)) {
+        ALOGE("%s: %s", __func__, compress_get_error(active_loopback_patch->
+        source_stream));
+        ret = -EIO;
+        goto exit;
+    } else if (active_loopback_patch->source_stream == NULL) {
+        ALOGE("%s: Failure to open loopback stream in capture path", __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* Set config for compress stream open in playback path */
+    codec.id = get_snd_codec_id(sink_patch_config->format);
+    codec.ch_in = 2; // Irrelevant for loopback case in this direction
+    codec.ch_out = audio_channel_count_from_out_mask(sink_patch_config->
+                                                     channel_mask);
+    codec.sample_rate = sink_patch_config->sample_rate;
+    codec.format = hal_format_to_alsa(sink_patch_config->format);
+    sink_config.fragment_size = 1024;
+    sink_config.fragments = 1;
+    sink_config.codec = &codec;
+
+    /* Open compress stream in playback path */
+    active_loopback_patch->sink_stream = compress_open(adev->snd_card,
+                         pcm_dev_asm_rx_id, COMPRESS_IN, &sink_config);
+    if (active_loopback_patch->sink_stream && !is_compress_ready(
+        active_loopback_patch->sink_stream)) {
+        ALOGE("%s: %s", __func__, compress_get_error(active_loopback_patch->
+                sink_stream));
+        ret = -EIO;
+        goto exit;
+    } else if (active_loopback_patch->sink_stream == NULL) {
+        ALOGE("%s: Failure to open loopback stream in playback path", __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    active_loopback_patch->patch_state = PATCH_CREATED;
+
+    if (compress_start(active_loopback_patch->source_stream) < 0) {
+        ALOGE("%s: Failure to start loopback stream in capture path",
+        __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* Dummy compress_write to ensure compress_start does not fail */
+    compress_write(active_loopback_patch->sink_stream, dummy_write_buf, 64);
+    if (compress_start(active_loopback_patch->sink_stream) < 0) {
+        ALOGE("%s: Cannot start loopback stream in playback path",
+                __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* Move patch state to running, now that session is set up */
+    active_loopback_patch->patch_state = PATCH_RUNNING;
+    ALOGD("%s: Create loopback session end: status(%d)", __func__, ret);
+    return ret;
+
+exit:
+    ALOGE("%s: Problem in Loopback session creation: \
+            status(%d), releasing session ", __func__, ret);
+    release_loopback_session(active_loopback_patch);
+    return ret;
+}
+
+void update_patch_stream_config(struct stream_config *stream_cfg ,
+                                struct audio_port_config *port_cfg)
+{
+    stream_cfg->sample_rate = port_cfg->sample_rate;
+    stream_cfg->channel_mask = port_cfg->channel_mask;
+    stream_cfg->format = port_cfg->format;
+    stream_cfg->devices = port_cfg->ext.device.type;
+    stream_cfg->bit_width = format_to_bitwidth(port_cfg->format);
+}
+/* API to create audio patch */
+int audio_extn_hw_loopback_create_audio_patch(struct audio_hw_device *dev,
+                                     unsigned int num_sources,
+                                     const struct audio_port_config *sources,
+                                     unsigned int num_sinks,
+                                     const struct audio_port_config *sinks,
+                                     audio_patch_handle_t *handle)
+{
+    int status = 0;
+    patch_handle_type_t loopback_patch_type=0x0;
+    loopback_patch_t loopback_patch, *active_loopback_patch = NULL;
+    ALOGV("%s : Create audio patch begin", __func__);
+
+    if ((audio_loopback_mod == NULL) || (dev == NULL)) {
+        ALOGE("%s, Loopback module not initialized orInvalid device", __func__);
+        status = -EINVAL;
+        return status;
+    }
+
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+    if (audio_loopback_mod->patch_db.num_patches >= MAX_NUM_PATCHES ) {
+        ALOGE("%s, Exhausted maximum possible patches per device", __func__);
+        status = -EINVAL;
+        goto exit_create_patch;
+    }
+
+    /* Port configuration check & validation */
+    if (num_sources > MAX_SOURCE_PORTS_PER_PATCH ||
+        num_sinks > MAX_SINK_PORTS_PER_PATCH) {
+        ALOGE("%s, Unsupported patch configuration, sources %d sinks %d ",
+                __func__, num_sources, num_sources);
+        status = -EINVAL;
+        goto exit_create_patch;
+    }
+
+    /* Use an empty patch from patch database and initialze */
+    active_loopback_patch = &(audio_loopback_mod->patch_db.loopback_patch[
+                                audio_loopback_mod->patch_db.num_patches]);
+    active_loopback_patch->patch_handle_id = PATCH_HANDLE_INVALID;
+    active_loopback_patch->patch_state = PATCH_INACTIVE;
+    memcpy(&active_loopback_patch->loopback_source, &sources[0], sizeof(struct
+    audio_port_config));
+    memcpy(&active_loopback_patch->loopback_sink, &sinks[0], sizeof(struct
+    audio_port_config));
+
+    /* Get loopback patch type based on source and sink ports configuration */
+    loopback_patch_type = get_loopback_patch_type(active_loopback_patch);
+
+    if (loopback_patch_type == PATCH_HANDLE_INVALID) {
+        ALOGE("%s, Unsupported patch type", __func__);
+        status = -EINVAL;
+        goto exit_create_patch;
+    }
+
+    update_patch_stream_config(&active_loopback_patch->patch_stream.in_config,
+                                &active_loopback_patch->loopback_source);
+    update_patch_stream_config(&active_loopback_patch->patch_stream.out_config,
+                                &active_loopback_patch->loopback_sink);
+    // Lock patch database, create patch handle and add patch handle to the list
+
+    active_loopback_patch->patch_handle_id = (loopback_patch_type << 8 |
+                                audio_loopback_mod->patch_db.num_patches);
+
+    /* Is usecase transcode loopback? If yes, invoke loopback driver */
+    if ((active_loopback_patch->loopback_source.type == AUDIO_PORT_TYPE_DEVICE)
+       &&
+       (active_loopback_patch->loopback_sink.type == AUDIO_PORT_TYPE_DEVICE)) {
+        status = create_loopback_session(active_loopback_patch);
+        if (status != 0)
+            goto exit_create_patch;
+    }
+
+    // Create callback thread to listen to events from HW data path
+
+    /* Fill unique handle ID generated based on active loopback patch */
+    *handle = audio_loopback_mod->patch_db.loopback_patch[audio_loopback_mod->
+                                        patch_db.num_patches].patch_handle_id;
+    audio_loopback_mod->patch_db.num_patches++;
+
+exit_create_patch :
+    ALOGV("%s : Create audio patch end, status(%d)", __func__, status);
+    pthread_mutex_unlock(&audio_loopback_mod->lock);
+    return status;
+}
+
+/* API to release audio patch */
+int audio_extn_hw_loopback_release_audio_patch(struct audio_hw_device *dev,
+                                             audio_patch_handle_t handle)
+{
+    int status = 0, n=0, patch_index=-1;
+    bool patch_found = false;
+    loopback_patch_t *active_loopback_patch = NULL;
+    ALOGV("%s audio_extn_hw_loopback_release_audio_patch begin %d", __func__, __LINE__);
+
+    if ((audio_loopback_mod == NULL) || (dev == NULL)) {
+        ALOGE("%s, Invalid device", __func__);
+        status = -1;
+        return status;
+    }
+
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+
+    for (n=0;n < MAX_NUM_PATCHES;n++) {
+        if (audio_loopback_mod->patch_db.loopback_patch[n].patch_handle_id ==
+           handle) {
+            patch_found = true;
+            patch_index = n;
+            break;
+        }
+    }
+
+    if (patch_found) {
+        active_loopback_patch = &(audio_loopback_mod->patch_db.loopback_patch[
+                                patch_index]);
+        status = release_loopback_session(active_loopback_patch);
+    } else {
+        ALOGE("%s, Requested Patch handle does not exist", __func__);
+        status = -1;
+    }
+    pthread_mutex_unlock(&audio_loopback_mod->lock);
+
+    ALOGV("%s audio_extn_hw_loopback_release_audio_patch done, status(%d)", __func__,
+    status);
+    return status;
+}
+
+/* Find port config from patch database based on port info */
+struct audio_port_config* get_port_from_patch_db(port_info_t *port,
+                               patch_db_t *audio_patch_db, int *patch_num)
+{
+    int n=0, patch_index=-1;
+    struct audio_port_config *cur_port=NULL;
+
+    if (port->role == AUDIO_PORT_ROLE_SOURCE) {
+        for (n=0;n < audio_patch_db->num_patches;n++) {
+            cur_port = &(audio_patch_db->loopback_patch[n].loopback_source);
+            if ((cur_port->id == port->id) && (cur_port->type == port->type) && (
+               cur_port->role == port->role)) {
+                patch_index = n;
+                break;
+            }
+        }
+    } else if (port->role == AUDIO_PORT_ROLE_SINK) {
+        for (n=0;n < audio_patch_db->num_patches;n++) {
+            cur_port = &(audio_patch_db->loopback_patch[n].loopback_sink);
+            if ((cur_port->id == port->id) && (cur_port->type == port->type) && (
+               cur_port->role == port->role)) {
+                patch_index = n;
+                break;
+            }
+        }
+    }
+    *patch_num = patch_index;
+    return cur_port;
+}
+
+/* API to get port config based on port unique ID */
+int audio_extn_hw_loopback_get_audio_port(struct audio_hw_device *dev,
+                                    struct audio_port *port_in)
+{
+    int status = 0, n=0, patch_num=-1;
+    loopback_patch_t *active_loopback_patch = NULL;
+    port_info_t *port_info = NULL;
+    struct audio_port_config *port_out=NULL;
+    ALOGV("%s %d", __func__, __LINE__);
+
+    if ((audio_loopback_mod == NULL) || (dev == NULL)) {
+        ALOGE("%s, Invalid device", __func__);
+        status = -1;
+        return status;
+    }
+
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+
+    port_info->id = port_in->id;
+    port_info->role = port_in->role;              /* sink or source */
+    port_info->type = port_in->type;              /* device, mix ... */
+    port_out = get_port_from_patch_db(port_info, &audio_loopback_mod->patch_db,
+                                      &patch_num);
+    if (port_out == NULL) {
+        ALOGE("%s, Unable to find a valid matching port in patch \
+        database,exiting", __func__);
+        status = -EINVAL;
+        return status;
+    }
+
+    /* Fill port output properties before returning the port */
+    memcpy(&port_in->active_config,port_out, sizeof(struct audio_port_config));
+
+    /* Multiple fields are not valid for loopback extension usecases, TODO :
+    enhance for all patch handler cases. */
+    port_in->num_sample_rates = 1;
+    port_in->sample_rates[0] = port_out->sample_rate;
+    port_in->num_channel_masks = 1;
+    port_in->channel_masks[0] = port_out->channel_mask;
+    port_in->num_formats = 1;
+    port_in->formats[0] = port_out->format;
+    port_in->num_gains = 1;
+
+    pthread_mutex_unlock(&audio_loopback_mod->lock);
+    return status;
+}
+
+/* API to set port config based on port unique ID */
+int audio_extn_hw_loopback_set_audio_port_config(struct audio_hw_device *dev,
+                                        const struct audio_port_config *config)
+{
+    int status = 0, n=0, patch_num=-1;
+    loopback_patch_t *active_loopback_patch = NULL;
+    port_info_t *port_info = NULL;
+    struct audio_port_config *port_out=NULL;
+    ALOGV("%s %d", __func__, __LINE__);
+
+    if ((audio_loopback_mod == NULL) || (dev == NULL)) {
+        ALOGE("%s, Invalid device", __func__);
+        status = -1;
+        return status;
+    }
+
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+
+    port_info->id = config->id;
+    port_info->role = config->role;              /* sink or source */
+    port_info->type = config->type;              /* device, mix  */
+    port_out = get_port_from_patch_db(port_info, &audio_loopback_mod->patch_db
+                                    , &patch_num);
+
+    if (port_out == NULL) {
+        ALOGE("%s, Unable to find a valid matching port in patch \
+        database,exiting", __func__);
+        status = -1;
+        return status;
+    }
+
+    port_out->config_mask = config->config_mask;
+    port_out->channel_mask = config->channel_mask;
+    port_out->format = config->format;
+    port_out->gain = config->gain;
+    port_out->sample_rate = config->sample_rate;
+
+    /* Currently, port config is not used for anything,
+    need to restart session    */
+    pthread_mutex_unlock(&audio_loopback_mod->lock);
+    return status;
+}
+
+/* Loopback extension initialization, part of hal init sequence */
+int audio_extn_loopback_init(struct audio_device *adev)
+{
+    ALOGV("%s Audio loopback extension initializing", __func__);
+    int ret = 0, size = 0;
+
+    if (audio_loopback_mod != NULL) {
+        pthread_mutex_lock(&audio_loopback_mod->lock);
+        if (audio_loopback_mod->adev == adev) {
+            ALOGV("%s %d : Audio loopback module already exists", __func__,
+                    __LINE__);
+        } else {
+            ALOGV("%s %d : Audio loopback module called for invalid device",
+                    __func__, __LINE__);
+            ret = -EINVAL;
+        }
+        goto loopback_done;
+    }
+    audio_loopback_mod = malloc(sizeof(struct audio_loopback));
+    if (audio_loopback_mod == NULL) {
+        ALOGE("%s, out of memory", __func__);
+        ret = -ENOMEM;
+        goto loopback_done;
+    }
+
+    pthread_mutex_init(&audio_loopback_mod->lock,
+                        (const pthread_mutexattr_t *)NULL);
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+    audio_loopback_mod->adev = adev;
+
+    ret = init_patch_database(&audio_loopback_mod->patch_db);
+
+    audio_loopback_mod->uc_id = USECASE_AUDIO_TRANSCODE_LOOPBACK;
+    audio_loopback_mod->uc_type = TRANSCODE_LOOPBACK;
+
+loopback_done:
+    if (ret != 0) {
+        if (audio_loopback_mod != NULL) {
+            pthread_mutex_unlock(&audio_loopback_mod->lock);
+            pthread_mutex_destroy(&audio_loopback_mod->lock);
+            free(audio_loopback_mod);
+            audio_loopback_mod = NULL;
+        }
+    } else {
+        pthread_mutex_unlock(&audio_loopback_mod->lock);
+    }
+    ALOGV("%s Audio loopback extension initialized", __func__);
+    return ret;
+}
+
+void audio_extn_loopback_deinit(struct audio_device *adev)
+{
+    ALOGV("%s Audio loopback extension de-initializing", __func__);
+
+    if (audio_loopback_mod == NULL) {
+        ALOGE("%s, loopback module NULL, cannot deinitialize", __func__);
+        return;
+    }
+    pthread_mutex_lock(&audio_loopback_mod->lock);
+
+    if (audio_loopback_mod->adev == adev) {
+        if (audio_loopback_mod != NULL) {
+            pthread_mutex_unlock(&audio_loopback_mod->lock);
+            pthread_mutex_destroy(&audio_loopback_mod->lock);
+            free(audio_loopback_mod);
+            audio_loopback_mod = NULL;
+        }
+        return;
+    } else {
+        ALOGE("%s, loopback module not valid, cannot deinitialize", __func__);
+    }
+    pthread_mutex_unlock(&audio_loopback_mod->lock);
+    return;
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 3d3a5d7..6375ec5 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -263,6 +263,8 @@
     [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
     [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
     [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = "silence-playback",
+    /* Transcode loopback cases */
+    [USECASE_AUDIO_TRANSCODE_LOOPBACK] = "audio-transcode-loopback",
 };
 
 static const audio_usecase_t offload_usecases[] = {
@@ -989,12 +991,22 @@
                                                struct audio_usecase *new_uc,
                                                snd_device_t new_snd_device)
 {
-    audio_devices_t a1 = uc->stream.out->devices;
-    audio_devices_t a2 = new_uc->stream.out->devices;
+    audio_devices_t a1, a2;
 
     snd_device_t d1 = uc->out_snd_device;
     snd_device_t d2 = new_snd_device;
 
+    switch (uc->type) {
+        case TRANSCODE_LOOPBACK :
+            a1 = uc->stream.inout->out_config.devices;
+            a2 = new_uc->stream.inout->out_config.devices;
+            break;
+        default :
+            a1 = uc->stream.out->devices;
+            a2 = new_uc->stream.out->devices;
+            break;
+    }
+
     // Treat as a special case when a1 and a2 are not disjoint
     if ((a1 != a2) && (a1 & a2)) {
         snd_device_t d3[2];
@@ -1554,6 +1566,14 @@
                                                         usecase->stream.out);
         in_snd_device = platform_get_input_snd_device(adev->platform, usecase->stream.out->devices);
         usecase->devices = usecase->stream.out->devices;
+    } else if (usecase->type == TRANSCODE_LOOPBACK ) {
+        if (usecase->stream.inout == NULL) {
+            ALOGE("%s: stream.inout is NULL", __func__);
+            return -EINVAL;
+        }
+        out_snd_device = usecase->stream.inout->out_config.devices;
+        in_snd_device = usecase->stream.inout->in_config.devices;
+        usecase->devices = (out_snd_device | in_snd_device);
     } else {
         /*
          * If the voice call is active, use the sound devices of voice call usecase
@@ -5353,6 +5373,41 @@
     return;
 }
 
+int adev_create_audio_patch(struct audio_hw_device *dev,
+                            unsigned int num_sources,
+                            const struct audio_port_config *sources,
+                            unsigned int num_sinks,
+                            const struct audio_port_config *sinks,
+                            audio_patch_handle_t *handle)
+{
+
+
+     return audio_extn_hw_loopback_create_audio_patch(dev,
+                                         num_sources,
+                                         sources,
+                                         num_sinks,
+                                         sinks,
+                                         handle);
+
+}
+
+int adev_release_audio_patch(struct audio_hw_device *dev,
+                           audio_patch_handle_t handle)
+{
+    return audio_extn_hw_loopback_release_audio_patch(dev, handle);
+}
+
+int adev_get_audio_port(struct audio_hw_device *dev, struct audio_port *config)
+{
+    return audio_extn_hw_loopback_get_audio_port(dev, config);
+}
+
+int adev_set_audio_port_config(struct audio_hw_device *dev,
+                        const struct audio_port_config *config)
+{
+    return audio_extn_hw_loopback_set_audio_port_config(dev, config);
+}
+
 static int adev_dump(const audio_hw_device_t *device __unused,
                      int fd __unused)
 {
@@ -5386,6 +5441,7 @@
         qahwi_deinit(device);
         audio_extn_adsp_hdlr_deinit();
         audio_extn_snd_mon_deinit();
+        audio_extn_loopback_deinit(adev);
         free(device);
         adev = NULL;
     }
@@ -5498,6 +5554,10 @@
     adev->device.close_output_stream = adev_close_output_stream;
     adev->device.open_input_stream = adev_open_input_stream;
     adev->device.close_input_stream = adev_close_input_stream;
+    adev->device.create_audio_patch = adev_create_audio_patch;
+    adev->device.release_audio_patch = adev_release_audio_patch;
+    adev->device.get_audio_port = adev_get_audio_port;
+    adev->device.set_audio_port_config = adev_set_audio_port_config;
     adev->device.dump = adev_dump;
 
     /* Set the default route before the PCM stream is opened */
@@ -5564,6 +5624,7 @@
     audio_extn_init(adev);
     audio_extn_listen_init(adev, adev->snd_card);
     audio_extn_gef_init(adev);
+    audio_extn_loopback_init(adev);
 
     if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
         adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 3980ab6..20edc99 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -81,6 +81,12 @@
 #define DEFAULT_HDMI_OUT_SAMPLE_RATE 48000
 #define DEFAULT_HDMI_OUT_FORMAT AUDIO_FORMAT_PCM_16_BIT
 
+#define SND_CARD_STATE_OFFLINE 0
+#define SND_CARD_STATE_ONLINE 1
+
+#define STREAM_DIRECTION_IN 0
+#define STREAM_DIRECTION_OUT 1
+
 #define MAX_PERF_LOCK_OPTS 20
 
 #define MAX_STREAM_PROFILE_STR_LEN 32
@@ -157,6 +163,7 @@
 
     USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
 
+    USECASE_AUDIO_TRANSCODE_LOOPBACK,
     AUDIO_USECASE_MAX
 };
 
@@ -204,6 +211,20 @@
     int app_type;
 };
 
+struct stream_config {
+    unsigned int sample_rate;
+    audio_channel_mask_t channel_mask;
+    audio_format_t format;
+    audio_devices_t devices;
+    unsigned int bit_width;
+};
+struct stream_inout {
+    pthread_mutex_t lock; /* see note below on mutex acquisition order */
+    pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
+    pthread_cond_t  cond;
+    struct stream_config in_config;
+    struct stream_config out_config;
+};
 struct stream_out {
     struct audio_stream_out stream;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -310,12 +331,14 @@
     PCM_CAPTURE,
     VOICE_CALL,
     VOIP_CALL,
-    PCM_HFP_CALL
+    PCM_HFP_CALL,
+    TRANSCODE_LOOPBACK
 } usecase_type_t;
 
 union stream_ptr {
     struct stream_in *in;
     struct stream_out *out;
+    struct stream_inout *inout;
 };
 
 struct audio_usecase {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index f7a0335..6a0338f 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -370,6 +370,8 @@
     [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
                                         AFE_PROXY_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
+    [USECASE_AUDIO_TRANSCODE_LOOPBACK] = {TRANSCODE_LOOPBACK_RX_DEV_ID, TRANSCODE_LOOPBACK_TX_DEV_ID},
+
 };
 
 /* Array to store sound devices */
@@ -5732,10 +5734,19 @@
 
     backend_idx = platform_get_backend_index(snd_device);
 
-    backend_cfg.bit_width = usecase->stream.out->bit_width;
-    backend_cfg.sample_rate = usecase->stream.out->sample_rate;
-    backend_cfg.format = usecase->stream.out->format;
-    backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+    if (usecase->type == TRANSCODE_LOOPBACK) {
+        backend_cfg.bit_width = usecase->stream.inout->out_config.bit_width;
+        backend_cfg.sample_rate = usecase->stream.inout->out_config.sample_rate;
+        backend_cfg.format = usecase->stream.inout->out_config.format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(
+                usecase->stream.inout->out_config.channel_mask);
+    } else {
+        backend_cfg.bit_width = usecase->stream.out->bit_width;
+        backend_cfg.sample_rate = usecase->stream.out->sample_rate;
+        backend_cfg.format = usecase->stream.out->format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+    }
+
     /*this is populated by check_codec_backend_cfg hence set default value to false*/
     backend_cfg.passthrough_enabled = false;
 
@@ -5866,7 +5877,14 @@
     struct audio_backend_cfg backend_cfg;
 
     backend_cfg.passthrough_enabled = false;
-    if(usecase->type == PCM_CAPTURE) {
+
+    if (usecase->type == TRANSCODE_LOOPBACK) {
+        backend_cfg.bit_width = usecase->stream.inout->in_config.bit_width;
+        backend_cfg.sample_rate = usecase->stream.inout->in_config.sample_rate;
+        backend_cfg.format = usecase->stream.inout->in_config.format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(
+                usecase->stream.inout->in_config.channel_mask);
+    } else if (usecase->type == PCM_CAPTURE) {
         backend_cfg.sample_rate= usecase->stream.in->sample_rate;
         backend_cfg.bit_width= usecase->stream.in->bit_width;
         backend_cfg.format= usecase->stream.in->format;
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index 28fe62b..5aa02a0 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -345,6 +345,9 @@
 #define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
 #define AFE_PROXY_RECORD_PCM_DEVICE 8
 
+#define TRANSCODE_LOOPBACK_RX_DEV_ID 43
+#define TRANSCODE_LOOPBACK_TX_DEV_ID 44
+
 #define PLATFORM_MAX_MIC_COUNT "input_mic_max_count"
 #define PLATFORM_DEFAULT_MIC_COUNT 2
 
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index ad3927c..fdabbd5 100755
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -349,6 +349,7 @@
     [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
                                         AFE_PROXY_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
+    [USECASE_AUDIO_TRANSCODE_LOOPBACK] = {TRANSCODE_LOOPBACK_RX_DEV_ID, TRANSCODE_LOOPBACK_TX_DEV_ID},
 
 };
 
@@ -5583,10 +5584,19 @@
 
     backend_idx = platform_get_backend_index(snd_device);
 
-    backend_cfg.bit_width = usecase->stream.out->bit_width;
-    backend_cfg.sample_rate = usecase->stream.out->sample_rate;
-    backend_cfg.format = usecase->stream.out->format;
-    backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+    if (usecase->type == TRANSCODE_LOOPBACK) {
+        backend_cfg.bit_width = usecase->stream.inout->out_config.bit_width;
+        backend_cfg.sample_rate = usecase->stream.inout->out_config.sample_rate;
+        backend_cfg.format = usecase->stream.inout->out_config.format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(
+                usecase->stream.inout->out_config.channel_mask);
+    } else {
+        backend_cfg.bit_width = usecase->stream.out->bit_width;
+        backend_cfg.sample_rate = usecase->stream.out->sample_rate;
+        backend_cfg.format = usecase->stream.out->format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+    }
+
     /*this is populated by check_codec_backend_cfg hence set default value to false*/
     backend_cfg.passthrough_enabled = false;
 
@@ -5716,7 +5726,14 @@
     struct audio_backend_cfg backend_cfg;
 
     backend_cfg.passthrough_enabled = false;
-    if(usecase->type == PCM_CAPTURE) {
+
+    if (usecase->type == TRANSCODE_LOOPBACK) {
+        backend_cfg.bit_width = usecase->stream.inout->in_config.bit_width;
+        backend_cfg.sample_rate = usecase->stream.inout->in_config.sample_rate;
+        backend_cfg.format = usecase->stream.inout->in_config.format;
+        backend_cfg.channels = audio_channel_count_from_out_mask(
+                usecase->stream.inout->in_config.channel_mask);
+    } else if (usecase->type == PCM_CAPTURE) {
         backend_cfg.sample_rate= usecase->stream.in->sample_rate;
         backend_cfg.bit_width= usecase->stream.in->bit_width;
         backend_cfg.format= usecase->stream.in->format;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index e4d797e..7329e7a 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -451,6 +451,9 @@
 #define HFP_ASM_RX_TX 24
 #endif
 
+#define TRANSCODE_LOOPBACK_RX_DEV_ID 43
+#define TRANSCODE_LOOPBACK_TX_DEV_ID 44
+
 #ifdef PLATFORM_APQ8084
 #define FM_RX_VOLUME "Quat MI2S FM RX Volume"
 #elif PLATFORM_MSM8994
diff --git a/qahw_api/inc/qahw_api.h b/qahw_api/inc/qahw_api.h
index 0f3fc50..5e0e661 100644
--- a/qahw_api/inc/qahw_api.h
+++ b/qahw_api/inc/qahw_api.h
@@ -444,6 +444,33 @@
                         qahw_param_id param_id,
                         qahw_param_payload *payload);
 
+/* Creates an audio patch between several source and sink ports.
+ * The handle is allocated by the HAL and should be unique for this
+ * audio HAL module.
+ */
+int qahw_create_audio_patch(qahw_module_handle_t *hw_module,
+                        unsigned int num_sources,
+                        const struct audio_port_config *sources,
+                        unsigned int num_sinks,
+                        const struct audio_port_config *sinks,
+                        audio_patch_handle_t *handle);
+
+/* Release an audio patch */
+int qahw_release_audio_patch(qahw_module_handle_t *hw_module,
+                        audio_patch_handle_t handle);
+/* Fills the list of supported attributes for a given audio port.
+ * As input, "port" contains the information (type, role, address etc...)
+ * needed by the HAL to identify the port.
+ * As output, "port" contains possible attributes (sampling rates, formats,
+ * channel masks, gain controllers...) for this port.
+ */
+int qahw_get_audio_port(qahw_module_handle_t *hw_module,
+                      struct audio_port *port);
+
+/* Set audio port configuration */
+int qahw_set_audio_port_config(qahw_module_handle_t *hw_module,
+                     const struct audio_port_config *config);
+
 __END_DECLS
 
 #endif  // QTI_AUDIO_HAL_API_H
diff --git a/qahw_api/src/qahw.c b/qahw_api/src/qahw.c
index df69df5..b5a85d7 100644
--- a/qahw_api/src/qahw.c
+++ b/qahw_api/src/qahw.c
@@ -1363,6 +1363,146 @@
      return ret;
 }
 
+/* Creates an audio patch between several source and sink ports.
+ * The handle is allocated by the HAL and should be unique for this
+ * audio HAL module.
+ */
+int qahw_create_audio_patch(qahw_module_handle_t *hw_module,
+                        unsigned int num_sources,
+                        const struct audio_port_config *sources,
+                        unsigned int num_sinks,
+                        const struct audio_port_config *sinks,
+                        audio_patch_handle_t *handle)
+{
+    int ret = 0;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    if (qahw_module->audio_device->create_audio_patch) {
+        ret = qahw_module->audio_device->create_audio_patch(
+                        qahw_module->audio_device,
+                        num_sources,
+                        sources,
+                        num_sinks,
+                        sinks,
+                        handle);
+    } else {
+         ret = -ENOSYS;
+         ALOGE("%s not supported\n",__func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+     return ret;
+}
+
+/* Release an audio patch */
+int qahw_release_audio_patch(qahw_module_handle_t *hw_module,
+                        audio_patch_handle_t handle)
+{
+    int ret = 0;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    if (qahw_module->audio_device->release_audio_patch) {
+        ret = qahw_module->audio_device->release_audio_patch(
+                        qahw_module->audio_device,
+                        handle);
+    } else {
+         ret = -ENOSYS;
+         ALOGE("%s not supported\n",__func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+     return ret;
+}
+
+/* Fills the list of supported attributes for a given audio port.
+ * As input, "port" contains the information (type, role, address etc...)
+ * needed by the HAL to identify the port.
+ * As output, "port" contains possible attributes (sampling rates, formats,
+ * channel masks, gain controllers...) for this port.
+ */
+int qahw_get_audio_port(qahw_module_handle_t *hw_module,
+                      struct audio_port *port)
+{
+    int ret = 0;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    if (qahw_module->audio_device->get_audio_port) {
+        ret = qahw_module->audio_device->get_audio_port(
+                    qahw_module->audio_device,
+                    port);
+    } else {
+         ret = -ENOSYS;
+         ALOGE("%s not supported\n",__func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+     return ret;
+}
+
+/* Set audio port configuration */
+int qahw_set_audio_port_config(qahw_module_handle_t *hw_module,
+                     const struct audio_port_config *config)
+{
+    int ret = 0;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    if (qahw_module->audio_device->set_audio_port_config) {
+        ret = qahw_module->audio_device->set_audio_port_config(
+                    qahw_module->audio_device,
+                        config);
+    } else {
+         ret = -ENOSYS;
+         ALOGE("%s not supported\n",__func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+     return ret;
+}
+
 /* Returns audio input buffer size according to parameters passed or
  * 0 if one of the parameters is not supported.
  * See also get_buffer_size which is for a particular stream.
diff --git a/qahw_api/test/Makefile.am b/qahw_api/test/Makefile.am
index abccdce..d87962b 100644
--- a/qahw_api/test/Makefile.am
+++ b/qahw_api/test/Makefile.am
@@ -19,3 +19,13 @@
 hal_rec_test_CPPFLAGS = -Dstrlcat=g_strlcat $(GLIB_CFLAGS) -include glib.h
 hal_rec_test_CPPFLAGS += $(REC_CPPFLAGS) $(REC_INCLUDES)
 hal_rec_test_LDADD = -lutils ../libqahw.la $(GLIB_LIBS)
+
+bin_PROGRAMS += trans_loopback_test
+
+trans_loopback_test_INCLUDES = -I $(top_srcdir)/qahw_api/inc
+
+trans_loopback_test_SOURCES = trans_loopback_test.c
+
+trans_loopback_test_CFLAGS  = $(CFLAGS) -Wno-sign-compare -Werror
+trans_loopback_test_CFLAGS  += $(trans_loopback_test_INCLUDES)
+trans_loopback_test_LDADD = -llog  -lutils ../libqahw.la -lcutils
diff --git a/qahw_api/test/trans_loopback_test.c b/qahw_api/test/trans_loopback_test.c
new file mode 100644
index 0000000..c0435c7
--- /dev/null
+++ b/qahw_api/test/trans_loopback_test.c
@@ -0,0 +1,391 @@
+/*
+* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test app to capture event updates from kernel */
+/*#define LOG_NDEBUG 0*/
+#include <fcntl.h>
+#include <linux/netlink.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <cutils/list.h>
+#include <hardware/audio.h>
+#include <system/audio.h>
+#include "qahw_api.h"
+#include "qahw_defs.h"
+
+
+typedef struct tlb_hdmi_config {
+    int hdmi_conn_state;
+    int hdmi_audio_state;
+    int hdmi_sample_rate;
+    int hdmi_num_channels;
+} tlb_hdmi_config_t;
+
+const char tlb_hdmi_in_audio_sys_path[] =
+"/sys/devices/virtual/switch/hpd_state/state";
+const char tlb_hdmi_in_audio_dev_path[] = "/devices/virtual/switch/hpd_state";
+const char tlb_hdmi_in_audio_state_sys_path[] =
+"/sys/devices/virtual/switch/audio_state/state";
+const char tlb_hdmi_in_audio_state_dev_path[] =
+"/devices/virtual/switch/audio_state";
+const char tlb_hdmi_in_audio_sample_rate_sys_path[] =
+"/sys/devices/virtual/switch/sample_rate/state";
+const char tlb_hdmi_in_audio_sample_rate_dev_path[] =
+"/devices/virtual/switch/sample_rate";
+const char tlb_hdmi_in_audio_channel_sys_path[] =
+"/sys/devices/virtual/switch/channels/state";
+const char tlb_hdmi_in_audio_channel_dev_path[] =
+"/devices/virtual/switch/channels";
+
+qahw_module_handle_t *primary_hal_handle = NULL;
+
+FILE * log_file = NULL;
+volatile bool stop_playback = false;
+const char *log_filename = NULL;
+
+#define TRANSCODE_LOOPBACK_SOURCE_PORT_ID 0x4C00
+#define TRANSCODE_LOOPBACK_SINK_PORT_ID 0x4D00
+
+#define MAX_MODULE_NAME_LENGTH  100
+
+typedef enum source_port_type {
+    SOURCE_PORT_NONE,
+    SOURCE_PORT_HDMI,
+    SOURCE_PORT_SPDIF,
+    SOURCE_PORT_MIC
+} source_port_type_t;
+
+typedef struct trnscode_loopback_config {
+    qahw_module_handle_t *hal_handle;
+    audio_devices_t devices;
+    struct audio_port_config source_config;
+    struct audio_port_config sink_config;
+    audio_patch_handle_t patch_handle;
+} transcode_loopback_config_t;
+
+transcode_loopback_config_t g_trnscode_loopback_config;
+
+
+void init_transcode_loopback_config(transcode_loopback_config_t **p_transcode_loopback_config)
+{
+    fprintf(log_file,"\nInitializing global transcode loopback config\n");
+    g_trnscode_loopback_config.hal_handle = NULL;
+
+    audio_devices_t out_device = AUDIO_DEVICE_OUT_SPEAKER; // Get output device mask from connected device
+    audio_devices_t in_device = AUDIO_DEVICE_IN_HDMI;
+
+    g_trnscode_loopback_config.devices = (out_device | in_device);
+
+    /* Patch source port config init */
+    g_trnscode_loopback_config.source_config.id = TRANSCODE_LOOPBACK_SOURCE_PORT_ID;
+    g_trnscode_loopback_config.source_config.role = AUDIO_PORT_ROLE_SOURCE;
+    g_trnscode_loopback_config.source_config.type = AUDIO_PORT_TYPE_DEVICE;
+    g_trnscode_loopback_config.source_config.config_mask =
+                        (AUDIO_PORT_CONFIG_ALL ^ AUDIO_PORT_CONFIG_GAIN);
+    g_trnscode_loopback_config.source_config.sample_rate = 48000;
+    g_trnscode_loopback_config.source_config.channel_mask =
+                        AUDIO_CHANNEL_OUT_STEREO; // Using OUT as this is digital data and not mic capture
+    g_trnscode_loopback_config.source_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    /*TODO: add gain */
+    g_trnscode_loopback_config.source_config.ext.device.hw_module =
+                        AUDIO_MODULE_HANDLE_NONE;
+    g_trnscode_loopback_config.source_config.ext.device.type = in_device;
+
+    /* Patch sink port config init */
+    g_trnscode_loopback_config.sink_config.id = TRANSCODE_LOOPBACK_SINK_PORT_ID;
+    g_trnscode_loopback_config.sink_config.role = AUDIO_PORT_ROLE_SINK;
+    g_trnscode_loopback_config.sink_config.type = AUDIO_PORT_TYPE_DEVICE;
+    g_trnscode_loopback_config.sink_config.config_mask =
+                            (AUDIO_PORT_CONFIG_ALL ^ AUDIO_PORT_CONFIG_GAIN);
+    g_trnscode_loopback_config.sink_config.sample_rate = 48000;
+    g_trnscode_loopback_config.sink_config.channel_mask =
+                             AUDIO_CHANNEL_OUT_STEREO;
+    g_trnscode_loopback_config.sink_config.format = AUDIO_FORMAT_PCM_16_BIT;
+
+    g_trnscode_loopback_config.sink_config.ext.device.hw_module =
+                            AUDIO_MODULE_HANDLE_NONE;
+    g_trnscode_loopback_config.sink_config.ext.device.type = out_device;
+
+    /* Init patch handle */
+    g_trnscode_loopback_config.patch_handle = AUDIO_PATCH_HANDLE_NONE;
+
+    *p_transcode_loopback_config = &g_trnscode_loopback_config;
+
+    fprintf(log_file,"\nDone Initializing global transcode loopback config\n");
+}
+
+void deinit_transcode_loopback_config()
+{
+    g_trnscode_loopback_config.hal_handle = NULL;
+
+    g_trnscode_loopback_config.devices = AUDIO_DEVICE_NONE;
+}
+
+void read_data_from_fd(const char* path, int *value)
+{
+    int fd = -1;
+    char buf[16];
+    int ret;
+
+    fd = open(path, O_RDONLY, 0);
+    if (fd < 0) {
+        ALOGE("Unable open fd for file %s", path);
+        return;
+    }
+
+    ret = read(fd, buf, 15);
+    if (ret < 0) {
+        ALOGE("File %s Data is empty", path);
+        close(fd);
+        return;
+    }
+
+    buf[ret] = '\0';
+    *value = atoi(buf);
+    close(fd);
+}
+
+int actual_channels_from_audio_infoframe(int infoframe_channels)
+{
+    if (infoframe_channels > 0 && infoframe_channels < 8) {
+      /* refer CEA-861-D Table 17 Audio InfoFrame Data Byte 1 */
+        return (infoframe_channels+1);
+    }
+    fprintf(log_file,"\nInfoframe channels 0, need to get from stream, returning default 2\n");
+    return 2;
+}
+
+int read_and_set_source_config(source_port_type_t source_port_type,
+                               struct audio_port_config *dest_port_config)
+{
+    int rc=0, channels = 2;
+    tlb_hdmi_config_t hdmi_config = {0};
+    switch(source_port_type)
+    {
+    case SOURCE_PORT_HDMI :
+    read_data_from_fd(tlb_hdmi_in_audio_sys_path, &hdmi_config.hdmi_conn_state);
+    read_data_from_fd(tlb_hdmi_in_audio_state_sys_path,
+                      &hdmi_config.hdmi_audio_state);
+    read_data_from_fd(tlb_hdmi_in_audio_sample_rate_sys_path,
+                      &hdmi_config.hdmi_sample_rate);
+    read_data_from_fd(tlb_hdmi_in_audio_channel_sys_path,
+                      &hdmi_config.hdmi_num_channels);
+
+    channels = actual_channels_from_audio_infoframe(hdmi_config.hdmi_num_channels);
+    fprintf(log_file,"\nHDMI In state: %d, audio_state: %d, samplerate: %d, channels: %d\n",
+            hdmi_config.hdmi_conn_state, hdmi_config.hdmi_audio_state,
+            hdmi_config.hdmi_sample_rate, channels);
+
+    ALOGD("HDMI In state: %d, audio_state: %d, samplerate: %d, channels: %d",
+           hdmi_config.hdmi_conn_state, hdmi_config.hdmi_audio_state,
+           hdmi_config.hdmi_sample_rate, channels);
+
+        dest_port_config->sample_rate = hdmi_config.hdmi_sample_rate;
+        switch(channels) {
+        case 2 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+            break;
+        case 3 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_2POINT1;
+            break;
+        case 4 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_QUAD;
+            break;
+        case 5 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_PENTA;
+            break;
+        case 6 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+            break;
+        case 7 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_6POINT1;
+            break;
+        case 8 :
+            dest_port_config->channel_mask = AUDIO_CHANNEL_OUT_7POINT1;
+            break;
+        default :
+            fprintf(log_file,"\nUnsupported number of channels in source port %d\n",
+                    channels);
+            rc = -1;
+            break;
+        }
+        break;
+    default :
+        fprintf(log_file,"\nUnsupported port type, cannot set configuration\n");
+        rc = -1;
+        break;
+    }
+    return rc;
+}
+
+void stop_transcode_loopback(
+            transcode_loopback_config_t *transcode_loopback_config)
+{
+    qahw_release_audio_patch(transcode_loopback_config->hal_handle,
+                             transcode_loopback_config->patch_handle);
+}
+
+int create_run_transcode_loopback(
+            transcode_loopback_config_t *transcode_loopback_config)
+{
+    int rc=0;
+    qahw_module_handle_t *module_handle = transcode_loopback_config->hal_handle;
+
+
+    fprintf(log_file,"\nCreating audio patch\n");
+    rc = qahw_create_audio_patch(module_handle,
+                        1,
+                        &transcode_loopback_config->source_config,
+                        1,
+                        &transcode_loopback_config->sink_config,
+                        &transcode_loopback_config->patch_handle);
+    fprintf(log_file,"\nCreate patch returned %d\n",rc);
+    return rc;
+}
+
+static audio_hw_device_t *load_hal(audio_devices_t dev)
+{
+    if (primary_hal_handle == NULL) {
+        primary_hal_handle = qahw_load_module(QAHW_MODULE_ID_PRIMARY);
+        if (primary_hal_handle == NULL) {
+            fprintf(stderr,"failure in Loading primary HAL\n");
+            goto exit;
+		}
+    }
+
+exit:
+    return primary_hal_handle;
+}
+
+/*
+* this function unloads all the loaded hal modules so this should be called
+* after all the stream playback are concluded.
+*/
+static int unload_hals(void) {
+    if (primary_hal_handle) {
+        qahw_unload_module(primary_hal_handle);
+        primary_hal_handle = NULL;
+    }
+    return 1;
+}
+
+int main(int argc, char *argv[]) {
+
+    int status = 0,play_duration_in_seconds = 30;
+    source_port_type_t source_port_type = SOURCE_PORT_NONE;
+    log_file = stdout;
+
+    fprintf(log_file,"\nTranscode loopback test begin\n");
+    if (argc == 2) {
+        play_duration_in_seconds = atoi(argv[1]);
+        if (play_duration_in_seconds < 0 | play_duration_in_seconds > 3600) {
+            fprintf(log_file,
+                    "\nPlayback duration %s invalid or unsupported(range : 1 to 3600 )\n",
+                    argv[1]);
+            goto usage;
+        }
+    } else {
+        goto usage;
+    }
+
+    transcode_loopback_config_t    *transcode_loopback_config = NULL;
+    transcode_loopback_config_t *temp = NULL;
+
+    /* Initialize global transcode loopback struct */
+    init_transcode_loopback_config(&temp);
+    transcode_loopback_config = &g_trnscode_loopback_config;
+
+    /* Load HAL */
+    fprintf(log_file,"\nLoading HAL for loopback usecase begin\n");
+    primary_hal_handle = load_hal(transcode_loopback_config->devices);
+    if (primary_hal_handle == NULL) {
+        fprintf(log_file,"\n Failure in Loading HAL, exiting\n");
+        goto exit_transcode_loopback_test;
+    }
+    transcode_loopback_config->hal_handle = primary_hal_handle;
+    fprintf(log_file,"\nLoading HAL for loopback usecase done\n");
+
+    /* Configuration assuming source port is HDMI */
+    {
+        source_port_type = SOURCE_PORT_HDMI;
+        fprintf(log_file,"\nSet port config being\n");
+        status = read_and_set_source_config(source_port_type,&transcode_loopback_config->source_config);
+        fprintf(log_file,"\nSet port config end\n");
+
+        if (status != 0) {
+            fprintf(log_file,"\nFailed to set port config, exiting\n");
+            goto exit_transcode_loopback_test;
+        }
+    }
+
+    /* Open transcode loopback session */
+    fprintf(log_file,"\nCreate and start transcode loopback session begin\n");
+    status = create_run_transcode_loopback(transcode_loopback_config);
+    fprintf(log_file,"\nCreate and start transcode loopback session end\n");
+
+    /* If session opened successfully, run for a duration and close session */
+    if (status == 0) {
+        fprintf(log_file,"\nSleeping for %d seconds for loopback session to run\n",
+                play_duration_in_seconds);
+        usleep(play_duration_in_seconds*1000*1000);
+
+        fprintf(log_file,"\nStop transcode loopback session begin\n");
+        stop_transcode_loopback(transcode_loopback_config);
+        fprintf(log_file,"\nStop transcode loopback session end\n");
+    } else {
+        fprintf(log_file,"\nEncountered error %d in creating transcode loopback session\n",
+              status);
+    }
+
+exit_transcode_loopback_test:
+    fprintf(log_file,"\nUnLoading HAL for loopback usecase begin\n");
+    unload_hals();
+    fprintf(log_file,"\nUnLoading HAL for loopback usecase end\n");
+
+    deinit_transcode_loopback_config();
+    transcode_loopback_config = NULL;
+
+    fprintf(log_file,"\nTranscode loopback test end\n");
+    return 0;
+usage:
+    fprintf(log_file,"\nInvald arguments\n");
+    fprintf(log_file,"\nUsage : trans_loopback_test <duration_in_seconds>\n");
+    fprintf(log_file,"\nExample to play for 1 minute : trans_loopback_test 60\n");
+    return 0;
+}
+
+