hal: add support to override volume gain to step mapping table

- if customer mapping is added in platform info file, use that
- in absence of customer table use default mapping

BUG 28897755
Change-Id: I4bf8bcf1913f16ad6298ff1f3fa5dd649c889b2a
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 911a49c..2a95ab8 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -369,6 +369,27 @@
     return ret_val;
 }
 
+__attribute__ ((visibility ("default")))
+int audio_hw_get_gain_level_mapping(struct amp_db_and_gain_table *mapping_tbl,
+                                    int table_size) {
+     int ret_val = 0;
+     ALOGV("%s: enter ... ", __func__);
+
+     pthread_mutex_lock(&adev_init_lock);
+     if (adev == NULL) {
+         ALOGW("%s: adev is NULL .... ", __func__);
+         goto done;
+     }
+
+     pthread_mutex_lock(&adev->lock);
+     ret_val = platform_get_gain_level_mapping(mapping_tbl, table_size);
+     pthread_mutex_unlock(&adev->lock);
+done:
+     pthread_mutex_unlock(&adev_init_lock);
+     ALOGV("%s: exit ... ", __func__);
+     return ret_val;
+}
+
 static bool is_supported_format(audio_format_t format)
 {
     switch (format) {
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 6a934c4..bd12e93 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1102,3 +1102,13 @@
     return false;
 }
 
+bool platform_add_gain_level_mapping(struct amp_db_and_gain_table *tbl_entry __unused)
+{
+    return false;
+}
+
+int platform_get_gain_level_mapping(struct amp_db_and_gain_table *mapping_tbl __unused,
+                                    int table_size __unused)
+{
+    return 0;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 220ccbb..f8ab959 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2733,3 +2733,53 @@
     }
     return 0;
 }
+
+static struct amp_db_and_gain_table tbl_mapping[MAX_VOLUME_CAL_STEPS];
+static int num_gain_tbl_entry = 0;
+
+bool platform_add_gain_level_mapping(struct amp_db_and_gain_table *tbl_entry) {
+
+    ALOGV("%s: enter .. add %f %f %d", __func__, tbl_entry->amp, tbl_entry->db, tbl_entry->level);
+    if (num_gain_tbl_entry == -1) {
+        ALOGE("%s: num entry beyond valid step levels or corrupted..rejecting custom mapping",
+               __func__);
+        return false;
+    }
+
+    if (num_gain_tbl_entry >= MAX_VOLUME_CAL_STEPS) {
+        ALOGE("%s: max entry reached max[%d] current index[%d]  .. rejecting", __func__,
+               MAX_VOLUME_CAL_STEPS, num_gain_tbl_entry);
+        num_gain_tbl_entry  = -1; // indicates error and no more info will be cached
+        return false;
+    }
+
+    if (num_gain_tbl_entry > 0 && tbl_mapping[num_gain_tbl_entry - 1].amp >= tbl_entry->amp) {
+        ALOGE("%s: value not in ascending order .. rejecting custom mapping", __func__);
+        num_gain_tbl_entry  = -1; // indicates error and no more info will be cached
+        return false;
+    }
+
+    tbl_mapping[num_gain_tbl_entry] = *tbl_entry;
+    ++num_gain_tbl_entry;
+
+    return true;
+}
+
+int platform_get_gain_level_mapping(struct amp_db_and_gain_table *mapping_tbl,
+                                    int table_size) {
+    int itt = 0;
+    ALOGV("platform_get_gain_level_mapping called ");
+
+    if (num_gain_tbl_entry <= 0 || num_gain_tbl_entry > MAX_VOLUME_CAL_STEPS) {
+        ALOGD("%s: empty or currupted gain_mapping_table", __func__);
+        return 0;
+    }
+
+    for (; itt < num_gain_tbl_entry && itt <= table_size; itt++) {
+        mapping_tbl[itt] = tbl_mapping[itt];
+        ALOGV("%s: added amp[%f] db[%f] level[%d]", __func__,
+               mapping_tbl[itt].amp, mapping_tbl[itt].db, mapping_tbl[itt].level);
+    }
+
+    return num_gain_tbl_entry;
+}
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 5e54e03..c813793 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -17,6 +17,15 @@
 #ifndef AUDIO_PLATFORM_API_H
 #define AUDIO_PLATFORM_API_H
 
+#include "voice.h"
+#define MAX_VOLUME_CAL_STEPS 15
+
+struct amp_db_and_gain_table {
+    float amp;
+    float db;
+    uint32_t level;
+};
+
 void *platform_init(struct audio_device *adev);
 void platform_deinit(void *platform);
 const char *platform_get_snd_device_name(snd_device_t snd_device);
@@ -57,6 +66,17 @@
                                            const char *operator,
                                            const char *mixer_path,
                                            unsigned int acdb_id);
+/* return true if adding entry success
+   return false if adding entry fails */
+
+bool platform_add_gain_level_mapping(struct amp_db_and_gain_table *tbl_entry);
+
+/* return 0 if no custome mapping table found or when error detected
+            use default mapping in this case
+   return > 0 indicates number of entries in mapping table */
+
+int platform_get_gain_level_mapping(struct amp_db_and_gain_table *mapping_tbl,
+                                    int table_size);
 
 /* returns the latency for a usecase in Us */
 int64_t platform_render_latency(audio_usecase_t usecase);
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 7432230..f6b57e3 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -24,6 +24,7 @@
 #include <audio_hw.h>
 #include "platform_api.h"
 #include <platform.h>
+#include <math.h>
 
 typedef enum {
     ROOT,
@@ -32,6 +33,7 @@
     BACKEND_NAME,
     CONFIG_PARAMS,
     OPERATOR_SPECIFIC,
+    GAIN_LEVEL_MAPPING,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -42,6 +44,7 @@
 static void process_config_params(const XML_Char **attr);
 static void process_root(const XML_Char **attr);
 static void process_operator_specific(const XML_Char **attr);
+static void process_gain_db_to_level_map(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
     [ROOT] = process_root,
@@ -50,6 +53,7 @@
     [BACKEND_NAME] = process_backend_name,
     [CONFIG_PARAMS] = process_config_params,
     [OPERATOR_SPECIFIC] = process_operator_specific,
+    [GAIN_LEVEL_MAPPING] = process_gain_db_to_level_map,
 };
 
 static section_t section;
@@ -191,6 +195,29 @@
     return;
 }
 
+static void process_gain_db_to_level_map(const XML_Char **attr)
+{
+    struct amp_db_and_gain_table tbl_entry;
+
+    if ((strcmp(attr[0], "db") != 0) ||
+        (strcmp(attr[2], "level") != 0)) {
+        ALOGE("%s: invalid attribute passed  %s %sexpected amp db level",
+               __func__, attr[0], attr[2]);
+        goto done;
+    }
+
+    tbl_entry.db = atof(attr[1]);
+    tbl_entry.amp = exp(tbl_entry.db * 0.115129f);
+    tbl_entry.level = atoi(attr[3]);
+
+    ALOGV("%s: amp [%f]  db [%f] level [%d]", __func__,
+           tbl_entry.amp, tbl_entry.db, tbl_entry.level);
+    platform_add_gain_level_mapping(&tbl_entry);
+
+done:
+    return;
+}
+
 static void process_acdb_id(const XML_Char **attr)
 {
     int index;
@@ -297,6 +324,8 @@
         section = CONFIG_PARAMS;
     } else if (strcmp(tag_name, "operator_specific") == 0) {
         section = OPERATOR_SPECIFIC;
+    } else if (strcmp(tag_name, "gain_db_to_level_mapping") == 0) {
+        section = GAIN_LEVEL_MAPPING;
     } else if (strcmp(tag_name, "device") == 0) {
         if ((section != ACDB) && (section != BACKEND_NAME) && (section != OPERATOR_SPECIFIC)) {
             ALOGE("device tag only supported for acdb/backend names");
@@ -322,6 +351,14 @@
 
         section_process_fn fn = section_table[section];
         fn(attr);
+    } else if (strcmp(tag_name, "gain_level_map") == 0) {
+        if (section != GAIN_LEVEL_MAPPING) {
+            ALOGE("usecase tag only supported with GAIN_LEVEL_MAPPING section");
+            return;
+        }
+
+        section_process_fn fn = section_table[GAIN_LEVEL_MAPPING];
+        fn(attr);
     }
 
     return;
@@ -339,6 +376,8 @@
         section = ROOT;
     } else if (strcmp(tag_name, "operator_specific") == 0) {
         section = ROOT;
+    } else if (strcmp(tag_name, "gain_db_to_level_mapping") == 0) {
+        section = ROOT;
     }
 }
 
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 1a8550c..59ee105 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -53,6 +53,7 @@
 LOCAL_MODULE:= libvolumelistener
 
 LOCAL_C_INCLUDES := \
+        hardware/qcom/audio/hal \
 	$(call include-path-for, audio-effects)
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
index 107a475..897dd8f 100644
--- a/post_proc/volume_listener.c
+++ b/post_proc/volume_listener.c
@@ -23,6 +23,7 @@
 #include <cutils/log.h>
 #include <hardware/audio_effect.h>
 #include <cutils/properties.h>
+#include <platform_api.h>
 
 #define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
 #define XSTR(x) STR(x)
@@ -44,6 +45,7 @@
 #define MAX_GAIN_LEVELS 5
 
 #define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration"
+#define AHAL_GAIN_GET_MAPPING_TABLE "audio_hw_get_gain_level_mapping"
 
 enum {
     VOL_LISTENER_STATE_UNINITIALIZED,
@@ -142,14 +144,10 @@
     "Qualcomm Technologies Inc",
 };
 
-struct amp_db_and_gain_table {
-    float amp;
-    float db;
-    uint32_t level;
-} amp_to_dBLevel_table;
+static int total_volume_cal_step = MAX_GAIN_LEVELS;
 
 // using gain level for non-drc volume curve
-static const struct amp_db_and_gain_table  volume_curve_gain_mapping_table[MAX_GAIN_LEVELS] =
+struct amp_db_and_gain_table  volume_curve_gain_mapping_table[MAX_VOLUME_CAL_STEPS] =
 {
     /* Level 0 in the calibration database contains default calibration */
     { 0.001774, -55, 5 },
@@ -157,6 +155,16 @@
     { 0.630957,  -4, 3 },
     { 0.794328,  -2, 2 },
     { 1.0,        0, 1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 },
+    { 0, 0, -1 }
 };
 
 static const effect_descriptor_t *descriptors[] = {
@@ -178,6 +186,8 @@
 /* HAL interface to send calibration */
 static bool (*send_gain_dep_cal)(int);
 
+static int (*get_custom_gain_table)(struct amp_db_and_gain_table *, int);
+
 /* if dumping allowed */
 static bool dumping_enabled = false;
 
@@ -249,12 +259,12 @@
             // send Gain dep cal level
             int gain_dep_cal_level = -1;
 
-            if (new_vol >= 1) { // max amplitude, use highest DRC level
-                gain_dep_cal_level = volume_curve_gain_mapping_table[MAX_GAIN_LEVELS - 1].level;
+            if (new_vol >= 1 && total_volume_cal_step > 0) { // max amplitude, use highest DRC level
+                gain_dep_cal_level = volume_curve_gain_mapping_table[total_volume_cal_step - 1].level;
             } else if (new_vol <= 0) {
                 gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
             } else {
-                for (max_level = 0; max_level + 1 < MAX_GAIN_LEVELS; max_level++) {
+                for (max_level = 0; max_level + 1 < total_volume_cal_step; max_level++) {
                     if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
                         new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
                         gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
@@ -570,13 +580,16 @@
 
 static void init_once()
 {
-    int i = 0;
+    int max_table_ent = 0;
     if (initialized) {
         ALOGV("%s : already init .. do nothing", __func__);
         return;
     }
 
     ALOGD("%s Called ", __func__);
+    send_gain_dep_cal = NULL;
+    get_custom_gain_table = NULL;
+
     pthread_mutex_init(&vol_listner_init_lock, NULL);
 
     // get hal function pointer
@@ -584,17 +597,47 @@
         void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
         if (hal_lib_pointer == NULL) {
             ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
-            send_gain_dep_cal = NULL;
         } else {
             ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
             send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
             if (send_gain_dep_cal == NULL) {
                 ALOGE("Couldnt able to get the function symbol");
             }
+            get_custom_gain_table = (int (*) (struct amp_db_and_gain_table *, int))dlsym(hal_lib_pointer, AHAL_GAIN_GET_MAPPING_TABLE);
+            if (get_custom_gain_table == NULL) {
+                ALOGE("Couldnt able to get the function AHAL_GAIN_GET_MAPPING_TABLE  symbol");
+            } else {
+                max_table_ent = get_custom_gain_table(volume_curve_gain_mapping_table, MAX_VOLUME_CAL_STEPS);
+                // if number of entries is 0 use default
+                // if number of entries > MAX_VOLUME_CAL_STEPS (this should never happen) then in this case
+                // use only default number of steps but this will result in unexpected behaviour
+
+                if (max_table_ent > 0 && max_table_ent <= MAX_VOLUME_CAL_STEPS) {
+                    if (max_table_ent < total_volume_cal_step) {
+                        for (int i = max_table_ent; i < total_volume_cal_step; i++ ) {
+                            volume_curve_gain_mapping_table[i].amp = 0;
+                            volume_curve_gain_mapping_table[i].db = 0;
+                            volume_curve_gain_mapping_table[i].level = -1;
+                        }
+                    }
+                    total_volume_cal_step = max_table_ent;
+                    ALOGD("%s: using custome volume table", __func__);
+                } else {
+                    ALOGD("%s: using default volume table", __func__);
+                }
+
+                if (dumping_enabled) {
+                    ALOGD("%s: dumping table here .. size of table received %d",
+                           __func__, max_table_ent);
+                    for (int i = 0; i < MAX_VOLUME_CAL_STEPS ; i++)
+                        ALOGD("[%d]  %f %f %d", i, volume_curve_gain_mapping_table[i].amp,
+                                                   volume_curve_gain_mapping_table[i].db,
+                                                   volume_curve_gain_mapping_table[i].level);
+                }
+            }
         }
     } else {
         ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
-        send_gain_dep_cal = NULL;
     }
 
     // check system property to see if dumping is required