hal: Adding Pan-Scale, downmix mixer control support.

Add support in HAL to send mix matrix params using downmix,
Pan-Scale mixer controls. Add new interactive usecases and
also add support to open and teardown for interactive streams.
Add support in test app for supporting up to 9 concurrent
streams. Add support for mixer matrix params handling.

Change-Id: I0dc5b908ee779b2b2c526a67609c057f591f26e7
diff --git a/hal/audio_extn/audio_defs.h b/hal/audio_extn/audio_defs.h
index dfe8c61..4e5f4d8 100644
--- a/hal/audio_extn/audio_defs.h
+++ b/hal/audio_extn/audio_defs.h
@@ -236,6 +236,17 @@
    struct audio_device_cfg_param dev_cfg_params;
 };
 
+typedef struct mix_matrix_params {
+    uint16_t num_output_channels;
+    uint16_t num_input_channels;
+    uint8_t has_output_channel_map;
+    uint32_t output_channel_map[AUDIO_CHANNEL_COUNT_MAX];
+    uint8_t has_input_channel_map;
+    uint32_t input_channel_map[AUDIO_CHANNEL_COUNT_MAX];
+    uint8_t has_mixer_coeffs;
+    float mixer_coeffs[AUDIO_CHANNEL_COUNT_MAX][AUDIO_CHANNEL_COUNT_MAX];
+} mix_matrix_params_t;
+
 typedef union {
     struct source_tracking_param st_params;
     struct sound_focus_param sf_params;
@@ -248,6 +259,7 @@
     struct audio_adsp_event adsp_event_params;
     struct audio_out_channel_map_param channel_map_param;
     struct audio_device_cfg_param device_cfg;
+    struct mix_matrix_params mm_params;
 } audio_extn_param_payload;
 
 typedef enum {
@@ -264,7 +276,11 @@
     AUDIO_EXTN_PARAM_ADSP_STREAM_CMD,
     /* param to set input channel map for playback stream */
     AUDIO_EXTN_PARAM_OUT_CHANNEL_MAP,
-    AUDIO_EXTN_PARAM_DEVICE_CONFIG
+    AUDIO_EXTN_PARAM_DEVICE_CONFIG,
+    /* Pan/scale params to be set on ASM */
+    AUDIO_EXTN_PARAM_OUT_MIX_MATRIX_PARAMS,
+    /* Downmix params to be set on ADM */
+    AUDIO_EXTN_PARAM_CH_MIX_MATRIX_PARAMS
 } audio_extn_param_id;
 
 #endif /* AUDIO_DEFS_H */
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 072444e..84de66f 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -1379,6 +1379,14 @@
             ret = audio_extn_utils_set_channel_map(out,
                     (struct audio_out_channel_map_param *)(payload));
             break;
+        case AUDIO_EXTN_PARAM_OUT_MIX_MATRIX_PARAMS:
+            ret = audio_extn_utils_set_pan_scale_params(out,
+                    (struct mix_matrix_params *)(payload));
+            break;
+        case AUDIO_EXTN_PARAM_CH_MIX_MATRIX_PARAMS:
+            ret = audio_extn_utils_set_downmix_params(out,
+                    (struct mix_matrix_params *)(payload));
+            break;
         default:
             ALOGE("%s:: unsupported param_id %d", __func__, param_id);
             break;
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c2a5484..8360703 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -103,6 +103,10 @@
 #define AUDIO_OUTPUT_FLAG_TIMESTAMP 0x10000
 #endif
 
+#ifndef AUDIO_OUTPUT_FLAG_INTERACTIVE
+#define AUDIO_OUTPUT_FLAG_INTERACTIVE 0x40000
+#endif
+
 #ifndef COMPRESS_METADATA_NEEDED
 #define audio_extn_parse_compress_metadata(out, parms) (0)
 #else
@@ -892,6 +896,12 @@
 int audio_extn_utils_set_channel_map(
             struct stream_out *out,
             struct audio_out_channel_map_param *channel_map_param);
+int audio_extn_utils_set_pan_scale_params(
+            struct stream_out *out,
+            struct mix_matrix_params *mm_params);
+int audio_extn_utils_set_downmix_params(
+            struct stream_out *out,
+            struct mix_matrix_params *mm_params);
 #ifdef AUDIO_HW_LOOPBACK_ENABLED
 /* API to create audio patch */
 int audio_extn_hw_loopback_create_audio_patch(struct audio_hw_device *dev,
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 9f78a0c..fb1362c 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -102,6 +102,10 @@
 /* ToDo: Check and update a proper value in msec */
 #define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 50
 
+#ifndef MAX_CHANNELS_SUPPORTED
+#define MAX_CHANNELS_SUPPORTED 8
+#endif
+
 struct string_to_enum {
     const char *name;
     uint32_t value;
@@ -122,6 +126,7 @@
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH),
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_TIMESTAMP),
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INTERACTIVE),
     STRING_TO_ENUM(AUDIO_INPUT_FLAG_NONE),
     STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST),
     STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD),
@@ -821,6 +826,9 @@
     int snd_device = split_snd_device, snd_device_be_idx = -1;
     int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
     char value[PROPERTY_VALUE_MAX] = {0};
+    struct streams_io_cfg *s_info = NULL;
+    struct listnode *node = NULL;
+    int direct_app_type = 0;
 
     ALOGV("%s: usecase->out_snd_device %s, usecase->in_snd_device %s, split_snd_device %s",
           __func__, platform_get_snd_device_name(usecase->out_snd_device),
@@ -837,6 +845,7 @@
         (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
         (usecase->id != USECASE_AUDIO_PLAYBACK_ULL) &&
         (usecase->id != USECASE_AUDIO_PLAYBACK_VOIP) &&
+        (!is_interactive_usecase(usecase->id)) &&
         (!is_offload_usecase(usecase->id)) &&
         (usecase->type != PCM_CAPTURE)) {
         ALOGV("%s: a rx/tx/loopback path where app type cfg is not required %d", __func__, usecase->id);
@@ -917,7 +926,18 @@
         }
         sample_rate = usecase->stream.out->app_type_cfg.sample_rate;
 
-        app_type = usecase->stream.out->app_type_cfg.app_type;
+        /* Interactive streams are supported with only direct app type id.
+         * Get Direct profile app type and use it for interactive streams
+         */
+        list_for_each(node, &adev->streams_output_cfg_list) {
+            s_info = node_to_item(node, struct streams_io_cfg, list);
+            if (s_info->flags.out_flags == AUDIO_OUTPUT_FLAG_DIRECT)
+                direct_app_type = s_info->app_type_cfg.app_type;
+        }
+        if (usecase->stream.out->flags == AUDIO_OUTPUT_FLAG_INTERACTIVE)
+            app_type = direct_app_type;
+        else
+            app_type = usecase->stream.out->app_type_cfg.app_type;
         app_type_cfg[len++] = app_type;
         app_type_cfg[len++] = acdb_dev_id;
         if (((usecase->stream.out->format == AUDIO_FORMAT_E_AC3) ||
@@ -2160,3 +2180,100 @@
 exit:
     return ret;
 }
+
+int audio_extn_utils_set_pan_scale_params(
+            struct stream_out *out,
+            struct mix_matrix_params *mm_params)
+{
+    int ret = -EINVAL, i = 0, j = 0;
+
+    if (mm_params == NULL && out != NULL) {
+        ALOGE("%s:: Invalid mix matrix params", __func__);
+        goto exit;
+    }
+
+    if (mm_params->num_output_channels > MAX_CHANNELS_SUPPORTED ||
+        mm_params->num_output_channels <= 0 ||
+        mm_params->num_input_channels > MAX_CHANNELS_SUPPORTED ||
+        mm_params->num_input_channels <= 0)
+        goto exit;
+
+    out->pan_scale_params.num_output_channels = mm_params->num_output_channels;
+    out->pan_scale_params.num_input_channels = mm_params->num_input_channels;
+    out->pan_scale_params.has_output_channel_map =
+                                        mm_params->has_output_channel_map;
+    for (i = 0; i < mm_params->num_output_channels; i++)
+        out->pan_scale_params.output_channel_map[i] =
+                                         mm_params->output_channel_map[i];
+
+    out->pan_scale_params.has_input_channel_map =
+                                         mm_params->has_input_channel_map;
+    for (i = 0; i < mm_params->num_input_channels; i++)
+        out->pan_scale_params.input_channel_map[i] =
+                                         mm_params->input_channel_map[i];
+
+    out->pan_scale_params.has_mixer_coeffs = mm_params->has_mixer_coeffs;
+    for (i = 0; i < mm_params->num_output_channels; i++)
+        for (j = 0; j < mm_params->num_input_channels; j++) {
+            //Convert the channel coefficient gains in Q14 format
+            out->pan_scale_params.mixer_coeffs[i][j] =
+                               mm_params->mixer_coeffs[i][j] * (2 << 13);
+        }
+
+    ret = platform_set_stream_pan_scale_params(out->dev->platform,
+                                               out->pcm_device_id,
+                                               out->pan_scale_params);
+
+exit:
+    return ret;
+}
+
+int audio_extn_utils_set_downmix_params(
+            struct stream_out *out,
+            struct mix_matrix_params *mm_params)
+{
+    int ret = -EINVAL, i = 0, j = 0;
+    struct audio_usecase *usecase = NULL;
+
+    if (mm_params == NULL && out != NULL) {
+        ALOGE("%s:: Invalid mix matrix params", __func__);
+        goto exit;
+    }
+
+    if (mm_params->num_output_channels > MAX_CHANNELS_SUPPORTED ||
+        mm_params->num_output_channels <= 0 ||
+        mm_params->num_input_channels > MAX_CHANNELS_SUPPORTED ||
+        mm_params->num_input_channels <= 0)
+        goto exit;
+
+    usecase = get_usecase_from_list(out->dev, out->usecase);
+    out->downmix_params.num_output_channels = mm_params->num_output_channels;
+    out->downmix_params.num_input_channels = mm_params->num_input_channels;
+
+    out->downmix_params.has_output_channel_map =
+                                        mm_params->has_output_channel_map;
+    for (i = 0; i < mm_params->num_output_channels; i++) {
+        out->downmix_params.output_channel_map[i] =
+                                          mm_params->output_channel_map[i];
+    }
+
+    out->downmix_params.has_input_channel_map =
+                                        mm_params->has_input_channel_map;
+    for (i = 0; i < mm_params->num_input_channels; i++)
+        out->downmix_params.input_channel_map[i] =
+                                          mm_params->input_channel_map[i];
+
+    out->downmix_params.has_mixer_coeffs = mm_params->has_mixer_coeffs;
+    for (i = 0; i < mm_params->num_output_channels; i++)
+        for (j = 0; j < mm_params->num_input_channels; j++)
+            out->downmix_params.mixer_coeffs[i][j] =
+                               mm_params->mixer_coeffs[i][j];
+
+    ret = platform_set_stream_downmix_params(out->dev->platform,
+                                             out->pcm_device_id,
+                                             usecase->out_snd_device,
+                                             out->downmix_params);
+
+exit:
+    return ret;
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 8b44cd5..14b3563 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -318,6 +318,15 @@
 
     [USECASE_AUDIO_PLAYBACK_VOIP] = "audio-playback-voip",
     [USECASE_AUDIO_RECORD_VOIP] = "audio-record-voip",
+    /* For Interactive Audio Streams */
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1] = "audio-interactive-stream1",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2] = "audio-interactive-stream2",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3] = "audio-interactive-stream3",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4] = "audio-interactive-stream4",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5] = "audio-interactive-stream5",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6] = "audio-interactive-stream6",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7] = "audio-interactive-stream7",
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8] = "audio-interactive-stream8",
 };
 
 static const audio_usecase_t offload_usecases[] = {
@@ -332,6 +341,17 @@
     USECASE_AUDIO_PLAYBACK_OFFLOAD9,
 };
 
+static const audio_usecase_t interactive_usecases[] = {
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8,
+};
+
 #define STRING_TO_ENUM(string) { #string, string }
 
 struct string_to_enum {
@@ -2149,6 +2169,50 @@
     }
 }
 
+bool is_interactive_usecase(audio_usecase_t uc_id)
+{
+    unsigned int i;
+    for (i = 0; i < sizeof(interactive_usecases)/sizeof(interactive_usecases[0]); i++) {
+        if (uc_id == interactive_usecases[i])
+            return true;
+    }
+    return false;
+}
+
+static audio_usecase_t get_interactive_usecase(struct audio_device *adev)
+{
+    audio_usecase_t ret_uc = USECASE_INVALID;
+    unsigned int intract_uc_index;
+    unsigned int num_usecase = sizeof(interactive_usecases)/sizeof(interactive_usecases[0]);
+
+    ALOGV("%s: num_usecase: %d", __func__, num_usecase);
+    for (intract_uc_index = 0; intract_uc_index < num_usecase; intract_uc_index++) {
+        if (!(adev->interactive_usecase_state & (0x1 << intract_uc_index))) {
+            adev->interactive_usecase_state |= 0x1 << intract_uc_index;
+            ret_uc = interactive_usecases[intract_uc_index];
+            break;
+        }
+    }
+
+    ALOGV("%s: Interactive usecase is %d", __func__, ret_uc);
+    return ret_uc;
+}
+
+static void free_interactive_usecase(struct audio_device *adev,
+                                 audio_usecase_t uc_id)
+{
+    unsigned int interact_uc_index;
+    unsigned int num_usecase = sizeof(interactive_usecases)/sizeof(interactive_usecases[0]);
+
+    for (interact_uc_index = 0; interact_uc_index < num_usecase; interact_uc_index++) {
+        if (interactive_usecases[interact_uc_index] == uc_id) {
+            adev->interactive_usecase_state &= ~(0x1 << interact_uc_index);
+            break;
+        }
+    }
+    ALOGV("%s: free Interactive usecase %d", __func__, uc_id);
+}
+
 bool is_offload_usecase(audio_usecase_t uc_id)
 {
     unsigned int i;
@@ -2814,7 +2878,9 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
 
-    if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+    if (is_interactive_usecase(out->usecase)) {
+        return out->config.period_size;
+    } else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
         if (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)
             return out->compr_config.fragment_size - sizeof(struct snd_codec_metadata);
         else
@@ -5180,7 +5246,10 @@
 
         channels = audio_channel_count_from_out_mask(out->channel_mask);
 
-        if (out->flags & AUDIO_OUTPUT_FLAG_RAW) {
+        if (out->flags & AUDIO_OUTPUT_FLAG_INTERACTIVE) {
+            out->usecase = get_interactive_usecase(adev);
+            out->config = pcm_config_low_latency;
+        } else if (out->flags & AUDIO_OUTPUT_FLAG_RAW) {
             out->usecase = USECASE_AUDIO_PLAYBACK_ULL;
             out->realtime = may_use_noirq_mode(adev, USECASE_AUDIO_PLAYBACK_ULL,
                                                out->flags);
@@ -5411,6 +5480,9 @@
 
     out->a2dp_compress_mute = false;
 
+    if (is_interactive_usecase(out->usecase))
+        free_interactive_usecase(adev, out->usecase);
+
     if (out->convert_buffer != NULL) {
         free(out->convert_buffer);
         out->convert_buffer = NULL;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 8228f3b..fec2400 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -170,6 +170,15 @@
     USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
 
     USECASE_AUDIO_TRANSCODE_LOOPBACK,
+
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7,
+    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8,
     AUDIO_USECASE_MAX
 };
 
@@ -312,6 +321,8 @@
 
     char pm_qos_mixer_path[MAX_MIXER_PATH_LEN];
     int dynamic_pm_qos_enabled;
+    mix_matrix_params_t pan_scale_params;
+    mix_matrix_params_t downmix_params;
 };
 
 struct stream_in {
@@ -484,6 +495,7 @@
     bool vr_audio_mode_enabled;
     bool bt_sco_on;
     struct audio_device_config_param *device_cfg_params;
+    unsigned int interactive_usecase_state;
 };
 
 int select_devices(struct audio_device *adev,
@@ -526,6 +538,8 @@
 void adev_close_output_stream(struct audio_hw_device *dev __unused,
                               struct audio_stream_out *stream);
 
+bool is_interactive_usecase(audio_usecase_t uc_id);
+
 #define LITERAL_TO_STRING(x) #x
 #define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)\
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 7778b2b..3e71527 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -377,6 +377,23 @@
     [USECASE_AUDIO_PLAYBACK_VOIP] = {AUDIO_PLAYBACK_VOIP_PCM_DEVICE, AUDIO_PLAYBACK_VOIP_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_VOIP] = {AUDIO_RECORD_VOIP_PCM_DEVICE, AUDIO_RECORD_VOIP_PCM_DEVICE},
 
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE1, PLAYBACK_INTERACTIVE_STRM_DEVICE1},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE2, PLAYBACK_INTERACTIVE_STRM_DEVICE2},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE3, PLAYBACK_INTERACTIVE_STRM_DEVICE3},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE4, PLAYBACK_INTERACTIVE_STRM_DEVICE4},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE5, PLAYBACK_INTERACTIVE_STRM_DEVICE5},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE6, PLAYBACK_INTERACTIVE_STRM_DEVICE6},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE7, PLAYBACK_INTERACTIVE_STRM_DEVICE7},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE8, PLAYBACK_INTERACTIVE_STRM_DEVICE8},
+
 };
 
 /* Array to store sound devices */
@@ -6174,6 +6191,147 @@
      *length = msm_be_id_array_len;
 }
 
+int platform_set_stream_pan_scale_params(void *platform,
+                                         int snd_id,
+                                         struct mix_matrix_params mm_params)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl = NULL;
+    char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
+    int ret = 0;
+    int iter_i = 0;
+    int iter_j = 0;
+    int length = 0;
+    int pan_scale_data[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
+
+    if (sizeof(mm_params) > MAX_LENGTH_MIXER_CONTROL_IN_INT) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+                          "Audio Stream %d Pan Scale Control", snd_id);
+    ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+        goto end;
+    }
+    pan_scale_data[length++] = mm_params.num_output_channels;
+    pan_scale_data[length++] = mm_params.num_input_channels;
+
+    pan_scale_data[length++] = mm_params.has_output_channel_map;
+    if (mm_params.has_output_channel_map &&
+        mm_params.num_output_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_output_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            pan_scale_data[length++] = mm_params.output_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    pan_scale_data[length++] = mm_params.has_input_channel_map;
+    if (mm_params.has_input_channel_map &&
+        mm_params.num_input_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_input_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_input_channels; iter_i++)
+            pan_scale_data[length++] = mm_params.input_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    pan_scale_data[length++] = mm_params.has_mixer_coeffs;
+    if (mm_params.has_mixer_coeffs)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            for (iter_j = 0; iter_j < mm_params.num_input_channels; iter_j++)
+                pan_scale_data[length++] =
+                                    mm_params.mixer_coeffs[iter_i][iter_j];
+
+    ret = mixer_ctl_set_array(ctl, pan_scale_data, length);
+end:
+    return ret;
+}
+
+int platform_set_stream_downmix_params(void *platform,
+                                       int snd_id,
+                                       snd_device_t snd_device,
+                                       struct mix_matrix_params mm_params)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl;
+    char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
+    int downmix_param_data[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
+    int ret = 0;
+    int iter_i = 0;
+    int iter_j = 0;
+    int length = 0;
+    int be_idx = 0;
+
+    if ((sizeof(mm_params) +
+         sizeof(be_idx)) >
+        MAX_LENGTH_MIXER_CONTROL_IN_INT) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+                          "Audio Device %d Downmix Control", snd_id);
+    ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+        goto end;
+    }
+
+    be_idx = platform_get_snd_device_backend_index(snd_device);
+    downmix_param_data[length]   = be_idx;
+    downmix_param_data[length++] = mm_params.num_output_channels;
+    downmix_param_data[length++] = mm_params.num_input_channels;
+
+    downmix_param_data[length++] = mm_params.has_output_channel_map;
+    if (mm_params.has_output_channel_map &&
+        mm_params.num_output_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_output_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            downmix_param_data[length++] = mm_params.output_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    downmix_param_data[length++] = mm_params.has_input_channel_map;
+    if (mm_params.has_input_channel_map &&
+        mm_params.num_input_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_input_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_input_channels; iter_i++)
+            downmix_param_data[length++] = mm_params.input_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    downmix_param_data[length++] = mm_params.has_mixer_coeffs;
+    if (mm_params.has_mixer_coeffs)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            for (iter_j = 0; iter_j < mm_params.num_input_channels; iter_j++)
+                downmix_param_data[length++] =
+                                       mm_params.mixer_coeffs[iter_i][iter_j];
+
+    ret = mixer_ctl_set_array(ctl, downmix_param_data, length);
+end:
+    return ret;
+}
+
 int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask,
                                                int snd_id, uint8_t *input_channel_map)
 {
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index 7a950a8..30ae2c7 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -359,6 +359,15 @@
 #define TRANSCODE_LOOPBACK_RX_DEV_ID 43
 #define TRANSCODE_LOOPBACK_TX_DEV_ID 44
 
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE1 0
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE2 1
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE3 27
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE4 45
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE5 46
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE6 47
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE7 48
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE8 49
+
 #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 1d787d0..6c1ab76 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -355,6 +355,22 @@
 
     [USECASE_AUDIO_PLAYBACK_VOIP] = {AUDIO_PLAYBACK_VOIP_PCM_DEVICE, AUDIO_PLAYBACK_VOIP_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_VOIP] = {AUDIO_RECORD_VOIP_PCM_DEVICE, AUDIO_RECORD_VOIP_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE1, PLAYBACK_INTERACTIVE_STRM_DEVICE1},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE2, PLAYBACK_INTERACTIVE_STRM_DEVICE2},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE3, PLAYBACK_INTERACTIVE_STRM_DEVICE3},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE4, PLAYBACK_INTERACTIVE_STRM_DEVICE4},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE5, PLAYBACK_INTERACTIVE_STRM_DEVICE5},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE6, PLAYBACK_INTERACTIVE_STRM_DEVICE6},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE7, PLAYBACK_INTERACTIVE_STRM_DEVICE7},
+    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8] =
+                     {PLAYBACK_INTERACTIVE_STRM_DEVICE8, PLAYBACK_INTERACTIVE_STRM_DEVICE8},
 
 };
 
@@ -6018,6 +6034,145 @@
      *length = msm_be_id_array_len;
 }
 
+int platform_set_stream_pan_scale_params(void *platform,
+                                         int snd_id,
+                                         struct mix_matrix_params mm_params)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl = NULL;
+    char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
+    int ret = 0;
+    int iter_i = 0;
+    int iter_j = 0;
+    int length = 0;
+    int pan_scale_data[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
+
+    if (sizeof(mm_params) > MAX_LENGTH_MIXER_CONTROL_IN_INT) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+                          "Audio Stream %d Pan Scale Control", snd_id);
+    ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+        goto end;
+    }
+    pan_scale_data[length++] = mm_params.num_output_channels;
+    pan_scale_data[length++] = mm_params.num_input_channels;
+
+    pan_scale_data[length++] = mm_params.has_output_channel_map;
+    if (mm_params.has_output_channel_map &&
+        mm_params.num_output_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_output_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            pan_scale_data[length++] = mm_params.output_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    pan_scale_data[length++] = mm_params.has_input_channel_map;
+    if (mm_params.has_input_channel_map &&
+        mm_params.num_input_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_input_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_input_channels; iter_i++)
+            pan_scale_data[length++] = mm_params.input_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+    pan_scale_data[length++] = mm_params.has_mixer_coeffs;
+    if (mm_params.has_mixer_coeffs)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            for (iter_j = 0; iter_j < mm_params.num_input_channels; iter_j++)
+                pan_scale_data[length++] =
+                                    mm_params.mixer_coeffs[iter_i][iter_j];
+
+    ret = mixer_ctl_set_array(ctl, pan_scale_data, length);
+end:
+    return ret;
+}
+
+int platform_set_stream_downmix_params(void *platform,
+                                       int snd_id,
+                                       snd_device_t snd_device,
+                                       struct mix_matrix_params mm_params)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl;
+    char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
+    int downmix_param_data[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
+    int ret = 0;
+    int iter_i = 0;
+    int iter_j = 0;
+    int length = 0;
+    int be_idx = 0;
+
+    if ((sizeof(mm_params) +
+         sizeof(be_idx)) >
+        MAX_LENGTH_MIXER_CONTROL_IN_INT) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+                          "Audio Device %d Downmix Control", snd_id);
+    ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+    }
+
+    be_idx = platform_get_snd_device_backend_index(snd_device);
+    downmix_param_data[length]   = be_idx;
+    downmix_param_data[length++] = mm_params.num_output_channels;
+    downmix_param_data[length++] = mm_params.num_input_channels;
+
+    downmix_param_data[length++] = mm_params.has_output_channel_map;
+    if (mm_params.has_output_channel_map &&
+        mm_params.num_output_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_output_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            downmix_param_data[length++] = mm_params.output_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    downmix_param_data[length++] = mm_params.has_input_channel_map;
+    if (mm_params.has_input_channel_map &&
+        mm_params.num_input_channels <= MAX_CHANNELS_SUPPORTED &&
+        mm_params.num_input_channels > 0)
+        for (iter_i = 0; iter_i < mm_params.num_input_channels; iter_i++)
+            downmix_param_data[length++] = mm_params.input_channel_map[iter_i];
+    else {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    downmix_param_data[length++] = mm_params.has_mixer_coeffs;
+    if (mm_params.has_mixer_coeffs)
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            for (iter_j = 0; iter_j < mm_params.num_input_channels; iter_j++)
+                downmix_param_data[length++] =
+                                       mm_params.mixer_coeffs[iter_i][iter_j];
+
+    ret = mixer_ctl_set_array(ctl, downmix_param_data, length);
+end:
+    return ret;
+}
+
 int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask,
                                                int snd_id, uint8_t *input_channel_map)
 {
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 33edf75..f4d60c2 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -465,6 +465,15 @@
 #define TRANSCODE_LOOPBACK_RX_DEV_ID 43
 #define TRANSCODE_LOOPBACK_TX_DEV_ID 44
 
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE1 0
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE2 1
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE3 27
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE4 45
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE5 46
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE6 47
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE7 48
+#define PLAYBACK_INTERACTIVE_STRM_DEVICE8 49
+
 #ifdef PLATFORM_APQ8084
 #define FM_RX_VOLUME "Quat MI2S FM RX Volume"
 #elif PLATFORM_MSM8994
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 9a34582..6f8cf7e 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -169,6 +169,13 @@
                              int snd_id);
 int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask,
                                    int snd_id, uint8_t *input_channel_map);
+int platform_set_stream_pan_scale_params(void *platform,
+                                         int snd_id,
+                                         struct mix_matrix_params mm_params);
+int platform_set_stream_downmix_params(void *platform,
+                                       int snd_id,
+                                       snd_device_t snd_device,
+                                       struct mix_matrix_params mm_params);
 int platform_set_edid_channels_configuration(void *platform, int channels);
 unsigned char platform_map_to_edid_format(int format);
 bool platform_is_edid_supported_format(void *platform, int format);
diff --git a/qahw_api/inc/qahw_defs.h b/qahw_api/inc/qahw_defs.h
index 23e51cb..fa780bd 100644
--- a/qahw_api/inc/qahw_defs.h
+++ b/qahw_api/inc/qahw_defs.h
@@ -177,6 +177,9 @@
 /* Query if a2dp  is supported */
 #define QAHW_PARAMETER_KEY_HANDLE_A2DP_DEVICE "isA2dpDeviceSupported"
 
+#define MAX_OUT_CHANNELS 8
+#define MAX_INP_CHANNELS 8
+
 /* type of asynchronous write callback events. Mutually exclusive */
 typedef enum {
     QAHW_STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */
@@ -323,6 +326,17 @@
    uint16_t   channel_allocation;
 };
 
+typedef struct qahw_mix_matrix_params {
+    uint16_t num_output_channels;
+    uint16_t num_input_channels;
+    uint8_t has_output_channel_map;
+    uint32_t output_channel_map[AUDIO_CHANNEL_COUNT_MAX];
+    uint8_t has_input_channel_map;
+    uint32_t input_channel_map[AUDIO_CHANNEL_COUNT_MAX];
+    uint8_t has_mixer_coeffs;
+    float mixer_coeffs[AUDIO_CHANNEL_COUNT_MAX][AUDIO_CHANNEL_COUNT_MAX];
+} qahw_mix_matrix_params_t;
+
 typedef union {
     struct qahw_source_tracking_param st_params;
     struct qahw_sound_focus_param sf_params;
@@ -335,6 +349,7 @@
     struct qahw_adsp_event adsp_event_params;
     struct qahw_out_channel_map_param channel_map_params;
     struct qahw_device_cfg_param device_cfg_params;
+    struct qahw_mix_matrix_params mix_matrix_params;
 } qahw_param_payload;
 
 typedef enum {
@@ -350,7 +365,9 @@
     QAHW_PARAM_OUT_CORRECT_DRIFT,
     QAHW_PARAM_ADSP_STREAM_CMD,
     QAHW_PARAM_OUT_CHANNEL_MAP,    /* PARAM to set i/p channel map */
-    QAHW_PARAM_DEVICE_CONFIG      /* PARAM to set device config */
+    QAHW_PARAM_DEVICE_CONFIG,      /* PARAM to set device config */
+    QAHW_PARAM_OUT_MIX_MATRIX_PARAMS,
+    QAHW_PARAM_CH_MIX_MATRIX_PARAMS,
 } qahw_param_id;
 
 __END_DECLS
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index db189eb..3c986bd 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -22,6 +22,7 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
@@ -52,7 +53,7 @@
 #define FORMAT_PCM 1
 #define WAV_HEADER_LENGTH_MAX 46
 
-#define MAX_PLAYBACK_STREAMS   3
+#define MAX_PLAYBACK_STREAMS   9
 #define PRIMARY_STREAM_INDEX   0
 
 #define KVPAIRS_MAX 100
@@ -194,6 +195,7 @@
     pthread_mutex_t write_lock;
     pthread_cond_t drain_cond;
     pthread_mutex_t drain_lock;
+    bool interactive_strm;
 }stream_config;
 
 /* Lock for dual main usecase */
@@ -263,6 +265,9 @@
 #define AUDIO_OUTPUT_FLAG_ASSOCIATED 0x8000
 #endif
 
+#ifndef AUDIO_OUTPUT_FLAG_INTERACTIVE
+#define AUDIO_OUTPUT_FLAG_INTERACTIVE 0x40000
+#endif
 
 static bool request_wake_lock(bool wakelock_acquired, bool enable)
 {
@@ -683,6 +688,12 @@
             fprintf(log_file, "stream %d: after the dual main signal\n", params->stream_index);
             pthread_mutex_unlock(&dual_main_lock);
     }
+
+    if (params->interactive_strm) {
+        params->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE;
+        fprintf(stderr, "stream %s %d: Interactive stream\n", __func__, params->stream_index);
+    }
+
     rc = qahw_open_output_stream(params->qahw_out_hal_handle,
                              params->handle,
                              params->output_device,
@@ -1066,7 +1077,10 @@
     int rc = 0;
 
     if (!(stream_info->flags_set)) {
-        stream_info->flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+        if (stream_info->interactive_strm)
+            stream_info->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE;
+        else
+            stream_info->flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING;
         stream_info->flags |= AUDIO_OUTPUT_FLAG_DIRECT;
     }
 
@@ -1458,6 +1472,10 @@
     else
         direction = PCM_OUT;
 
+    if (direction == PCM_OUT && stream->interactive_strm) {
+        stream->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE;
+        fprintf(stderr, "stream %d: Interactive stream\n", stream->stream_index);
+    }
     fprintf(log_file, "%s: opening %s stream\n", __func__, ((direction == PCM_IN)? "input":"output"));
 
     if (PCM_IN == direction)
@@ -1595,6 +1613,10 @@
     printf(" -m  --mode                                - usb operating mode(Device Mode is default)\n");
     printf("                                             0:Device Mode(host drives the stream and its params and so no need to give params as input)\n");
     printf("                                             1:Host Mode(user can give stream and stream params via a stream(SD card file) or setup loopback with given params\n");
+    printf(" -O  --output-ch-map                       - output channel map");
+    printf(" -I  --input-ch-map                        - input channel map");
+    printf(" -M  --mixer-coeffs                        - mixer coefficient matrix");
+    printf(" -i  --intr-strm                           - interactive stream indicator");
     printf(" \n Examples \n");
     printf(" hal_play_test -f /data/Anukoledenadu.wav  -> plays Wav stream with default params\n\n");
     printf(" hal_play_test -f /data/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
@@ -1852,15 +1874,146 @@
     return 1;
 }
 
+audio_channel_mask_t get_channel_mask_for_name(char *name) {
+    audio_channel_mask_t channel_type = AUDIO_CHANNEL_INVALID;
+    if (NULL == name)
+        return channel_type;
+    else if (strncmp(name, "fl", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_FRONT_LEFT;
+    else if (strncmp(name, "fr", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_FRONT_RIGHT;
+    else if (strncmp(name, "fc", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_FRONT_CENTER;
+    else if (strncmp(name, "lfe", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
+    else if (strncmp(name, "bl", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_BACK_LEFT;
+    else if (strncmp(name, "br", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_BACK_RIGHT;
+    else if (strncmp(name, "flc", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
+    else if (strncmp(name, "frc", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
+    else if (strncmp(name, "bc", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_BACK_CENTER;
+    else if (strncmp(name, "sl", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_SIDE_LEFT;
+    else if (strncmp(name, "sr", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_SIDE_RIGHT;
+    else if (strncmp(name, "tc", 2) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_CENTER;
+    else if (strncmp(name, "tfl", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT;
+    else if (strncmp(name, "tfc", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER;
+    else if (strncmp(name, "tfr", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT;
+    else if (strncmp(name, "tbl", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_BACK_LEFT;
+    else if (strncmp(name, "tbc", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_BACK_CENTER;
+    else if (strncmp(name, "tbr", 3) == 0)
+        channel_type = AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT;
+
+    return channel_type;
+}
+
+int extract_channel_mapping(uint32_t *channel_map, const char * arg_string){
+
+    char *token_string = NULL;
+    char *init_ptr = NULL;
+    char *token = NULL;
+    char *saveptr = NULL;
+
+    if (NULL == channel_map)
+        return -EINVAL;
+
+    if (NULL == arg_string)
+        return EINVAL;
+
+    token_string = strdup(arg_string);
+
+    if(token_string != NULL) {
+        init_ptr = token_string;
+        token = strtok_r(token_string, ",", &saveptr);
+        int index = 0;
+        if (NULL == token)
+            return -EINVAL;
+        else
+            channel_map[index++] = get_channel_mask_for_name(token);
+
+        while(NULL !=(token = strtok_r(NULL,",",&saveptr)))
+            channel_map[index++] = get_channel_mask_for_name(token);
+
+        free(init_ptr);
+        init_ptr = NULL;
+        token_string = NULL;
+    } else
+        return -EINVAL;
+    return 0;
+}
+
+int extract_mixer_coeffs(qahw_mix_matrix_params_t * mm_params, const char * arg_string){
+
+    char *token_string = NULL;
+    char *init_ptr = NULL;
+    char *token = NULL;
+    char *saveptr = NULL;
+    int i = 0;
+    int j = 0;
+
+    if (NULL == mm_params)
+        return -EINVAL;
+
+    if (NULL == arg_string)
+        return -EINVAL;
+
+    token_string = strdup(arg_string);
+
+    if(token_string != NULL) {
+        init_ptr = token_string;
+        token = strtok_r(token_string, ",", &saveptr);
+        int index = 0;
+        if (NULL == token)
+            return -EINVAL;
+        else {
+            mm_params->mixer_coeffs[i][j] = atof(token);
+            j++;
+        }
+
+        while(NULL !=(token = strtok_r(NULL,",",&saveptr))) {
+            if(j == mm_params->num_input_channels) {
+                j=0;
+                i++;
+            }
+            if(i == mm_params->num_output_channels)
+                break;
+            mm_params->mixer_coeffs[i][j++] = atof(token);
+        }
+        free(init_ptr);
+        init_ptr = NULL;
+        token_string = NULL;
+    } else
+        return -EINVAL;
+    return 0;
+}
 
 int main(int argc, char* argv[]) {
     char *ba = NULL;
+    char *temp_input_channel_map = NULL;
+    char *temp_output_channel_map = NULL;
+    char *temp_mixer_coeffs = NULL;
     qahw_param_payload payload;
     qahw_param_id param_id;
     struct qahw_aptx_dec_param aptx_params;
+    qahw_mix_matrix_params_t mm_params;
     int rc = 0;
     int i = 0;
+    int iter_i = 0;
+    int iter_j = 0;
     kpi_mode = false;
+    char mixer_ctrl_name[64] = {0};
+    int mixer_ctrl_type = 0;
     event_trigger = false;
     bool wakelock_acquired = false;
 
@@ -1899,6 +2052,11 @@
         {"effect-preset",   required_argument,    0, 'p'},
         {"effect-strength", required_argument,    0, 'S'},
         {"help",          no_argument,          0, 'h'},
+        {"output-ch-map", required_argument,    0, 'O'},
+        {"input-ch-map",  required_argument,    0, 'I'},
+        {"mixer-coeffs",  required_argument,    0, 'M'},
+        {"num-out-ch",    required_argument,    0, 'o'},
+        {"intr-strm",    required_argument,    0, 'i'},
         {0, 0, 0, 0}
     };
 
@@ -1921,7 +2079,7 @@
 
     while ((opt = getopt_long(argc,
                               argv,
-                              "-f:r:c:b:d:s:v:l:t:a:w:k:PD:KF:Ee:A:u:m:S:p:qQh",
+                              "-f:r:c:b:d:s:v:l:t:a:w:k:PD:KF:Ee:A:u:m:S:p:qQhI:O:M:o:i:",
                               long_options,
                               &option_index)) != -1) {
 
@@ -1938,6 +2096,7 @@
         case 'c':
             stream_param[i].channels = atoi(optarg);
             stream_param[i].config.channel_mask = audio_channel_out_mask_from_count(atoi(optarg));
+            mm_params.num_input_channels = stream_param[i].channels;
             break;
         case 'b':
             stream_param[i].config.offload_info.bit_width = atoi(optarg);
@@ -1962,6 +2121,9 @@
                 log_file = stdout;
             }
             break;
+        case 'i':
+            stream_param[i].interactive_strm = atoi(optarg);
+            break;
         case 't':
             stream_param[i].filetype = atoi(optarg);
             break;
@@ -1972,7 +2134,15 @@
             stream_param[i].wma_fmt_type = atoi(optarg);
             break;
         case 'k':
-            stream_param[i].kvpair_values = optarg;
+            get_kvpairs_string(optarg, "mixer_ctrl", mixer_ctrl_name);
+            if(strncmp(mixer_ctrl_name, "downmix", 7) == 0) {
+                mixer_ctrl_type = QAHW_PARAM_CH_MIX_MATRIX_PARAMS;
+            } else if(strncmp(mixer_ctrl_name, "pan_scale", 9) == 0) {
+                mixer_ctrl_type = QAHW_PARAM_OUT_MIX_MATRIX_PARAMS;
+            } else {
+                mixer_ctrl_type = 0;
+                stream_param[i].kvpair_values = optarg;
+            }
             break;
         case 'D':
             proxy_params.acp.file_name = optarg;
@@ -2033,6 +2203,18 @@
         case 'm':
             stream_param[i].usb_mode = atoi(optarg);
             break;
+        case 'O':
+            temp_output_channel_map = strdup(optarg);
+            break;
+        case 'I':
+            temp_input_channel_map = strdup(optarg);
+            break;
+        case 'M':
+            temp_mixer_coeffs = strdup(optarg);
+            break;
+        case 'o':
+            mm_params.num_output_channels = atoi(optarg);
+            break;
         case 'h':
             usage();
             return 0;
@@ -2041,6 +2223,49 @@
         }
     }
 
+    /*
+     * Process Input channel map's input
+     */
+    if (NULL != temp_input_channel_map) {
+        extract_channel_mapping(mm_params.input_channel_map, temp_input_channel_map);
+        mm_params.has_input_channel_map = 1;
+        fprintf(log_file, "\nInput channel mapping: ");
+        for (iter_i= 0; iter_i < mm_params.num_input_channels; iter_i++) {
+            fprintf(log_file, "0x%x, ", mm_params.input_channel_map[iter_i]);
+        }
+        free(temp_input_channel_map);
+        temp_input_channel_map = NULL;
+    }
+
+    /*
+     * Process Output channel map's input
+     */
+    if (NULL != temp_output_channel_map) {
+        extract_channel_mapping(mm_params.output_channel_map, temp_output_channel_map);
+        mm_params.has_output_channel_map = 1;
+        fprintf(log_file, "\nOutput channel mapping: ");
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++)
+            fprintf(log_file, "0x%x, ", mm_params.output_channel_map[iter_i]);
+
+        free(temp_output_channel_map);
+        temp_output_channel_map = NULL;
+    }
+
+    /*
+     * Process mixer-coeffs input
+     */
+    if (NULL != temp_mixer_coeffs) {
+        extract_mixer_coeffs(&mm_params, temp_mixer_coeffs);
+        mm_params.has_mixer_coeffs = 1;
+        fprintf(log_file, "\nmixer coeffs:\n");
+        for (iter_i = 0; iter_i < mm_params.num_output_channels; iter_i++){
+            for (iter_j = 0; iter_j < mm_params.num_input_channels; iter_j++){
+                fprintf(log_file, "%.2f ",mm_params.mixer_coeffs[iter_i][iter_j]);
+            }
+            fprintf(log_file, "\n");
+        }
+    }
+
     wakelock_acquired = request_wake_lock(wakelock_acquired, true);
     num_of_streams = i+1;
     /* Caution: Below ADL log shouldnt be altered without notifying automation APT since it used
@@ -2085,7 +2310,7 @@
     for (i = 0; i < num_of_streams; i++) {
         stream = &stream_param[i];
 
-        if ((kpi_mode == false) && 
+        if ((kpi_mode == false) &&
             (AUDIO_DEVICE_NONE == stream->input_device)){
             if (stream_param[PRIMARY_STREAM_INDEX].filename == nullptr) {
                 fprintf(log_file, "Primary file name is must for non kpi-mode\n");
@@ -2189,7 +2414,6 @@
             fprintf(log_file, "stream %d: play_later = %d\n", i, stream_param[i].play_later);
         }
 
-
         rc = pthread_create(&playback_thread[i], NULL, start_stream_playback, (void *)&stream_param[i]);
         if (rc) {
             fprintf(log_file, "stream %d: failed to create thread\n", stream->stream_index);
@@ -2198,7 +2422,23 @@
         }
 
         thread_active[i] = true;
-
+        usleep(500000); //Wait until stream is created
+        if(mixer_ctrl_type == QAHW_PARAM_OUT_MIX_MATRIX_PARAMS) {
+            payload = (qahw_param_payload) mm_params;
+            param_id = QAHW_PARAM_OUT_MIX_MATRIX_PARAMS;
+            rc = qahw_out_set_param_data(stream->out_handle, param_id, &payload);
+            if (rc != 0) {
+                fprintf(log_file, "QAHW_PARAM_OUT_MIX_MATRIX_PARAMS could not be sent!\n");
+            }
+        }
+        if(mixer_ctrl_type == QAHW_PARAM_CH_MIX_MATRIX_PARAMS) {
+            payload = (qahw_param_payload) mm_params;
+            param_id = QAHW_PARAM_CH_MIX_MATRIX_PARAMS;
+            rc = qahw_out_set_param_data(stream->out_handle, param_id, &payload);
+            if (rc != 0) {
+                fprintf(log_file, "QAHW_PARAM_CH_MIX_MATRIX_PARAMS could not be sent!\n");
+            }
+        }
     }
 
 exit: