audio: add support for multichannel to dual mono conversion

Add support to set channel mix coffecients to convert from
multichannel to dual mono.

Change-Id: I2ecd8babfe53ca48b6e596dce0579e95879ce7e0
Signed-off-by: Surendar Karka <skarka@codeaurora.org>
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 5db3706..292e516 100755
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -63,6 +63,8 @@
 
 #define MAX_SLEEP_RETRY 100
 #define WIFI_INIT_WAIT_SLEEP 50
+#define MAX_NUM_CHANNELS 8
+#define Q14_GAIN_UNITY 0x4000
 
 struct audio_extn_module {
     bool anc_enabled;
@@ -1485,3 +1487,71 @@
 
     return 0;
 }
+
+void audio_extn_send_dual_mono_mixing_coefficients(struct stream_out *out)
+{
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    char mixer_ctl_name[128];
+    int cust_ch_mixer_cfg[128], len = 0;
+    int ip_channel_cnt = audio_channel_count_from_out_mask(out->channel_mask);
+    int pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+    int op_channel_cnt= 2;
+    int i, j, err;
+
+    ALOGV("%s", __func__);
+    if (!out->started) {
+        out->set_dual_mono = true;
+        goto exit;
+    }
+
+    ALOGD("%s: i/p channel count %d, o/p channel count %d, pcm id %d", __func__,
+           ip_channel_cnt, op_channel_cnt, pcm_device_id);
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Audio Stream %d Channel Mix Cfg", pcm_device_id);
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+        __func__, mixer_ctl_name);
+        goto exit;
+    }
+
+    /* Output channel count corresponds to backend configuration channels.
+     * Input channel count corresponds to ASM session channels.
+     * Set params is called with channels that need to be selected from
+     * input to generate output.
+     * ex: "8,2" to downmix from 8 to 2 i.e. to downmix from 8 to 2,
+     *
+     * This mixer control takes values in the following sequence:
+     * - input channel count(m)
+     * - output channel count(n)
+     * - weight coeff for [out ch#1, in ch#1]
+     * ....
+     * - weight coeff for [out ch#1, in ch#m]
+     *
+     * - weight coeff for [out ch#2, in ch#1]
+     * ....
+     * - weight coeff for [out ch#2, in ch#m]
+     *
+     * - weight coeff for [out ch#n, in ch#1]
+     * ....
+     * - weight coeff for [out ch#n, in ch#m]
+     *
+     * To get dualmono ouptu weightage coeff is calculated as Unity gain
+     * divided by number of input channels.
+     */
+    cust_ch_mixer_cfg[len++] = ip_channel_cnt;
+    cust_ch_mixer_cfg[len++] = op_channel_cnt;
+    for (i = 0; i < op_channel_cnt; i++) {
+         for (j = 0; j < ip_channel_cnt; j++) {
+              cust_ch_mixer_cfg[len++] = Q14_GAIN_UNITY/ip_channel_cnt;
+         }
+    }
+
+    err = mixer_ctl_set_array(ctl, cust_ch_mixer_cfg, len);
+    if (err)
+        ALOGE("%s: ERROR. Mixer ctl set failed", __func__);
+exit:
+    return;
+}
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index f1f45c0..4a0b8ce 100755
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -42,6 +42,8 @@
 #include "adsp_hdlr.h"
 #include "ip_hdlr_intf.h"
 
+#define AUDIO_PARAMETER_DUAL_MONO  "dual_mono"
+
 #ifndef AFE_PROXY_ENABLED
 #define AUDIO_DEVICE_OUT_PROXY 0x40000
 #endif
@@ -1033,4 +1035,10 @@
 snd_device_t audio_extn_ffv_get_capture_snd_device();
 void audio_extn_ffv_append_ec_ref_dev_name(char *device_name);
 #endif
+
+#ifndef CUSTOM_STEREO_ENABLED
+#define audio_extn_send_dual_mono_mixing_coefficients(out) (0)
+#else
+void audio_extn_send_dual_mono_mixing_coefficients(struct stream_out *out);
+#endif
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 9ebe142..3b8fb30 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -3076,6 +3076,8 @@
             }
             break;
         }
+        platform_set_stream_channel_map(adev->platform, out->channel_mask,
+                   out->pcm_device_id, &out->channel_map_param.channel_map[0]);
 
         ALOGV("%s: pcm_prepare", __func__);
         if (pcm_is_ready(out->pcm)) {
@@ -3089,8 +3091,6 @@
                 goto error_open;
             }
         }
-        platform_set_stream_channel_map(adev->platform, out->channel_mask,
-                   out->pcm_device_id, &out->channel_map_param.channel_map[0]);
         // apply volume for voip playback after path is set up
         if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP)
             out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
@@ -3775,6 +3775,13 @@
         pthread_mutex_unlock(&out->lock);
     }
 
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_DUAL_MONO, value,
+                            sizeof(value));
+    if (err >= 0) {
+        if (!strncmp("true", value, sizeof("true")) || atoi(value))
+            audio_extn_send_dual_mono_mixing_coefficients(out);
+    }
+
     err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
     if (err >= 0) {
         strlcpy(out->profile, value, sizeof(out->profile));
@@ -4342,6 +4349,8 @@
             ret = -EINVAL;
             goto exit;
         }
+        if (out->set_dual_mono)
+            audio_extn_send_dual_mono_mixing_coefficients(out);
     }
 
     if (adev->is_channel_status_set == false && (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)){
@@ -5639,6 +5648,7 @@
     out->a2dp_compress_mute = false;
     out->hal_output_suspend_supported = 0;
     out->dynamic_pm_qos_config_supported = 0;
+    out->set_dual_mono = false;
 
     if ((flags & AUDIO_OUTPUT_FLAG_BD) &&
         (property_get_bool("vendor.audio.matrix.limiter.enable", false)))
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index b70f66f..8cb395f 100755
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -357,6 +357,7 @@
     bool stream_config_changed;
     mix_matrix_params_t pan_scale_params;
     mix_matrix_params_t downmix_params;
+    bool set_dual_mono;
 };
 
 struct stream_in {
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index bbad92c..3ce01ef 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -251,6 +251,7 @@
         stream_param[i].ethread_data                        =   nullptr;
         stream_param[i].device_url                          =   "stream";
         stream_param[i].play_later                          =   false;
+        stream_param[i].set_params                          =   nullptr;
 
         pthread_mutex_init(&stream_param[i].write_lock, (const pthread_mutexattr_t *)NULL);
         pthread_cond_init(&stream_param[i].write_cond, (const pthread_condattr_t *) NULL);
@@ -797,6 +798,14 @@
     if (bytes_to_read <= 0)
         read_complete_file = true;
 
+    if (params->set_params) {
+        rc = qahw_out_set_parameters(params->out_handle, params->set_params);
+        if (rc) {
+            fprintf(log_file, "stream %s: failed to set kvpairs\n", params->set_params);
+            fprintf(stderr, "stream %s: failed to set kvpairs\n", params->set_params);
+        }
+    }
+
     while (!exit && !stop_playback) {
         if (!bytes_remaining) {
             fprintf(log_file, "\nstream %d: reading bytes %zd\n", params->stream_index, bytes_wanted);
@@ -2095,7 +2104,7 @@
 
     while ((opt = getopt_long(argc,
                               argv,
-                              "-f:r:c:b:d:s:v:V:l:t:a:w:k:PD:KF:Ee:A:u:m:S:C:p::x:y:qQh:i:h:g:",
+                              "-f:r:c:b:d:s:v:V:l:t:a:w:k:PD:KF:Ee:A:u:m:S:C:p::x:y:qQh:i:h:g:O:",
                               long_options,
                               &option_index)) != -1) {
 
@@ -2233,6 +2242,9 @@
         case 'K':
             kpi_mode = true;
             break;
+        case 'O':
+            stream_param[i].set_params = optarg;
+            break;
         case 'F':
             stream_param[i].flags = atoll(optarg);
             stream_param[i].flags_set = true;
@@ -2533,6 +2545,7 @@
         fprintf(log_file, "stream %d: Bitwidth:%d\n", stream->stream_index, stream->config.offload_info.bit_width);
         fprintf(log_file, "stream %d: AAC Format Type:%d\n", stream->stream_index, stream->aac_fmt_type);
         fprintf(log_file, "stream %d: Kvpair Values:%s\n", stream->stream_index, stream->kvpair_values);
+        fprintf(log_file, "stream %d: set params Values:%s\n", stream->stream_index, stream->set_params);
         fprintf(log_file, "Log file:%s\n", log_filename);
         fprintf(log_file, "Volume level:%f\n", vol_level);
 
diff --git a/qahw_api/test/qahw_playback_test.h b/qahw_api/test/qahw_playback_test.h
index 66229f5..b8bddbc 100644
--- a/qahw_api/test/qahw_playback_test.h
+++ b/qahw_api/test/qahw_playback_test.h
@@ -143,6 +143,7 @@
     pthread_cond_t input_buffer_available_cond;
     pthread_mutex_t input_buffer_available_lock;
     uint32_t input_buffer_available_size;
+    char *set_params;
 }stream_config;
 
 qahw_module_handle_t * load_hal(audio_devices_t dev);