hal: Add support for 3-mic surround sound recording

Add support for stereo surround sound recording using 3 mics.

Change-Id: I741dd5507d72ac45dff8006d3875abe8dbdc2df2
diff --git a/hal/Android.mk b/hal/Android.mk
index 4bd87f9..7ae554c 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -108,6 +108,7 @@
     LOCAL_CFLAGS += -DSSR_ENABLED
     LOCAL_SRC_FILES += audio_extn/ssr.c
     LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/surround_sound/
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/surround_sound_3mic/
     LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/common/inc/
 endif
 
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index fe959be..e7822b3 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -565,6 +565,7 @@
    audio_extn_fm_set_parameters(adev, parms);
    audio_extn_sound_trigger_set_parameters(adev, parms);
    audio_extn_listen_set_parameters(adev, parms);
+   audio_extn_ssr_set_parameters(adev, parms);
    audio_extn_hfp_set_parameters(adev, parms);
    audio_extn_dts_eagle_set_parameters(adev, parms);
    audio_extn_ddp_set_parameters(adev, parms);
@@ -584,6 +585,7 @@
     char *kv_pairs = NULL;
     audio_extn_get_afe_proxy_parameters(query, reply);
     audio_extn_get_fluence_parameters(adev, query, reply);
+    audio_extn_ssr_get_parameters(adev, query, reply);
     get_active_offload_usecases(adev, query, reply);
     audio_extn_dts_eagle_get_parameters(adev, query, reply);
     audio_extn_hpx_get_parameters(query, reply);
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 5231e93..97fbbbd 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -175,18 +175,28 @@
 #endif
 
 #ifndef SSR_ENABLED
-#define audio_extn_ssr_init(in)                       (0)
+#define audio_extn_ssr_init(in, num_out_chan)         (0)
 #define audio_extn_ssr_deinit()                       (0)
 #define audio_extn_ssr_update_enabled()               (0)
 #define audio_extn_ssr_get_enabled()                  (0)
 #define audio_extn_ssr_read(stream, buffer, bytes)    (0)
+#define audio_extn_ssr_set_parameters(adev, parms)    (0)
+#define audio_extn_ssr_get_parameters(adev, parms, reply) (0)
+#define audio_extn_ssr_get_stream()                   (0)
 #else
-int32_t audio_extn_ssr_init(struct stream_in *in);
+int32_t audio_extn_ssr_init(struct stream_in *in,
+                            int num_out_chan);
 int32_t audio_extn_ssr_deinit();
 void audio_extn_ssr_update_enabled();
 bool audio_extn_ssr_get_enabled();
 int32_t audio_extn_ssr_read(struct audio_stream_in *stream,
                        void *buffer, size_t bytes);
+void audio_extn_ssr_set_parameters(struct audio_device *adev,
+                                   struct str_parms *parms);
+void audio_extn_ssr_get_parameters(const struct audio_device *adev,
+                                   struct str_parms *query,
+                                   struct str_parms *reply);
+struct stream_in *audio_extn_ssr_get_stream();
 #endif
 
 #ifndef HW_VARIANTS_ENABLED
diff --git a/hal/audio_extn/ssr.c b/hal/audio_extn/ssr.c
index 07b28b0..c1ebd99 100644
--- a/hal/audio_extn/ssr.c
+++ b/hal/audio_extn/ssr.c
@@ -27,20 +27,26 @@
 #include <dlfcn.h>
 #include <cutils/str_parms.h>
 #include <cutils/log.h>
+#include <pthread.h>
+#include <cutils/sched_policy.h>
+#include <sys/resource.h>
+#include <system/thread_defs.h>
 
 #include "audio_hw.h"
+#include "audio_extn.h"
 #include "platform.h"
 #include "platform_api.h"
 #include "surround_filters_interface.h"
+#include "surround_rec_interface.h"
 
 #ifdef SSR_ENABLED
 #define COEFF_ARRAY_SIZE            4
 #define FILT_SIZE                   ((512+1)* 6)  /* # ((FFT bins)/2+1)*numOutputs */
-#define SSR_CHANNEL_INPUT_NUM       4
 #define SSR_CHANNEL_OUTPUT_NUM      6
-#define SSR_PERIOD_COUNT            8
-#define SSR_PERIOD_SIZE             512
-#define SSR_INPUT_FRAME_SIZE        (SSR_PERIOD_SIZE * SSR_PERIOD_COUNT)
+#define SSR_PERIOD_SIZE             240
+
+#define NUM_IN_BUFS                 4
+#define NUM_OUT_BUFS                4
 
 #define SURROUND_FILE_1R "/system/etc/surround_sound/filter1r.pcm"
 #define SURROUND_FILE_2R "/system/etc/surround_sound/filter2r.pcm"
@@ -53,6 +59,11 @@
 #define SURROUND_FILE_4I "/system/etc/surround_sound/filter4i.pcm"
 
 #define LIB_SURROUND_PROC       "libsurround_proc.so"
+#define LIB_SURROUND_3MIC_PROC  "libsurround_3mic_proc.so"
+#define LIB_DRC                 "libdrc.so"
+
+#define AUDIO_PARAMETER_SSRMODE_ON        "ssrOn"
+
 
 typedef int  (*surround_filters_init_t)(void *, int, int, Word16 **,
                                         Word16 **, int, int, int, Profiler *);
@@ -60,20 +71,73 @@
 typedef int  (*surround_filters_set_channel_map_t)(void *, const int *);
 typedef void (*surround_filters_intl_process_t)(void *, Word16 *, Word16 *);
 
+typedef const get_param_data_t* (*surround_rec_get_get_param_data_t)(void);
+typedef const set_param_data_t* (*surround_rec_get_set_param_data_t)(void);
+typedef int (*surround_rec_init_t)(void **, int, int, int, int, const char *);
+typedef void (*surround_rec_deinit_t)(void *);
+typedef void (*surround_rec_process_t)(void *, const int16_t *, int16_t *);
+
+typedef int (*drc_init_t)(void **, int, int, const char *);
+typedef void (*drc_deinit_t)(void *);
+typedef int (*drc_process_t)(void *, const int16_t *, int16_t *);
+
+struct pcm_buffer {
+    void *data;
+    int length;
+};
+
+struct pcm_buffer_queue {
+    struct pcm_buffer_queue *next;
+    struct pcm_buffer buffer;
+};
+
 struct ssr_module {
+    int                 ssr_3mic;
+    int                 num_out_chan;
     FILE                *fp_4ch;
     FILE                *fp_6ch;
     Word16             **real_coeffs;
     Word16             **imag_coeffs;
     void                *surround_obj;
-    Word16             *surround_raw_buffer;
-    bool                is_ssr_enabled;
+    Word16              *surround_raw_buffer;
+    int                  surround_raw_buffer_size;
+    bool                 is_ssr_enabled;
+    struct stream_in    *in;
+    void                *drc_obj;
 
     void *surround_filters_handle;
     surround_filters_init_t surround_filters_init;
     surround_filters_release_t surround_filters_release;
     surround_filters_set_channel_map_t surround_filters_set_channel_map;
     surround_filters_intl_process_t surround_filters_intl_process;
+
+    void *surround_rec_handle;
+    surround_rec_get_get_param_data_t surround_rec_get_get_param_data;
+    surround_rec_get_set_param_data_t surround_rec_get_set_param_data;
+    surround_rec_init_t surround_rec_init;
+    surround_rec_deinit_t surround_rec_deinit;
+    surround_rec_process_t surround_rec_process;
+
+    void *drc_handle;
+    drc_init_t drc_init;
+    drc_deinit_t drc_deinit;
+    drc_process_t drc_process;
+
+    pthread_t ssr_process_thread;
+    bool ssr_process_thread_started;
+    bool ssr_process_thread_stop;
+    struct pcm_buffer_queue in_buf_nodes[NUM_IN_BUFS];
+    struct pcm_buffer_queue out_buf_nodes[NUM_OUT_BUFS];
+    void *in_buf_data;
+    void *out_buf_data;
+    struct pcm_buffer_queue *out_buf_free;
+    struct pcm_buffer_queue *out_buf;
+    struct pcm_buffer_queue *in_buf_free;
+    struct pcm_buffer_queue *in_buf;
+    pthread_mutex_t ssr_process_lock;
+    pthread_cond_t cond_process;
+    pthread_cond_t cond_read;
+    bool is_ssr_mode_on;
 };
 
 static struct ssr_module ssrmod = {
@@ -83,15 +147,45 @@
     .imag_coeffs = NULL,
     .surround_obj = NULL,
     .surround_raw_buffer = NULL,
+    .surround_raw_buffer_size = 0,
     .is_ssr_enabled = 0,
+    .in = NULL,
+    .drc_obj = NULL,
 
     .surround_filters_handle = NULL,
     .surround_filters_init = NULL,
     .surround_filters_release = NULL,
     .surround_filters_set_channel_map = NULL,
     .surround_filters_intl_process = NULL,
+
+    .surround_rec_handle = NULL,
+    .surround_rec_get_get_param_data = NULL,
+    .surround_rec_get_set_param_data = NULL,
+    .surround_rec_init = NULL,
+    .surround_rec_deinit = NULL,
+    .surround_rec_process = NULL,
+
+    .drc_handle = NULL,
+    .drc_init = NULL,
+    .drc_deinit = NULL,
+    .drc_process = NULL,
+
+    .ssr_process_thread_stop = 0,
+    .ssr_process_thread_started = 0,
+    .in_buf_data = NULL,
+    .out_buf_data = NULL,
+    .out_buf_free = NULL,
+    .out_buf = NULL,
+    .in_buf_free = NULL,
+    .in_buf = NULL,
+    .cond_process = PTHREAD_COND_INITIALIZER,
+    .cond_read = PTHREAD_COND_INITIALIZER,
+    .ssr_process_lock = PTHREAD_MUTEX_INITIALIZER,
+    .is_ssr_mode_on = false,
 };
 
+static void *ssr_process_thread(void *context);
+
 /* Use AAC/DTS channel mapping as default channel mapping: C,FL,FR,Ls,Rs,LFE */
 static const int chan_map[] = { 1, 2, 4, 3, 0, 5};
 
@@ -206,6 +300,159 @@
     return 0;
 }
 
+static int32_t drc_init_lib(int num_chan, int sample_rate __unused)
+{
+    int ret = 0;
+    const char *cfgFileName = "";
+
+    if (ssrmod.drc_obj) {
+        ALOGE("%s: DRC library is already initialized", __func__);
+        return 0;
+    }
+
+    ssrmod.drc_handle = dlopen(LIB_DRC, RTLD_NOW);
+    if (ssrmod.drc_handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_DRC);
+        ret = -ENOSYS;
+        goto init_fail;
+    }
+
+    ALOGV("%s: DLOPEN successful for %s", __func__, LIB_DRC);
+    ssrmod.drc_init = (drc_init_t)
+        dlsym(ssrmod.drc_handle, "DRC_init");
+    ssrmod.drc_deinit = (drc_deinit_t)
+        dlsym(ssrmod.drc_handle, "DRC_deinit");
+    ssrmod.drc_process = (drc_process_t)
+        dlsym(ssrmod.drc_handle, "DRC_process");
+
+    if (!ssrmod.drc_init ||
+        !ssrmod.drc_deinit ||
+        !ssrmod.drc_process){
+        ALOGW("%s: Could not find one of the symbols from %s",
+              __func__, LIB_DRC);
+        ret = -ENOSYS;
+        goto init_fail;
+    }
+
+    /* TO DO: different config files for different sample rates */
+    if (num_chan == 6) {
+        cfgFileName = "/system/etc/drc/drc_cfg_5.1.txt";
+    } else if (num_chan == 2) {
+        cfgFileName = "/system/etc/drc/drc_cfg_AZ.txt";
+    }
+
+    ALOGV("%s: Calling drc_init: num ch: %d, period: %d, cfg file: %s", __func__, num_chan, SSR_PERIOD_SIZE, cfgFileName);
+    ret = ssrmod.drc_init(&ssrmod.drc_obj, num_chan, SSR_PERIOD_SIZE, cfgFileName);
+    if (ret) {
+        ALOGE("drc_init failed with ret:%d",ret);
+        ret = -EINVAL;
+        goto init_fail;
+    }
+
+    return 0;
+
+init_fail:
+    if (ssrmod.drc_obj) {
+        free(ssrmod.drc_obj);
+        ssrmod.drc_obj = NULL;
+    }
+    if(ssrmod.drc_handle) {
+        dlclose(ssrmod.drc_handle);
+        ssrmod.drc_handle = NULL;
+    }
+    return ret;
+}
+
+static int32_t ssr_init_surround_sound_3mic_lib(unsigned long buffersize, int num_in_chan, int num_out_chan, int sample_rate)
+{
+    int ret = 0;
+    const char *cfgFileName = NULL;
+
+    if ( ssrmod.surround_obj ) {
+        ALOGE("%s: surround sound library is already initialized", __func__);
+        return 0;
+    }
+
+    /* Allocate memory for input buffer */
+    ssrmod.surround_raw_buffer = (Word16 *) calloc(buffersize,
+                                              sizeof(Word16));
+    if ( !ssrmod.surround_raw_buffer ) {
+       ALOGE("%s: Memory allocation failure. Not able to allocate "
+             "memory for surroundInputBuffer", __func__);
+       ret = -ENOMEM;
+       goto init_fail;
+    }
+
+    ssrmod.surround_raw_buffer_size = buffersize;
+
+    ssrmod.surround_rec_handle = dlopen(LIB_SURROUND_3MIC_PROC, RTLD_NOW);
+    if (ssrmod.surround_rec_handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SURROUND_3MIC_PROC);
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SURROUND_3MIC_PROC);
+        ssrmod.surround_rec_get_get_param_data = (surround_rec_get_get_param_data_t)
+        dlsym(ssrmod.surround_rec_handle, "surround_rec_get_get_param_data");
+
+        ssrmod.surround_rec_get_set_param_data = (surround_rec_get_set_param_data_t)
+        dlsym(ssrmod.surround_rec_handle, "surround_rec_get_set_param_data");
+        ssrmod.surround_rec_init = (surround_rec_init_t)
+        dlsym(ssrmod.surround_rec_handle, "surround_rec_init");
+        ssrmod.surround_rec_deinit = (surround_rec_deinit_t)
+        dlsym(ssrmod.surround_rec_handle, "surround_rec_deinit");
+        ssrmod.surround_rec_process = (surround_rec_process_t)
+        dlsym(ssrmod.surround_rec_handle, "surround_rec_process");
+
+        if (!ssrmod.surround_rec_get_get_param_data ||
+            !ssrmod.surround_rec_get_set_param_data ||
+            !ssrmod.surround_rec_init ||
+            !ssrmod.surround_rec_deinit ||
+            !ssrmod.surround_rec_process){
+            ALOGW("%s: Could not find the one of the symbols from %s",
+                  __func__, LIB_SURROUND_3MIC_PROC);
+            ret = -ENOSYS;
+            goto init_fail;
+        }
+    }
+
+    ssrmod.num_out_chan = num_out_chan;
+
+    if (num_out_chan == 6) {
+        cfgFileName = "/system/etc/surround_sound_3mic/surround_sound_rec_5.1.cfg";
+    } else if (num_out_chan == 2) {
+        cfgFileName = "/system/etc/surround_sound_3mic/surround_sound_rec_AZ.cfg";
+    } else {
+        ALOGE("%s: No cfg file for num_out_chan: %d", __func__, num_out_chan);
+    }
+
+    ALOGV("%s: Calling surround_rec_init: in ch: %d, out ch: %d, period: %d, sample rate: %d, cfg file: %s",
+          __func__, num_in_chan, num_out_chan, SSR_PERIOD_SIZE, sample_rate, cfgFileName);
+     ret = ssrmod.surround_rec_init(&ssrmod.surround_obj,
+        num_in_chan, num_out_chan, SSR_PERIOD_SIZE, sample_rate, cfgFileName);
+    if (ret) {
+        ALOGE("surround_rec_init failed with ret:%d",ret);
+        ret = -EINVAL;
+        goto init_fail;
+    }
+
+    return 0;
+
+init_fail:
+    if (ssrmod.surround_obj) {
+        free(ssrmod.surround_obj);
+        ssrmod.surround_obj = NULL;
+    }
+    if (ssrmod.surround_raw_buffer) {
+        free(ssrmod.surround_raw_buffer);
+        ssrmod.surround_raw_buffer = NULL;
+        ssrmod.surround_raw_buffer_size = 0;
+    }
+    if(ssrmod.surround_rec_handle) {
+        dlclose(ssrmod.surround_rec_handle);
+        ssrmod.surround_rec_handle = NULL;
+    }
+    return ret;
+}
+
 static int32_t ssr_init_surround_sound_lib(unsigned long buffersize)
 {
     /* sub_woofer channel assignment: default as first
@@ -224,6 +471,8 @@
         return 0;
     }
 
+    ssrmod.num_out_chan = SSR_CHANNEL_OUTPUT_NUM;
+
     /* Allocate memory for input buffer */
     ssrmod.surround_raw_buffer = (Word16 *) calloc(buffersize,
                                               sizeof(Word16));
@@ -233,6 +482,8 @@
        goto init_fail;
     }
 
+    ssrmod.surround_raw_buffer_size = buffersize;
+
     /* Allocate memory for real and imag coeffs array */
     ssrmod.real_coeffs = (Word16 **) calloc(COEFF_ARRAY_SIZE, sizeof(Word16 *));
     if ( !ssrmod.real_coeffs ) {
@@ -333,6 +584,7 @@
     if (ssrmod.surround_raw_buffer) {
         free(ssrmod.surround_raw_buffer);
         ssrmod.surround_raw_buffer = NULL;
+        ssrmod.surround_raw_buffer_size = 0;
     }
     if (ssrmod.real_coeffs){
         for (i =0; i<COEFF_ARRAY_SIZE; i++ ) {
@@ -374,77 +626,242 @@
 
 bool audio_extn_ssr_get_enabled()
 {
-    ALOGV("%s: is_ssr_enabled:%d", __func__, ssrmod.is_ssr_enabled);
-    return (ssrmod.is_ssr_enabled ? true: false);
+    ALOGV("%s: is_ssr_enabled:%d is_ssr_mode_on:%d ", __func__, ssrmod.is_ssr_enabled, ssrmod.is_ssr_mode_on);
+
+    if(ssrmod.is_ssr_enabled && ssrmod.is_ssr_mode_on)
+        return true;
+
+    return false;
 }
 
-int32_t audio_extn_ssr_init(struct stream_in *in)
+static void pcm_buffer_queue_push(struct pcm_buffer_queue **queue,
+                                  struct pcm_buffer_queue *node)
+{
+    struct pcm_buffer_queue *iter;
+
+    node->next = NULL;
+    if ((*queue) == NULL) {
+        *queue = node;
+    } else {
+        iter = *queue;
+        while (iter->next) {
+            iter = iter->next;
+        }
+        iter->next = node;
+    }
+}
+
+static struct pcm_buffer_queue *pcm_buffer_queue_pop(struct pcm_buffer_queue **queue)
+{
+    struct pcm_buffer_queue *node = (*queue);
+    if (node != NULL) {
+        *queue = node->next;
+        node->next = NULL;
+    }
+    return node;
+}
+
+static void deinit_ssr_process_thread()
+{
+    pthread_mutex_lock(&ssrmod.ssr_process_lock);
+    ssrmod.ssr_process_thread_stop = 1;
+    free(ssrmod.in_buf_data);
+    ssrmod.in_buf_data = NULL;
+    ssrmod.in_buf = NULL;
+    ssrmod.in_buf_free = NULL;
+    free(ssrmod.out_buf_data);
+    ssrmod.out_buf_data = NULL;
+    ssrmod.out_buf = NULL;
+    ssrmod.out_buf_free = NULL;
+    pthread_cond_broadcast(&ssrmod.cond_process);
+    pthread_cond_broadcast(&ssrmod.cond_read);
+    pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+    if (ssrmod.ssr_process_thread_started) {
+        pthread_join(ssrmod.ssr_process_thread, (void **)NULL);
+        ssrmod.ssr_process_thread_started = 0;
+    }
+}
+
+struct stream_in *audio_extn_ssr_get_stream()
+{
+    return ssrmod.in;
+}
+
+int32_t audio_extn_ssr_init(struct stream_in *in, int num_out_chan)
 {
     uint32_t ret;
     char c_multi_ch_dump[128] = {0};
+    char c_ssr_3mic[128] = {0};
     uint32_t buffer_size;
+    int num_in_chan;
 
-    ALOGD("%s: ssr case ", __func__);
-    in->config.channels = SSR_CHANNEL_INPUT_NUM;
+    ALOGD("%s: ssr case, sample rate %d", __func__, in->config.rate);
+
+    if (ssrmod.surround_obj != NULL) {
+        ALOGV("%s: reinitializing surround sound library", __func__);
+        audio_extn_ssr_deinit();
+    }
+
+    property_get("persist.audio.ssr.3mic",c_ssr_3mic,"0");
+    if (0 == strncmp("true", c_ssr_3mic, sizeof(c_ssr_3mic)-1)) {
+        ssrmod.ssr_3mic = 1;
+        num_in_chan = 3;
+    } else {
+        ssrmod.ssr_3mic = 0;
+        num_in_chan = 4;
+    }
+
+    in->config.channels = num_in_chan;
     in->config.period_size = SSR_PERIOD_SIZE;
-    in->config.period_count = SSR_PERIOD_COUNT;
+    in->config.period_count = in->config.channels * sizeof(int16_t);
 
-    /* use 4k hardcoded buffer size for ssr*/
-    buffer_size = SSR_INPUT_FRAME_SIZE;
+    buffer_size = in->config.period_size * in->config.period_count;
     ALOGV("%s: buffer_size: %d", __func__, buffer_size);
 
-    ret = ssr_init_surround_sound_lib(buffer_size);
-    if (0 != ret) {
-        ALOGE("%s: initSurroundSoundLibrary failed: %d  "
-              "handle->bufferSize:%d", __func__, ret, buffer_size);
-        return ret;
+    if (ssrmod.ssr_3mic != 0) {
+        ret = ssr_init_surround_sound_3mic_lib(buffer_size, num_in_chan, num_out_chan, in->config.rate);
+        if (0 != ret) {
+            ALOGE("%s: ssr_init_surround_sound_3mic_lib failed: %d  "
+                  "buffer_size:%d", __func__, ret, buffer_size);
+            goto fail;
+        }
+    } else {
+        ret = ssr_init_surround_sound_lib(buffer_size);
+        if (0 != ret) {
+            ALOGE("%s: initSurroundSoundLibrary failed: %d  "
+                  "handle->bufferSize:%d", __func__, ret, buffer_size);
+            goto fail;
+        }
     }
 
+    /* Initialize DRC if available */
+    ret = drc_init_lib(num_out_chan, in->config.rate);
+    if (0 != ret) {
+        ALOGE("%s: drc_init_lib failed, ret %d", __func__, ret);
+    }
+
+    pthread_mutex_lock(&ssrmod.ssr_process_lock);
+    if (!ssrmod.ssr_process_thread_started) {
+        int i;
+        int output_buf_size = SSR_PERIOD_SIZE * sizeof(int16_t) * num_out_chan;
+
+        ssrmod.in_buf_data = (void *)calloc(buffer_size, NUM_IN_BUFS);
+        if (ssrmod.in_buf_data == NULL) {
+            ALOGE("%s: failed to allocate input buffer", __func__);
+            pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+            ret = -ENOMEM;
+            goto fail;
+        }
+        ssrmod.out_buf_data = (void *)calloc(output_buf_size, NUM_OUT_BUFS);
+        if (ssrmod.out_buf_data == NULL) {
+            ALOGE("%s: failed to allocate output buffer", __func__);
+            pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+            ret = -ENOMEM;
+            // ssrmod.in_buf_data will be freed in deinit_ssr_process_thread()
+            goto fail;
+        }
+
+        ssrmod.in_buf = NULL;
+        ssrmod.in_buf_free = NULL;
+        ssrmod.out_buf = NULL;
+        ssrmod.out_buf_free = NULL;
+
+        for (i=0; i < NUM_IN_BUFS; i++) {
+            struct pcm_buffer_queue *buf = &ssrmod.in_buf_nodes[i];
+            buf->buffer.data = &(((char *)ssrmod.in_buf_data)[i*buffer_size]);
+            buf->buffer.length = buffer_size;
+            pcm_buffer_queue_push(&ssrmod.in_buf_free, buf);
+        }
+
+        for (i=0; i < NUM_OUT_BUFS; i++) {
+            struct pcm_buffer_queue *buf = &ssrmod.out_buf_nodes[i];
+            buf->buffer.data = &(((char *)ssrmod.out_buf_data)[i*output_buf_size]);
+            buf->buffer.length = output_buf_size;
+            pcm_buffer_queue_push(&ssrmod.out_buf, buf);
+        }
+
+        ssrmod.ssr_process_thread_stop = 0;
+        ALOGV("%s: creating thread", __func__);
+        ret = pthread_create(&ssrmod.ssr_process_thread,
+                             (const pthread_attr_t *) NULL,
+                             ssr_process_thread, NULL);
+        if (ret != 0) {
+            ALOGE("%s: failed to create thread for surround sound recording.",
+                  __func__);
+            pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+            goto fail;
+        }
+
+        ssrmod.ssr_process_thread_started = 1;
+        ALOGV("%s: done creating thread", __func__);
+    }
+    pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+
     property_get("ssr.pcmdump",c_multi_ch_dump,"0");
     if (0 == strncmp("true", c_multi_ch_dump, sizeof("ssr.dump-pcm"))) {
         /* Remember to change file system permission of data(e.g. chmod 777 data/),
           otherwise, fopen may fail */
         if ( !ssrmod.fp_4ch)
-            ssrmod.fp_4ch = fopen("/data/4ch.pcm", "wb");
+            ssrmod.fp_4ch = fopen("/data/misc/audio/4ch.pcm", "wb");
         if ( !ssrmod.fp_6ch)
-            ssrmod.fp_6ch = fopen("/data/6ch.pcm", "wb");
+            ssrmod.fp_6ch = fopen("/data/misc/audio/6ch.pcm", "wb");
         if ((!ssrmod.fp_4ch) || (!ssrmod.fp_6ch))
             ALOGE("%s: mfp_4ch or mfp_6ch open failed: mfp_4ch:%p mfp_6ch:%p",
                   __func__, ssrmod.fp_4ch, ssrmod.fp_6ch);
     }
 
+    ssrmod.in = in;
+
+    ALOGV("%s: exit", __func__);
     return 0;
+
+fail:
+    (void) audio_extn_ssr_deinit();
+    return ret;
 }
 
 int32_t audio_extn_ssr_deinit()
 {
     int i;
 
+    ALOGV("%s: entry", __func__);
+    deinit_ssr_process_thread();
+
+    if (ssrmod.drc_obj) {
+        ssrmod.drc_deinit(ssrmod.drc_obj);
+        ssrmod.drc_obj = NULL;
+    }
+
     if (ssrmod.surround_obj) {
-        ALOGV("%s: entry", __func__);
-        ssrmod.surround_filters_release(ssrmod.surround_obj);
-        if (ssrmod.surround_obj)
-            free(ssrmod.surround_obj);
-        ssrmod.surround_obj = NULL;
-        if (ssrmod.real_coeffs){
-            for (i =0; i<COEFF_ARRAY_SIZE; i++ ) {
-                if (ssrmod.real_coeffs[i]) {
-                    free(ssrmod.real_coeffs[i]);
-                    ssrmod.real_coeffs[i] = NULL;
+
+        if (ssrmod.ssr_3mic) {
+            ssrmod.surround_rec_deinit(ssrmod.surround_obj);
+            ssrmod.surround_obj = NULL;
+        } else {
+            ssrmod.surround_filters_release(ssrmod.surround_obj);
+            if (ssrmod.surround_obj)
+                free(ssrmod.surround_obj);
+            ssrmod.surround_obj = NULL;
+            if (ssrmod.real_coeffs){
+                for (i =0; i<COEFF_ARRAY_SIZE; i++ ) {
+                    if (ssrmod.real_coeffs[i]) {
+                        free(ssrmod.real_coeffs[i]);
+                        ssrmod.real_coeffs[i] = NULL;
+                    }
                 }
+                free(ssrmod.real_coeffs);
+                ssrmod.real_coeffs = NULL;
             }
-            free(ssrmod.real_coeffs);
-            ssrmod.real_coeffs = NULL;
-        }
-        if (ssrmod.imag_coeffs){
-            for (i =0; i<COEFF_ARRAY_SIZE; i++ ) {
-                if (ssrmod.imag_coeffs[i]) {
-                    free(ssrmod.imag_coeffs[i]);
-                    ssrmod.imag_coeffs[i] = NULL;
+            if (ssrmod.imag_coeffs){
+                for (i =0; i<COEFF_ARRAY_SIZE; i++ ) {
+                    if (ssrmod.imag_coeffs[i]) {
+                        free(ssrmod.imag_coeffs[i]);
+                        ssrmod.imag_coeffs[i] = NULL;
+                    }
                 }
+                free(ssrmod.imag_coeffs);
+                ssrmod.imag_coeffs = NULL;
             }
-            free(ssrmod.imag_coeffs);
-            ssrmod.imag_coeffs = NULL;
         }
         if (ssrmod.surround_raw_buffer) {
             free(ssrmod.surround_raw_buffer);
@@ -456,48 +873,225 @@
             fclose(ssrmod.fp_6ch);
     }
 
+    if(ssrmod.drc_handle) {
+        dlclose(ssrmod.drc_handle);
+        ssrmod.drc_handle = NULL;
+    }
+
+    if(ssrmod.surround_rec_handle) {
+        dlclose(ssrmod.surround_rec_handle);
+        ssrmod.surround_rec_handle = NULL;
+    }
+
     if(ssrmod.surround_filters_handle) {
         dlclose(ssrmod.surround_filters_handle);
         ssrmod.surround_filters_handle = NULL;
     }
+
+    ssrmod.in = NULL;
+    ssrmod.is_ssr_mode_on = false;
     ALOGV("%s: exit", __func__);
 
     return 0;
 }
 
+static void *ssr_process_thread(void *context __unused)
+{
+    int32_t ret;
+
+    ALOGV("%s: enter", __func__);
+
+    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_URGENT_AUDIO);
+    set_sched_policy(0, SP_FOREGROUND);
+
+    pthread_mutex_lock(&ssrmod.ssr_process_lock);
+    while (!ssrmod.ssr_process_thread_stop) {
+        struct pcm_buffer_queue *out_buf;
+        struct pcm_buffer_queue *in_buf;
+
+        while ((!ssrmod.ssr_process_thread_stop) &&
+               ((ssrmod.out_buf_free == NULL) ||
+                (ssrmod.in_buf == NULL))) {
+            ALOGV("%s: waiting for buffers", __func__);
+            pthread_cond_wait(&ssrmod.cond_process, &ssrmod.ssr_process_lock);
+        }
+        if (ssrmod.ssr_process_thread_stop) {
+            break;
+        }
+        ALOGV("%s: got buffers", __func__);
+
+        out_buf = pcm_buffer_queue_pop(&ssrmod.out_buf_free);
+        in_buf = pcm_buffer_queue_pop(&ssrmod.in_buf);
+
+        pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+
+        /* apply ssr libs to convert 4ch to 6ch */
+        if (ssrmod.ssr_3mic) {
+            ssrmod.surround_rec_process(ssrmod.surround_obj,
+                (int16_t *) in_buf->buffer.data,
+                (int16_t *) out_buf->buffer.data);
+        } else {
+            ssrmod.surround_filters_intl_process(ssrmod.surround_obj,
+                (uint16_t *) out_buf->buffer.data, in_buf->buffer.data);
+        }
+
+        /* Run DRC if initialized */
+        if (ssrmod.drc_obj != NULL) {
+            ALOGV("%s: Running DRC", __func__);
+            ret = ssrmod.drc_process(ssrmod.drc_obj, out_buf->buffer.data, out_buf->buffer.data);
+            if (ret != 0) {
+                ALOGE("%s: drc_process returned %d", __func__, ret);
+            }
+        }
+
+        /*dump for raw pcm data*/
+        if (ssrmod.fp_4ch)
+            fwrite(in_buf->buffer.data, 1, in_buf->buffer.length, ssrmod.fp_4ch);
+        if (ssrmod.fp_6ch)
+            fwrite(out_buf->buffer.data, 1, out_buf->buffer.length, ssrmod.fp_6ch);
+
+        pthread_mutex_lock(&ssrmod.ssr_process_lock);
+
+        pcm_buffer_queue_push(&ssrmod.out_buf, out_buf);
+        pcm_buffer_queue_push(&ssrmod.in_buf_free, in_buf);
+
+        /* Read thread should go on without waiting for condition
+         * variable. If it has to wait (due to this thread not keeping
+         * up with the read requests), let this thread use the remainder
+         * of its buffers before waking up the read thread. */
+        if (ssrmod.in_buf == NULL) {
+            pthread_cond_signal(&ssrmod.cond_read);
+        }
+    }
+    pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+
+    ALOGV("%s: exit", __func__);
+
+    pthread_exit(NULL);
+}
+
 int32_t audio_extn_ssr_read(struct audio_stream_in *stream,
                        void *buffer, size_t bytes)
 {
     struct stream_in *in = (struct stream_in *)stream;
     struct audio_device *adev = in->dev;
-    size_t peroid_bytes;
-    int32_t ret;
+    int32_t ret = 0;
+    struct pcm_buffer_queue *in_buf;
+    struct pcm_buffer_queue *out_buf;
 
-    /* Convert bytes for 6ch to 4ch*/
-    peroid_bytes = (bytes / SSR_CHANNEL_OUTPUT_NUM) * SSR_CHANNEL_INPUT_NUM;
+    ALOGV("%s: entry", __func__);
 
     if (!ssrmod.surround_obj) {
         ALOGE("%s: surround_obj not initialized", __func__);
         return -ENOMEM;
     }
 
-    ret = pcm_read(in->pcm, ssrmod.surround_raw_buffer, peroid_bytes);
+    ret = pcm_read(in->pcm, ssrmod.surround_raw_buffer, ssrmod.surround_raw_buffer_size);
     if (ret < 0) {
         ALOGE("%s: %s ret:%d", __func__, pcm_get_error(in->pcm),ret);
         return ret;
     }
 
-    /* apply ssr libs to conver 4ch to 6ch */
-    ssrmod.surround_filters_intl_process(ssrmod.surround_obj,
-        buffer, ssrmod.surround_raw_buffer);
+    pthread_mutex_lock(&ssrmod.ssr_process_lock);
 
-    /*dump for raw pcm data*/
-    if (ssrmod.fp_4ch)
-        fwrite(ssrmod.surround_raw_buffer, 1, peroid_bytes, ssrmod.fp_4ch);
-    if (ssrmod.fp_6ch)
-        fwrite(buffer, 1, bytes, ssrmod.fp_6ch);
+    if (!ssrmod.ssr_process_thread_started) {
+        pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+        ALOGV("%s: ssr_process_thread not initialized", __func__);
+        return -EINVAL;
+    }
 
+    if ((ssrmod.in_buf_free == NULL) || (ssrmod.out_buf == NULL)) {
+        ALOGE("%s: waiting for buffers", __func__);
+        pthread_cond_wait(&ssrmod.cond_read, &ssrmod.ssr_process_lock);
+        if ((ssrmod.in_buf_free == NULL) || (ssrmod.out_buf == NULL)) {
+            pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+            ALOGE("%s: failed to acquire buffers", __func__);
+            return -EINVAL;
+        }
+    }
+
+    in_buf = pcm_buffer_queue_pop(&ssrmod.in_buf_free);
+    out_buf = pcm_buffer_queue_pop(&ssrmod.out_buf);
+
+    memcpy(in_buf->buffer.data, ssrmod.surround_raw_buffer, in_buf->buffer.length);
+    pcm_buffer_queue_push(&ssrmod.in_buf, in_buf);
+
+    memcpy(buffer, out_buf->buffer.data, bytes);
+    pcm_buffer_queue_push(&ssrmod.out_buf_free, out_buf);
+
+    pthread_cond_signal(&ssrmod.cond_process);
+
+    pthread_mutex_unlock(&ssrmod.ssr_process_lock);
+
+    ALOGV("%s: exit", __func__);
     return ret;
 }
 
+void audio_extn_ssr_set_parameters(struct audio_device *adev __unused,
+                                   struct str_parms *parms)
+{
+    int err;
+    char value[4096] = {0};
+
+    //Do not update SSR mode during recording
+    if ( !ssrmod.surround_obj) {
+        int ret = 0;
+        ret = str_parms_get_str(parms, AUDIO_PARAMETER_SSRMODE_ON, value,
+                                sizeof(value));
+        if (ret >= 0) {
+            if (strcmp(value, "true") == 0) {
+                ALOGD("Received SSR session request..setting SSR mode to true");
+                ssrmod.is_ssr_mode_on = true;
+            } else {
+                ALOGD("resetting SSR mode to false");
+                ssrmod.is_ssr_mode_on = false;
+            }
+        }
+    }
+    if (ssrmod.ssr_3mic && ssrmod.surround_obj) {
+        const set_param_data_t *set_params = ssrmod.surround_rec_get_set_param_data();
+        if (set_params != NULL) {
+            while (set_params->name != NULL && set_params->set_param_fn != NULL) {
+                err = str_parms_get_str(parms, set_params->name, value, sizeof(value));
+                if (err >= 0) {
+                    ALOGV("Set %s to %s\n", set_params->name, value);
+                    set_params->set_param_fn(ssrmod.surround_obj, value);
+                }
+                set_params++;
+            }
+        }
+    }
+}
+
+void audio_extn_ssr_get_parameters(const struct audio_device *adev __unused,
+                                   struct str_parms *parms,
+                                   struct str_parms *reply)
+{
+    int err;
+    char value[4096] = {0};
+
+    if (ssrmod.ssr_3mic && ssrmod.surround_obj) {
+        const get_param_data_t *get_params = ssrmod.surround_rec_get_get_param_data();
+        int get_all = 0;
+        err = str_parms_get_str(parms, "ssr.all", value, sizeof(value));
+        if (err >= 0) {
+            get_all = 1;
+        }
+        if (get_params != NULL) {
+            while (get_params->name != NULL && get_params->get_param_fn != NULL) {
+                err = str_parms_get_str(parms, get_params->name, value, sizeof(value));
+                if (get_all || (err >= 0)) {
+                    ALOGV("Getting parameter %s", get_params->name);
+                    char *val = get_params->get_param_fn(ssrmod.surround_obj);
+                    if (val != NULL) {
+                        str_parms_add_str(reply, get_params->name, val);
+                        free(val);
+                    }
+                }
+                get_params++;
+            }
+        }
+    }
+}
+
 #endif /* SSR_ENABLED */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index a0f9206..5500ee3 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1057,6 +1057,10 @@
     /* 2. Disable the tx device */
     disable_snd_device(adev, uc_info->in_snd_device);
 
+    if (audio_extn_ssr_get_stream() == in) {
+        audio_extn_ssr_deinit();
+    }
+
     list_remove(&uc_info->list);
     free(uc_info);
 
@@ -2781,8 +2785,7 @@
         adev->adm_request_focus(adev->adm_data, in->capture_handle);
 
     if (in->pcm) {
-        if (audio_extn_ssr_get_enabled() &&
-                audio_channel_count_from_in_mask(in->channel_mask) == 6)
+        if (audio_extn_ssr_get_stream() == in)
             ret = audio_extn_ssr_read(stream, buffer, bytes);
         else if (audio_extn_compr_cap_usecase_supported(in->usecase))
             ret = audio_extn_compr_cap_read(in, buffer, bytes);
@@ -3650,15 +3653,27 @@
         in->config = pcm_config_afe_proxy_record;
         in->config.channels = channel_count;
         in->config.rate = config->sample_rate;
-    } else if (channel_count == 6) {
-        if(audio_extn_ssr_get_enabled()) {
-            if(audio_extn_ssr_init(in)) {
-                ALOGE("%s: audio_extn_ssr_init failed", __func__);
+    } else if (audio_extn_ssr_get_enabled() &&
+               ((channel_count == 2) || (channel_count == 6)) &&
+               ((AUDIO_SOURCE_MIC == source) || (AUDIO_SOURCE_CAMCORDER == source))) {
+        ALOGD("%s: Found SSR use case starting SSR lib with channel_count :%d",
+               __func__, channel_count);
+        if (audio_extn_ssr_init(in, channel_count)) {
+            ALOGE("%s: audio_extn_ssr_init failed", __func__);
+            if (channel_count == 2) {
+                ALOGD("%s: falling back to default record usecase", __func__);
+                in->config.channels = channel_count;
+                frame_size = audio_stream_in_frame_size(&in->stream);
+                buffer_size = get_input_buffer_size(config->sample_rate,
+                                        config->format,
+                                        channel_count,
+                                        is_low_latency);
+                in->config.period_size = buffer_size / frame_size;
+            } else {
+                ALOGD("%s: unable to start SSR record session for 6 channel input", __func__);
                 ret = -EINVAL;
                 goto err_open;
             }
-        } else {
-            ALOGW("%s: surround sound recording is not supported", __func__);
         }
     } else if (audio_extn_compr_cap_enabled() &&
             audio_extn_compr_cap_format_supported(config->format) &&
@@ -3717,8 +3732,7 @@
     } else
         in_standby(&stream->common);
 
-    if (audio_extn_ssr_get_enabled() &&
-            (audio_channel_count_from_in_mask(in->channel_mask) == 6)) {
+    if (audio_extn_ssr_get_stream() == in) {
         audio_extn_ssr_deinit();
     }
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 6f73f0f..37c998a 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -439,6 +439,7 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC] = "quad-mic",
     [SND_DEVICE_IN_SPEAKER_QMIC_NS] = "quad-mic",
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = "quad-mic",
+    [SND_DEVICE_IN_THREE_MIC] = "three-mic",
 };
 
 // Platform specific backend bit width table
@@ -550,6 +551,7 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC] = 126,
     [SND_DEVICE_IN_SPEAKER_QMIC_NS] = 127,
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = 129,
+    [SND_DEVICE_IN_THREE_MIC] = 46,
 };
 
 struct name_to_index {
@@ -2984,6 +2986,8 @@
         goto exit;
     }
 
+    if (adev->active_input && (audio_extn_ssr_get_stream() == adev->active_input))
+        snd_device = SND_DEVICE_IN_THREE_MIC;
 
     if (snd_device != SND_DEVICE_NONE) {
         goto exit;
@@ -2993,7 +2997,7 @@
             !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) &&
             !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            if (audio_extn_ssr_get_enabled() && channel_count == 6)
+            if (adev->active_input && (audio_extn_ssr_get_stream() == adev->active_input))
                 snd_device = SND_DEVICE_IN_QUAD_MIC;
             else if (my_data->fluence_type & (FLUENCE_DUAL_MIC | FLUENCE_QUAD_MIC) &&
                     channel_count == 2)
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index c4d0b59..39c154b 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -177,6 +177,7 @@
     SND_DEVICE_IN_SPEAKER_QMIC_AEC,
     SND_DEVICE_IN_SPEAKER_QMIC_NS,
     SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS,
+    SND_DEVICE_IN_THREE_MIC,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index d4cdf2e..2fb50c1 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -423,6 +423,7 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC] = "quad-mic",
     [SND_DEVICE_IN_SPEAKER_QMIC_NS] = "quad-mic",
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = "quad-mic",
+    [SND_DEVICE_IN_THREE_MIC] = "three-mic",
 };
 
 // Platform specific backend bit width table
@@ -527,6 +528,7 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC] = 126,
     [SND_DEVICE_IN_SPEAKER_QMIC_NS] = 127,
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = 129,
+    [SND_DEVICE_IN_THREE_MIC] = 46,
 };
 
 struct name_to_index {
@@ -2675,6 +2677,8 @@
         goto exit;
     }
 
+    if (adev->active_input && (audio_extn_ssr_get_stream() == adev->active_input))
+        snd_device = SND_DEVICE_IN_THREE_MIC;
 
     if (snd_device != SND_DEVICE_NONE) {
         goto exit;
@@ -2684,7 +2688,7 @@
             !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) &&
             !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            if (audio_extn_ssr_get_enabled() && channel_count == 6)
+            if (adev->active_input && (audio_extn_ssr_get_stream() == adev->active_input))
                 snd_device = SND_DEVICE_IN_QUAD_MIC;
             else if (my_data->fluence_type & (FLUENCE_DUAL_MIC | FLUENCE_QUAD_MIC) &&
                     channel_count == 2)
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index c98d1af..9045562 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -169,6 +169,7 @@
     SND_DEVICE_IN_SPEAKER_QMIC_AEC,
     SND_DEVICE_IN_SPEAKER_QMIC_NS,
     SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS,
+    SND_DEVICE_IN_THREE_MIC,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,