hal: Support for anc/aanc/proxy audio features

Added support for  anc/aanc/proxy audio features

Change-Id: Id6ebdb9094635563cfafce9dc1d3449efe25bda4
diff --git a/hal/Android.mk b/hal/Android.mk
index 71e6d5a..649f6fe 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -19,6 +19,16 @@
 	audio_hw.c \
 	$(AUDIO_PLATFORM)/platform.c
 
+LOCAL_SRC_FILES += audio_extn/audio_extn.c
+
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_ANC_HEADSET)),true)
+    LOCAL_CFLAGS += -DANC_HEADSET_ENABLED
+endif
+
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_PROXY_DEVICE)),true)
+    LOCAL_CFLAGS += -DAFE_PROXY_ENABLED
+endif
+
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
 	libcutils \
@@ -31,7 +41,8 @@
 	external/tinyalsa/include \
 	$(call include-path-for, audio-route) \
 	$(call include-path-for, audio-effects) \
-	$(LOCAL_PATH)/$(AUDIO_PLATFORM)
+	$(LOCAL_PATH)/$(AUDIO_PLATFORM) \
+	$(LOCAL_PATH)/audio_extn
 
 LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
 
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
new file mode 100644
index 0000000..610e0d1
--- /dev/null
+++ b/hal/audio_extn/audio_extn.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_extn"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <stdlib.h>
+#include <errno.h>
+#include <cutils/properties.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "audio_extn.h"
+
+struct audio_extn_module {
+    bool anc_enabled;
+    bool aanc_enabled;
+    uint32_t proxy_channel_num;
+};
+
+static struct audio_extn_module aextnmod = {
+    .anc_enabled = 0,
+    .aanc_enabled = 0,
+    .proxy_channel_num = 2,
+};
+
+#define AUDIO_PARAMETER_KEY_ANC        "anc_enabled"
+#define AUDIO_PARAMETER_KEY_WFD        "wfd_channel_cap"
+#define AUDIO_PARAMETER_CAN_OPEN_PROXY "can_open_proxy"
+
+#ifndef ANC_HEADSET_ENABLED
+#define audio_extn_set_anc_parameters(parms)       (0)
+#else
+bool audio_extn_get_anc_enabled(void)
+{
+    ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled);
+    return (aextnmod.anc_enabled ? true: false);
+}
+
+bool audio_extn_should_use_handset_anc(int in_channels)
+{
+    char prop_aanc[128] = "false";
+
+    property_get("persist.aanc.enable", prop_aanc, "0");
+    if (!strncmp("true", prop_aanc, 4)) {
+        ALOGD("%s: AANC enabled in the property", __func__);
+        aextnmod.aanc_enabled = 1;
+    }
+
+    return (aextnmod.aanc_enabled && aextnmod.anc_enabled
+            && (in_channels == 1));
+}
+
+bool audio_extn_should_use_fb_anc(void)
+{
+  char prop_anc[128] = "feedforward";
+
+  property_get("persist.headset.anc.type", prop_anc, "0");
+  if (!strncmp("feedback", prop_anc, sizeof("feedback"))) {
+    ALOGD("%s: FB ANC headset type enabled\n", __func__);
+    return true;
+  }
+  return false;
+}
+
+void audio_extn_set_anc_parameters(struct str_parms *parms)
+{
+    int ret;
+    char value[32] ={0};
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ANC, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        if (strcmp(value, "true") == 0)
+            aextnmod.anc_enabled = true;
+        else
+            aextnmod.anc_enabled = false;
+    }
+
+    ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled);
+}
+#endif /* ANC_HEADSET_ENABLED */
+
+#ifndef AFE_PROXY_ENABLED
+#define audio_extn_set_afe_proxy_parameters(parms)        (0)
+#define audio_extn_get_afe_proxy_parameters(query, reply) (0)
+#else
+int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev)
+{
+    int32_t ret = 0;
+    const char *channel_cnt_str = NULL;
+    struct mixer_ctl *ctl = NULL;
+    const char *mixer_ctl_name = "PROXY_RX Channels";
+
+
+    ALOGD("%s: entry", __func__);
+    /* use the existing channel count set by hardware params to
+    configure the back end for stereo as usb/a2dp would be
+    stereo by default */
+    ALOGD("%s: channels = %d", __func__,
+           aextnmod.proxy_channel_num);
+    switch (aextnmod.proxy_channel_num) {
+    case 8: channel_cnt_str = "Eight"; break;
+    case 7: channel_cnt_str = "Seven"; break;
+    case 6: channel_cnt_str = "Six"; break;
+    case 5: channel_cnt_str = "Five"; break;
+    case 4: channel_cnt_str = "Four"; break;
+    case 3: channel_cnt_str = "Three"; break;
+    default: channel_cnt_str = "Two"; break;
+    }
+
+    if(aextnmod.proxy_channel_num >= 2 && aextnmod.proxy_channel_num < 8) {
+       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);
+        return -EINVAL;
+       }
+    }
+    mixer_ctl_set_enum_by_string(ctl, channel_cnt_str);
+
+    ALOGD("%s: exit", __func__);
+    return ret;
+}
+
+void audio_extn_set_afe_proxy_parameters(struct str_parms *parms)
+{
+    int ret, val;
+    char value[32]={0};
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_WFD, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        val = atoi(value);
+        aextnmod.proxy_channel_num = val;
+        ALOGD("%s: channel capability set to: %d", __func__,
+               aextnmod.proxy_channel_num);
+    }
+}
+
+char* audio_extn_get_afe_proxy_parameters(struct str_parms *query,
+                                          struct str_parms *reply)
+{
+    int ret, val;
+    char value[32]={0};
+    char *str = NULL;
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_CAN_OPEN_PROXY, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        /* Todo: check if proxy is free by maintaining a state flag*/
+        val = 1;
+        str_parms_add_int(reply, AUDIO_PARAMETER_CAN_OPEN_PROXY, val);
+        str = str_parms_to_str(reply);
+    }
+
+    return str;
+}
+#endif /* AFE_PROXY_ENABLED */
+
+void audio_extn_set_parameters(struct audio_device *adev,
+                               struct str_parms *parms)
+{
+   audio_extn_set_anc_parameters(parms);
+   audio_extn_set_afe_proxy_parameters(parms);
+}
+
+char* audio_extn_get_parameters(const struct audio_hw_device *dev,
+                               const char *keys)
+{
+    struct str_parms *query = str_parms_create_str(keys);
+    struct str_parms *reply = str_parms_create();
+    char *str = NULL;
+
+    ALOGD("%s: enter: keys - %s", __func__, keys);
+
+    if (NULL != (str = audio_extn_get_afe_proxy_parameters(query, reply)))
+    {
+        ALOGD("%s: handled %s", __func__, str);
+        goto exit;
+    } else {
+        str = strdup(keys);
+    }
+
+exit:
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+
+    ALOGD("%s: exit: returns %s", __func__, str);
+    return str;
+}
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
new file mode 100644
index 0000000..c537b77
--- /dev/null
+++ b/hal/audio_extn/audio_extn.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_EXTN_H
+#define AUDIO_EXTN_H
+
+#include <cutils/str_parms.h>
+
+void audio_extn_set_parameters(struct audio_device *adev,
+                               struct str_parms *parms);
+
+char* audio_extn_get_parameters(const struct audio_hw_device *dev,
+                               const char *keys);
+
+#ifndef ANC_HEADSET_ENABLED
+#define audio_extn_get_anc_enabled()                     (0)
+#define audio_extn_should_use_fb_anc()                   (0)
+#define audio_extn_should_use_handset_anc(in_channels)   (0)
+#else
+bool audio_extn_get_anc_enabled(void);
+bool audio_extn_should_use_fb_anc(void);
+bool audio_extn_should_use_handset_anc(int in_channels);
+#endif
+
+#ifndef AFE_PROXY_ENABLED
+#define audio_extn_set_afe_proxy_channel_mixer(adev)     (0)
+#else
+int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev);
+#endif
+
+#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 5e966d9..1f2cdbb 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1,4 +1,7 @@
 /*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
  * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,6 +38,7 @@
 #include "audio_hw.h"
 #include "platform_api.h"
 #include <platform.h>
+#include "audio_extn.h"
 
 struct pcm_config pcm_config_deep_buffer = {
     .channels = 2,
@@ -1508,6 +1512,7 @@
         pthread_mutex_unlock(&adev->lock);
     }
 
+    audio_extn_set_parameters(adev, parms);
     str_parms_destroy(parms);
     ALOGV("%s: exit with code(%d)", __func__, ret);
     return ret;
@@ -1516,7 +1521,7 @@
 static char* adev_get_parameters(const struct audio_hw_device *dev,
                                  const char *keys)
 {
-    return strdup("");
+    return audio_extn_get_parameters(dev, keys);
 }
 
 static int adev_init_check(const struct audio_hw_device *dev)
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 3d13c7d..85c57fc 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -1,4 +1,7 @@
 /*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
  * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,6 +28,7 @@
 #include <audio_hw.h>
 #include <platform_api.h>
 #include "platform.h"
+#include "audio_extn.h"
 
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
 #define LIB_ACDB_LOADER "libacdbloader.so"
@@ -50,7 +54,7 @@
 #define MAX_VOL_INDEX 5
 #define MIN_VOL_INDEX 0
 #define percent_to_index(val, min, max) \
-	        ((val) * ((max) - (min)) * 0.01 + (min) + .5)
+                ((val) * ((max) - (min)) * 0.01 + (min) + .5)
 
 struct audio_block_header
 {
@@ -110,6 +114,12 @@
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
+    [SND_DEVICE_OUT_ANC_HEADSET] = "anc-headphones",
+    [SND_DEVICE_OUT_ANC_FB_HEADSET] = "anc-fb-headphones",
+    [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = "voice-anc-headphones",
+    [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = "voice-anc-fb-headphones",
+    [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = "speaker-and-anc-headphones",
+    [SND_DEVICE_OUT_ANC_HANDSET] = "anc-handset",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -132,6 +142,7 @@
     [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic",
     [SND_DEVICE_IN_VOICE_REC_DMIC] = "voice-rec-dmic-ef",
     [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
+    [SND_DEVICE_IN_AANC_HANDSET_MIC] = "aanc-handset-mic",
 };
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
@@ -152,6 +163,12 @@
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
+    [SND_DEVICE_OUT_ANC_HEADSET] = 26,
+    [SND_DEVICE_OUT_ANC_FB_HEADSET] = 26,
+    [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = 26,
+    [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 26,
+    [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26,
+    [SND_DEVICE_OUT_ANC_HANDSET] = 103,
 
     [SND_DEVICE_IN_HANDSET_MIC] = 4,
     [SND_DEVICE_IN_SPEAKER_MIC] = 4, /* ToDo: Check if this needs to changed to 11 */
@@ -171,6 +188,7 @@
     [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36,
     [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16,
     [SND_DEVICE_IN_VOICE_REC_MIC] = 62,
+    [SND_DEVICE_IN_AANC_HANDSET_MIC] = 104,
     /* TODO: Update with proper acdb ids */
     [SND_DEVICE_IN_VOICE_REC_DMIC] = 62,
     [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 6,
@@ -490,6 +508,10 @@
     audio_mode_t mode = adev->mode;
     snd_device_t snd_device = SND_DEVICE_NONE;
 
+    audio_channel_mask_t channel_mask = (adev->active_input == NULL) ?
+                                AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask;
+    int channel_count = popcount(channel_mask);
+
     ALOGV("%s: enter: output devices(%#x)", __func__, devices);
     if (devices == AUDIO_DEVICE_NONE ||
         devices & AUDIO_DEVICE_BIT_IN) {
@@ -497,6 +519,11 @@
         goto exit;
     }
 
+    if(devices & AUDIO_DEVICE_OUT_PROXY) {
+         ALOGD("%s: setting sink capability for Proxy", __func__);
+         audio_extn_set_afe_proxy_channel_mixer(adev);
+    }
+
     if (mode == AUDIO_MODE_IN_CALL) {
         if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
             devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
@@ -506,6 +533,12 @@
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES;
             else if (adev->tty_mode == TTY_MODE_HCO)
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET;
+            else if (audio_extn_get_anc_enabled()) {
+                if (audio_extn_should_use_fb_anc())
+                    snd_device = SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET;
+                else
+                    snd_device = SND_DEVICE_OUT_VOICE_ANC_HEADSET;
+           }
             else
                 snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
         } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
@@ -515,6 +548,8 @@
         } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
             if (is_operator_tmus())
                 snd_device = SND_DEVICE_OUT_VOICE_HANDSET_TMUS;
+            else if (audio_extn_should_use_handset_anc(channel_count))
+                snd_device = SND_DEVICE_OUT_ANC_HANDSET;
             else
                 snd_device = SND_DEVICE_OUT_VOICE_HANDSET;
         }
@@ -529,7 +564,10 @@
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
         } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+            if (audio_extn_get_anc_enabled())
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET;
+            else
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
         } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
@@ -549,7 +587,15 @@
 
     if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
         devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-        snd_device = SND_DEVICE_OUT_HEADPHONES;
+        if (devices & AUDIO_DEVICE_OUT_WIRED_HEADSET
+            && audio_extn_get_anc_enabled()) {
+            if (audio_extn_should_use_fb_anc())
+                snd_device = SND_DEVICE_OUT_ANC_FB_HEADSET;
+            else
+                snd_device = SND_DEVICE_OUT_ANC_HEADSET;
+        }
+        else
+            snd_device = SND_DEVICE_OUT_HEADPHONES;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
         if (adev->speaker_lr_swap)
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
@@ -583,6 +629,7 @@
     audio_channel_mask_t channel_mask = (adev->active_input == NULL) ?
                                 AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask;
     snd_device_t snd_device = SND_DEVICE_NONE;
+    int channel_count = popcount(channel_mask);
 
     ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
           __func__, out_device, in_device);
@@ -612,7 +659,10 @@
         }
         if (out_device & AUDIO_DEVICE_OUT_EARPIECE ||
             out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
-            if (my_data->fluence_type == FLUENCE_NONE ||
+            if (out_device & AUDIO_DEVICE_OUT_EARPIECE &&
+                audio_extn_should_use_handset_anc(channel_count)) {
+                snd_device = SND_DEVICE_IN_AANC_HANDSET_MIC;
+            } else if (my_data->fluence_type == FLUENCE_NONE ||
                 my_data->fluence_in_voice_call == false) {
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
             } else {
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 26d4a9f..bbbb821 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -1,4 +1,7 @@
 /*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
  * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -57,6 +60,12 @@
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
+    SND_DEVICE_OUT_ANC_HEADSET,
+    SND_DEVICE_OUT_ANC_FB_HEADSET,
+    SND_DEVICE_OUT_VOICE_ANC_HEADSET,
+    SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+    SND_DEVICE_OUT_ANC_HANDSET,
     SND_DEVICE_OUT_END,
 
     /*
@@ -86,6 +95,7 @@
     SND_DEVICE_IN_VOICE_REC_MIC,
     SND_DEVICE_IN_VOICE_REC_DMIC,
     SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE,
+    SND_DEVICE_IN_AANC_HANDSET_MIC,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,