hal: post_proc: add volume based audio calibration support

- add new effect module to listen to volume and device change
- based on volume levels of all streams active over speaker
  compute gain based calibration level and communicate
  with audio HAL to send the same to ACDB loader
- make audio HAL singleton

Bug: 22100304.

Change-Id: If74bf66d32def85022d79ccb9f84c3b85c8a2dc9
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 4ad4321..49e7a07 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -181,6 +181,30 @@
 };
 
 static int set_voice_volume_l(struct audio_device *adev, float volume);
+static struct audio_device *adev = NULL;
+static pthread_mutex_t adev_init_lock;
+static unsigned int audio_device_ref_count;
+
+__attribute__ ((visibility ("default")))
+bool audio_hw_send_gain_dep_calibration(int level) {
+    bool ret_val = false;
+    ALOGV("%s: enter ... ", __func__);
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if (adev != NULL && adev->platform != NULL) {
+        pthread_mutex_lock(&adev->lock);
+        ret_val = platform_send_gain_dep_cal(adev->platform, level);
+        pthread_mutex_unlock(&adev->lock);
+    } else {
+        ALOGE("%s: %s is NULL", __func__, adev == NULL ? "adev" : "adev->platform");
+    }
+
+    pthread_mutex_unlock(&adev_init_lock);
+
+    ALOGV("%s: exit with ret_val %d ", __func__, ret_val);
+    return ret_val;
+}
 
 static bool is_supported_format(audio_format_t format)
 {
@@ -1593,7 +1617,7 @@
 
     if (ret != 0) {
         if (out->pcm)
-            ALOGE("%s: error %d - %s", __func__, ret, pcm_get_error(out->pcm));
+            ALOGE("%s: error %zu - %s", __func__, ret, pcm_get_error(out->pcm));
         out_standby(&out->stream.common);
         usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
                out_get_sample_rate(&out->stream.common));
@@ -1660,7 +1684,7 @@
         }
     } else {
         if (out->pcm) {
-            size_t avail;
+            unsigned int avail;
             if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
                 size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
                 int64_t signed_frames = out->written - kernel_buffer_size + avail;
@@ -2694,15 +2718,25 @@
 {
     size_t i;
     struct audio_device *adev = (struct audio_device *)device;
-    audio_route_free(adev->audio_route);
-    free(adev->snd_dev_ref_cnt);
-    platform_deinit(adev->platform);
-    audio_extn_extspk_deinit(adev->extspk);
-    audio_extn_sound_trigger_deinit(adev);
-    for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
-        pcm_params_free(adev->use_case_table[i]);
+
+    if (!adev)
+        return 0;
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if ((--audio_device_ref_count) == 0) {
+        audio_route_free(adev->audio_route);
+        free(adev->snd_dev_ref_cnt);
+        platform_deinit(adev->platform);
+        audio_extn_extspk_deinit(adev->extspk);
+        audio_extn_sound_trigger_deinit(adev);
+        for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
+            pcm_params_free(adev->use_case_table[i]);
+        }
+        free(device);
     }
-    free(device);
+
+    pthread_mutex_unlock(&adev_init_lock);
     return 0;
 }
 
@@ -2730,12 +2764,19 @@
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
-    struct audio_device *adev;
     int i, ret;
 
     ALOGD("%s: enter", __func__);
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
-
+    pthread_mutex_lock(&adev_init_lock);
+    if (audio_device_ref_count != 0) {
+        *device = &adev->device.common;
+        audio_device_ref_count++;
+        ALOGV("%s: returning existing instance of adev", __func__);
+        ALOGV("%s: exit", __func__);
+        pthread_mutex_unlock(&adev_init_lock);
+        return 0;
+    }
     adev = calloc(1, sizeof(struct audio_device));
 
     pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
@@ -2783,6 +2824,7 @@
         free(adev);
         ALOGE("%s: Failed to init platform data, aborting.", __func__);
         *device = NULL;
+        pthread_mutex_unlock(&adev_init_lock);
         return -EINVAL;
     }
 
@@ -2825,6 +2867,7 @@
     adev->enable_voicerx = false;
 
     *device = &adev->device.common;
+
     if (k_enable_extended_precision)
         adev_verify_devices(adev);
 
@@ -2846,6 +2889,9 @@
         }
     }
 
+    audio_device_ref_count++;
+    pthread_mutex_unlock(&adev_init_lock);
+
     ALOGV("%s: exit", __func__);
     return 0;
 }
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 4536a05..acfa991 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1008,7 +1008,9 @@
     return -ENOSYS;
 }
 
-void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
+void platform_set_echo_reference(struct audio_device *adev __unused,
+                                 bool enable __unused,
+                                 audio_devices_t out_device __unused)
 {
     return;
 }
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 854acea..8aa917d 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -27,6 +27,7 @@
 #include <platform_api.h>
 #include "platform.h"
 #include "audio_extn.h"
+#include <linux/msm_audio.h>
 
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
 #define LIB_ACDB_LOADER "libacdbloader.so"
@@ -54,12 +55,20 @@
 #define RETRY_US 500000
 #define MAX_SND_CARD 8
 
+#define DEFAULT_APP_TYPE_RX_PATH  0x11130
+
 struct audio_block_header
 {
     int reserved;
     int length;
 };
 
+enum {
+    CAL_MODE_SEND           = 0x1,
+    CAL_MODE_PERSIST        = 0x2,
+    CAL_MODE_RTAC           = 0x4
+};
+
 /* Audio calibration related functions */
 typedef void (*acdb_deallocate_t)();
 #ifdef PLATFORM_MSM8084
@@ -70,6 +79,7 @@
 typedef void (*acdb_send_audio_cal_t)(int, int);
 typedef void (*acdb_send_voice_cal_t)(int, int);
 typedef int (*acdb_reload_vocvoltable_t)(int);
+typedef int (*acdb_send_gain_dep_cal_t)(int, int, int, int, int);
 
 /* Audio calibration related functions */
 struct platform_data {
@@ -87,6 +97,7 @@
     acdb_send_audio_cal_t      acdb_send_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
+    acdb_send_gain_dep_cal_t   acdb_send_gain_dep_cal;
     struct csd_data *csd;
     bool ext_speaker;
     bool ext_earpiece;
@@ -431,6 +442,57 @@
     return is_tmus;
 }
 
+bool platform_send_gain_dep_cal(void *platform, int level)
+{
+    bool ret_val = false;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    int acdb_dev_id, app_type;
+    int acdb_dev_type = MSM_SNDDEV_CAP_RX;
+    int mode = CAL_MODE_RTAC;
+    struct listnode *node;
+    struct audio_usecase *usecase;
+	snd_device_t snd_device;
+
+    if (my_data->acdb_send_gain_dep_cal == NULL) {
+        ALOGE("%s: dlsym error for acdb_send_gain_dep_cal", __func__);
+        return ret_val;
+    }
+
+    if (!voice_is_in_call(adev)) {
+        ALOGV("%s: Not Voice call usecase, apply new cal for level %d",
+               __func__, level);
+        app_type = DEFAULT_APP_TYPE_RX_PATH;
+
+        // find the current active sound device
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+
+            if (usecase != NULL &&
+                usecase->type == PCM_PLAYBACK &&
+                (usecase->stream.out->devices == AUDIO_DEVICE_OUT_SPEAKER)) {
+
+                ALOGV("%s: out device is %d", __func__,  usecase->out_snd_device);
+                snd_device = audio_extn_spkr_prot_get_acdb_id(usecase->out_snd_device);
+                acdb_dev_id = acdb_device_table[snd_device];
+                if (!my_data->acdb_send_gain_dep_cal(acdb_dev_id, app_type,
+                                                     acdb_dev_type, mode, level)) {
+                    // set ret_val true if at least one calibration is set successfully
+                    ret_val = true;
+                } else {
+                    ALOGE("%s: my_data->acdb_send_gain_dep_cal failed ", __func__);
+                }
+            } else {
+                ALOGW("%s: Usecase list is empty", __func__);
+            }
+        }
+    } else {
+        ALOGW("%s: Voice call in progress .. ignore setting new cal",
+              __func__);
+    }
+    return ret_val;
+}
+
 void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
 {
     struct platform_data *my_data = (struct platform_data *)adev->platform;
@@ -821,6 +883,13 @@
         if (!my_data->acdb_reload_vocvoltable)
             ALOGE("%s: Could not find the symbol acdb_loader_reload_vocvoltable from %s",
                   __func__, LIB_ACDB_LOADER);
+
+        my_data->acdb_send_gain_dep_cal = (acdb_send_gain_dep_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_send_gain_dep_cal");
+        if (!my_data->acdb_send_gain_dep_cal)
+            ALOGV("%s: Could not find the symbol acdb_loader_send_gain_dep_cal from %s",
+                  __func__, LIB_ACDB_LOADER);
+
 #ifdef PLATFORM_MSM8084
         my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_init_v2");
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 9f65a63..8b2b09c 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -22,6 +22,7 @@
 const char *platform_get_snd_device_name(snd_device_t snd_device);
 void platform_add_backend_name(void *platform, char *mixer_path,
                                                     snd_device_t snd_device);
+bool platform_send_gain_dep_cal(void *platform, int level);
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
 int platform_get_snd_device_index(char *snd_device_index_name);
 int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id);