hal: Add support to set island cfg and power mode for voice usecase

Add new APIs to set/reset island cfg and power mode in rx and tx
port separately during voice call usecase.

Change-Id: I01dae7c1e16a4c995e49bb81a534371af5f886fd
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 979781e..2c90c62 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1291,6 +1291,31 @@
         snd_device = usecase->in_snd_device;
     else
         snd_device = usecase->out_snd_device;
+
+    /* disable island and power mode on supported device for voice call */
+    if (usecase->type == VOICE_CALL) {
+        if (usecase->in_snd_device != SND_DEVICE_NONE) {
+            if (platform_get_island_cfg_on_device(adev->platform, usecase->in_snd_device) &&
+                platform_get_power_mode_on_device(adev->platform, usecase->in_snd_device)) {
+                platform_set_island_cfg_on_device(adev, usecase->in_snd_device, false);
+                platform_set_power_mode_on_device(adev, usecase->in_snd_device, false);
+                platform_reset_island_power_status(adev->platform, usecase->in_snd_device);
+                ALOGD("%s: disable island cfg and power mode in voice tx path",
+                      __func__);
+            }
+        }
+        if (usecase->out_snd_device != SND_DEVICE_NONE) {
+            if (platform_get_island_cfg_on_device(adev->platform, usecase->out_snd_device) &&
+                platform_get_power_mode_on_device(adev->platform, usecase->out_snd_device)) {
+                platform_set_island_cfg_on_device(adev, usecase->out_snd_device, false);
+                platform_set_power_mode_on_device(adev, usecase->out_snd_device, false);
+                platform_reset_island_power_status(adev->platform, usecase->out_snd_device);
+                ALOGD("%s: disable island cfg and power mode in voice rx path",
+                       __func__);
+            }
+        }
+    }
+
     // we shouldn't truncate mixer_path
     ALOGW_IF(strlcpy(mixer_path, use_case_table[usecase->id], sizeof(mixer_path))
             >= sizeof(mixer_path), "%s: truncation on mixer path", __func__);
@@ -1385,6 +1410,14 @@
     } else {
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name);
 
+        /* enable island and power mode on supported device */
+        if (platform_get_island_cfg_on_device(adev->platform, snd_device) &&
+            platform_get_power_mode_on_device(adev->platform, snd_device)) {
+            platform_set_island_cfg_on_device(adev, snd_device, true);
+            platform_set_power_mode_on_device(adev, snd_device, true);
+            ALOGD("%s: enable island cfg and power mode on: %s",
+                   __func__, device_name);
+        }
 
         if ((SND_DEVICE_OUT_BT_A2DP == snd_device) &&
             (audio_extn_a2dp_start_playback() < 0)) {
@@ -1707,6 +1740,20 @@
          force_routing = true;
          force_restart_session = true;
     }
+
+    /*
+     * Island cfg and power mode config needs to set before AFE port start.
+     * Set force routing in case of voice device was enable before.
+     */
+    if (uc_info->type == VOICE_CALL &&
+        voice_extn_is_voice_power_mode_supported() &&
+        platform_check_and_update_island_power_status(adev->platform,
+                                             uc_info,
+                                             snd_device)) {
+        force_routing = true;
+        ALOGD("%s:becf: force routing %d for power mode supported device",
+               __func__, force_routing);
+    }
     ALOGD("%s:becf: force routing %d", __func__, force_routing);
 
     /* Disable all the usecases on the shared backend other than the
@@ -1851,6 +1898,22 @@
      */
     if (uc_info->type == PCM_CAPTURE)
         backend_check_cond = is_codec_backend_in_device_type(&uc_info->device_list);
+
+    /*
+     * Island cfg and power mode config needs to set before AFE port start.
+     * Set force routing in case of voice device was enable before.
+     */
+
+    if (uc_info->type == VOICE_CALL &&
+        voice_extn_is_voice_power_mode_supported() &&
+        platform_check_and_update_island_power_status(adev->platform,
+                                             uc_info,
+                                             snd_device)) {
+        force_routing = true;
+        ALOGD("%s:becf: force routing %d for power mode supported device",
+               __func__, force_routing);
+    }
+
     /*
      * This function is to make sure that all the active capture usecases
      * are always routed to the same input sound device.
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 6267200..aee2179 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -386,6 +386,8 @@
     bool is_multiple_sample_rate_combo_supported;
     struct listnode custom_mtmx_params_list;
     struct listnode custom_mtmx_in_params_list;
+    struct power_mode_cfg power_mode_cfg[SND_DEVICE_MAX];
+    struct island_cfg island_cfg[SND_DEVICE_MAX];
 };
 
 struct  spkr_device_chmap {
@@ -2224,6 +2226,11 @@
         operator_specific_device_table[dev] = NULL;
         external_specific_device_table[dev] = NULL;
         snd_device_delay_ms[dev] = 0;
+        /* Init island cfg and power mode */
+        my_data->island_cfg[dev].mixer_ctl = NULL;
+        my_data->power_mode_cfg[dev].mixer_ctl = NULL;
+        my_data->island_cfg[dev].enable = false;
+        my_data->power_mode_cfg[dev].enable = false;
     }
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
         backend_bit_width_table[dev] = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
@@ -9026,6 +9033,154 @@
    return backend_idx;
 }
 
+bool platform_get_power_mode_on_device(void *platform, snd_device_t snd_device) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    ALOGD("%s:power mode status on snd_device = (%s %d)", __func__,
+           platform_get_snd_device_name(snd_device),
+           my_data->power_mode_cfg[snd_device].enable);
+    return my_data->power_mode_cfg[snd_device].enable;
+
+}
+
+bool platform_get_island_cfg_on_device(void *platform, snd_device_t snd_device) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    ALOGD("%s:island cfg status on snd_device = (%s %d)", __func__,
+           platform_get_snd_device_name(snd_device),
+           my_data->island_cfg[snd_device].enable);
+    return my_data->island_cfg[snd_device].enable;
+}
+
+int platform_set_power_mode_on_device(struct audio_device* adev,
+                                      snd_device_t snd_device,
+                                      bool enable)
+{
+    int ret = 0;
+    struct  mixer_ctl *ctl;
+    struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer,
+                                my_data->power_mode_cfg[snd_device].mixer_ctl);
+
+    if (ctl) {
+        ALOGD("%s:set power mode to %s",
+               __func__, (enable == true) ? "true" : "false");
+        mixer_ctl_set_value(ctl, 0, (int)enable);
+    } else {
+        ALOGE("%s:Could not get ctl for power mode mixer", __func__);
+        ret = -EINVAL;
+        goto error;
+    }
+    return ret;
+
+error:
+    my_data->power_mode_cfg[snd_device].enable = false;
+    my_data->power_mode_cfg[snd_device].mixer_ctl = NULL;
+    return ret;
+}
+
+int platform_set_island_cfg_on_device(struct audio_device* adev,
+                                      snd_device_t snd_device,
+                                      bool enable)
+{
+    int ret = 0;
+    struct  mixer_ctl *ctl;
+    struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer,
+                                my_data->island_cfg[snd_device].mixer_ctl);
+
+    if (ctl) {
+        ALOGD("%s:set island cfg to %s",
+               __func__, (enable == true) ? "true" : "false");
+        mixer_ctl_set_value(ctl, 0, (int)enable);
+    } else {
+        ALOGE("%s:Could not get ctl for island cfg mixer", __func__);
+        return -EINVAL;
+        goto error;
+    }
+    return ret;
+
+error:
+    my_data->island_cfg[snd_device].enable = false;
+    my_data->island_cfg[snd_device].mixer_ctl = NULL;
+    return ret;
+}
+
+char * platform_update_power_mode_mixer_ctrl(snd_device_t snd_device)
+{
+    char mixer_ctl[MIXER_PATH_MAX_LENGTH];
+    char *power_mode_mixer_ctrl = NULL;
+    char * be_itf = hw_interface_table[snd_device];
+
+    if (be_itf != NULL) {
+        snprintf(mixer_ctl, sizeof(mixer_ctl),
+                 "%s Power Mode", be_itf);
+        power_mode_mixer_ctrl = strdup(mixer_ctl);
+        ALOGD("%s: power mode mixer ctrl %s\n",
+              __func__, power_mode_mixer_ctrl);
+    }
+
+    return power_mode_mixer_ctrl;
+}
+
+char * platform_update_island_cfg_mixer_ctrl(snd_device_t snd_device)
+{
+    char mixer_ctl[MIXER_PATH_MAX_LENGTH];
+    char *island_cfg_mixer_ctrl = NULL;
+    char * be_itf = hw_interface_table[snd_device];
+
+    if (be_itf != NULL) {
+        snprintf(mixer_ctl, sizeof(mixer_ctl),
+                 "%s Island Config", be_itf);
+        island_cfg_mixer_ctrl = strdup(mixer_ctl);
+        ALOGD("%s: island cfg mixer ctrl %s\n",
+              __func__, island_cfg_mixer_ctrl);
+    }
+
+    return island_cfg_mixer_ctrl;
+}
+
+bool platform_check_and_update_island_power_status(void *platform,
+                                          struct audio_usecase* usecase,
+                                          snd_device_t snd_device)
+{
+    bool ret = false;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (compare_device_type(&usecase->device_list, AUDIO_DEVICE_OUT_EARPIECE) ||
+        compare_device_type(&usecase->device_list, AUDIO_DEVICE_OUT_WIRED_HEADSET) ||
+        compare_device_type(&usecase->device_list, AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) {
+        if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
+            /* update island and power mode in current device */
+            my_data->island_cfg[snd_device].mixer_ctl =
+                            platform_update_island_cfg_mixer_ctrl(snd_device);
+            my_data->power_mode_cfg[snd_device].mixer_ctl =
+                            platform_update_power_mode_mixer_ctrl(snd_device);
+            if (my_data->island_cfg[snd_device].mixer_ctl != NULL &&
+                my_data->power_mode_cfg[snd_device].mixer_ctl != NULL) {
+                /* enable island and power mode in current device */
+                my_data->island_cfg[snd_device].enable = true;
+                my_data->power_mode_cfg[snd_device].enable = true;
+                ret = true;
+            }
+        }
+    }
+
+    return ret;
+}
+
+void platform_reset_island_power_status(void *platform, snd_device_t snd_device)
+{
+     struct platform_data *my_data = (struct platform_data *)platform;
+
+     my_data->island_cfg[snd_device].mixer_ctl = NULL;
+     my_data->power_mode_cfg[snd_device].mixer_ctl = NULL;
+     my_data->island_cfg[snd_device].enable = false;
+     my_data->power_mode_cfg[snd_device].enable = false;
+}
+
 /*
  * configures afe with bit width and Sample Rate
  */
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 9a50946..c3efd3c 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -423,5 +423,14 @@
 void platform_set_audio_source_delay(audio_source_t audio_source, int delay_ms);
 
 int platform_get_audio_source_index(const char *audio_source_name);
-
+bool platform_check_and_update_island_power_status(void *platform,
+                                                   struct audio_usecase* usecase,
+                                                    snd_device_t snd_device);
+bool platform_get_power_mode_on_device(void *platform, snd_device_t snd_device);
+bool platform_get_island_cfg_on_device(void *platform, snd_device_t snd_device);
+int platform_set_power_mode_on_device(struct audio_device* adev, snd_device_t snd_device,
+                                      bool enable);
+int platform_set_island_cfg_on_device(struct audio_device* adev, snd_device_t snd_device,
+                                      bool enable);
+void platform_reset_island_power_status(void *platform, snd_device_t snd_device);
 #endif // AUDIO_PLATFORM_API_H
diff --git a/hal/voice.h b/hal/voice.h
index 94782b8..1f0978a 100644
--- a/hal/voice.h
+++ b/hal/voice.h
@@ -63,6 +63,16 @@
     bool in_call;
 };
 
+struct power_mode_cfg {
+   bool enable;
+   char *mixer_ctl;
+};
+
+struct island_cfg {
+   bool enable;
+   char *mixer_ctl;
+};
+
 enum {
     INCALL_REC_NONE = -1,
     INCALL_REC_UPLINK,
diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c
index c2e419a..1882f11 100644
--- a/hal/voice_extn/voice_extn.c
+++ b/hal/voice_extn/voice_extn.c
@@ -88,6 +88,7 @@
 static bool voice_extn_dynamic_ecns_feature_enabled = false;
 static bool voice_extn_incall_music_enabled = false;
 static bool voice_extn_multi_session_enabled = false;
+static bool voice_extn_power_mode_enabled = false;
 
 int voice_extn_is_call_state_active(struct audio_device *adev, bool *is_call_active);
 
@@ -456,6 +457,18 @@
     return voice_extn_multi_session_enabled;
 }
 
+void voice_power_mode_feature_init(bool is_feature_enabled)
+{
+    voice_extn_power_mode_enabled = is_feature_enabled;
+    ALOGV("%s:: ---- Feature POWER MODE is %s ----", __func__,
+                                is_feature_enabled ? "ENABLED" : "NOT ENABLED");
+}
+
+bool voice_extn_is_voice_power_mode_supported()
+{
+    return voice_extn_power_mode_enabled;
+}
+
 void voice_extn_feature_init()
 {
     // Register feature function here
@@ -472,6 +485,9 @@
     multi_voice_session_feature_init(
        property_get_bool("vendor.audio.feature.multi_voice_session.enable",
                           false));
+    voice_power_mode_feature_init(
+       property_get_bool("vendor.audio.feature.power_mode.enable",
+                          false));
 }
 
 void voice_extn_init(struct audio_device *adev)
diff --git a/hal/voice_extn/voice_extn.h b/hal/voice_extn/voice_extn.h
index b2aab7e..09d5414 100644
--- a/hal/voice_extn/voice_extn.h
+++ b/hal/voice_extn/voice_extn.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2020, The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -86,6 +86,8 @@
 bool voice_extn_is_compress_voip_supported();
 void multi_voice_session_feature_init(bool is_feature_enabled);
 bool voice_extn_is_multi_session_supported();
+void voice_power_mode_feature_init(bool is_feature_enabled);
+bool voice_extn_is_voice_power_mode_supported();
 
 
 #endif //VOICE_EXTN_H