hal: Added support for HFP

 - Added setparameter to be set from hfp app to enable
   and disable hfp session.
 - Implemeted start and stop hfp session which takes
   care of switching device and setting the session

Change-Id: Ie8697328ccbfee09d0d162f6fad01ddb552e4f83
diff --git a/hal/Android.mk b/hal/Android.mk
index 86f92d9..d8a4733 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -47,6 +47,10 @@
     LOCAL_CFLAGS += -DUSB_HEADSET_ENABLED
     LOCAL_SRC_FILES += audio_extn/usb.c
 endif
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_HFP)),true)
+    LOCAL_CFLAGS += -DHFP_ENABLED
+    LOCAL_SRC_FILES += audio_extn/hfp.c
+endif
 
 ifneq ($(strip $(AUDIO_FEATURE_DISABLED_SSR)),true)
     LOCAL_CFLAGS += -DSSR_ENABLED
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 2c7fbca..72e2f9c 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -55,7 +55,12 @@
 void audio_extn_fm_set_parameters(struct audio_device *adev,
                                    struct str_parms *parms);
 #endif
-
+#ifndef HFP_ENABLED
+void audio_extn_hfp_set_parameters(adev, parms) (0)
+#else
+void audio_extn_hfp_set_parameters(struct audio_device *adev,
+                                           struct str_parms *parms);
+#endif
 #ifndef SSR_ENABLED
 #define audio_extn_ssr_get_parameters(query, reply) (0)
 #else
@@ -219,6 +224,7 @@
    audio_extn_set_afe_proxy_parameters(parms);
    audio_extn_fm_set_parameters(adev, parms);
    audio_extn_listen_set_parameters(adev, parms);
+   audio_extn_hfp_set_parameters(adev, parms);
 }
 
 void audio_extn_get_parameters(const struct audio_device *adev,
diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c
new file mode 100644
index 0000000..73824bd
--- /dev/null
+++ b/hal/audio_extn/hfp.c
@@ -0,0 +1,231 @@
+/* hfp.c
+Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
+
+#define LOG_TAG "audio_hw_hfp"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <stdlib.h>
+#include <cutils/str_parms.h>
+
+#ifdef HFP_ENABLED
+#define AUDIO_PARAMETER_HFP_ENABLE      "hfp_enable"
+
+static int32_t audio_extn_start_hfp(struct audio_device *adev,
+                               struct str_parms *parms);
+
+static int32_t audio_extn_stop_hfp(struct audio_device *adev);
+
+struct hfp_module {
+    struct pcm *hfp_sco_rx;
+    struct pcm *hfp_sco_tx;
+    struct pcm *hfp_pcm_rx;
+    struct pcm *hfp_pcm_tx;
+    bool is_hfp_running;
+    int hfp_volume;
+};
+
+static struct hfp_module hfpmod = {
+    .hfp_sco_rx = NULL,
+    .hfp_sco_tx = NULL,
+    .hfp_pcm_rx = NULL,
+    .hfp_pcm_tx = NULL,
+    .hfp_volume = 0,
+    .is_hfp_running = 0,
+};
+static struct pcm_config pcm_config_hfp = {
+    .channels = 1,
+    .rate = 8000,
+    .period_size = 240,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+    int ret;
+    char value[32]={0};
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+           if(!strncmp(value,"true",sizeof(value)))
+               ret = audio_extn_start_hfp(adev,parms);
+           else
+               audio_extn_stop_hfp(adev);
+    }
+}
+
+static int32_t audio_extn_start_hfp(struct audio_device *adev,
+                               struct str_parms *parms)
+{
+    int32_t i, ret = 0;
+    struct audio_usecase *uc_info;
+    int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
+
+    ALOGD("%s: enter", __func__);
+
+    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    uc_info->id = USECASE_AUDIO_HFP_SCO;
+    uc_info->type = PCM_HFP_CALL;
+    uc_info->stream.out = adev->primary_output;
+    uc_info->devices = adev->primary_output->devices;
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &uc_info->list);
+
+    select_devices(adev, USECASE_AUDIO_HFP_SCO);
+
+    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+    pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
+    pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
+    if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
+        pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
+        ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
+        ret = -EIO;
+        goto exit;
+    }
+
+    ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+
+    ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
+          __func__, SOUND_CARD, pcm_dev_rx_id);
+    hfpmod.hfp_sco_rx = pcm_open(SOUND_CARD,
+                                  pcm_dev_asm_rx_id,
+                                  PCM_OUT, &pcm_config_hfp);
+    if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
+        ret = -EIO;
+        goto exit;
+    }
+    ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, SOUND_CARD, pcm_dev_tx_id);
+    hfpmod.hfp_pcm_rx = pcm_open(SOUND_CARD,
+                                   pcm_dev_rx_id,
+                                   PCM_OUT, &pcm_config_hfp);
+    if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
+        ret = -EIO;
+        goto exit;
+    }
+    hfpmod.hfp_sco_tx = pcm_open(SOUND_CARD,
+                                  pcm_dev_asm_tx_id,
+                                  PCM_IN, &pcm_config_hfp);
+    if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
+        ret = -EIO;
+        goto exit;
+    }
+    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, SOUND_CARD, pcm_dev_tx_id);
+    hfpmod.hfp_pcm_tx = pcm_open(SOUND_CARD,
+                                   pcm_dev_tx_id,
+                                   PCM_IN, &pcm_config_hfp);
+    if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
+        ret = -EIO;
+        goto exit;
+    }
+    pcm_start(hfpmod.hfp_sco_rx);
+    pcm_start(hfpmod.hfp_sco_tx);
+    pcm_start(hfpmod.hfp_pcm_rx);
+    pcm_start(hfpmod.hfp_pcm_tx);
+
+
+    hfpmod.is_hfp_running = true;
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return 0;
+
+exit:
+    audio_extn_stop_hfp(adev);
+    ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
+    return ret;
+}
+
+static int32_t audio_extn_stop_hfp(struct audio_device *adev)
+{
+    int32_t i, ret = 0;
+    struct audio_usecase *uc_info;
+
+    ALOGD("%s: enter", __func__);
+    hfpmod.is_hfp_running = false;
+
+    /* 1. Close the PCM devices */
+    if (hfpmod.hfp_sco_rx) {
+        pcm_close(hfpmod.hfp_sco_rx);
+        hfpmod.hfp_sco_rx = NULL;
+    }
+    if (hfpmod.hfp_sco_tx) {
+        pcm_close(hfpmod.hfp_sco_tx);
+        hfpmod.hfp_sco_tx = NULL;
+    }
+    if (hfpmod.hfp_pcm_rx) {
+        pcm_close(hfpmod.hfp_pcm_rx);
+        hfpmod.hfp_pcm_rx = NULL;
+    }
+    if (hfpmod.hfp_pcm_tx) {
+        pcm_close(hfpmod.hfp_pcm_tx);
+        hfpmod.hfp_pcm_tx = NULL;
+    }
+
+    uc_info = get_usecase_from_list(adev, USECASE_AUDIO_HFP_SCO);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, USECASE_AUDIO_HFP_SCO);
+        return -EINVAL;
+    }
+
+    /* 2. Get and set stream specific mixer controls */
+    disable_audio_route(adev, uc_info, true);
+
+    /* 3. Disable the rx and tx devices */
+    disable_snd_device(adev, uc_info->out_snd_device, false);
+    disable_snd_device(adev, uc_info->in_snd_device, true);
+
+    list_remove(&uc_info->list);
+    free(uc_info);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+#endif /*HFP_ENABLED*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index ee67703..920dbc2 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -109,6 +109,7 @@
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
     [USECASE_AUDIO_RECORD_FM_VIRTUAL] = "fm-virtual-record",
     [USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
+    [USECASE_AUDIO_HFP_SCO] = "hfp-sco",
     [USECASE_VOICE_CALL] = "voice-call",
     
     [USECASE_VOICE2_CALL] = "voice2-call",
@@ -593,7 +594,8 @@
     }
 
     if ((usecase->type == VOICE_CALL) ||
-        (usecase->type == VOIP_CALL)) {
+        (usecase->type == VOIP_CALL)  ||
+        (usecase->type == PCM_HFP_CALL)) {
         out_snd_device = platform_get_output_snd_device(adev->platform,
                                                         usecase->stream.out->devices);
         in_snd_device = platform_get_input_snd_device(adev->platform, usecase->stream.out->devices);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 6ff6f40..639032f 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -62,6 +62,9 @@
     /* FM usecase */
     USECASE_AUDIO_PLAYBACK_FM,
 
+    /* HFP Use case*/
+    USECASE_AUDIO_HFP_SCO,
+
     /* Capture usecases */
     USECASE_AUDIO_RECORD,
     USECASE_AUDIO_RECORD_COMPRESS,
@@ -179,7 +182,8 @@
     PCM_PLAYBACK,
     PCM_CAPTURE,
     VOICE_CALL,
-    VOIP_CALL
+    VOIP_CALL,
+    PCM_HFP_CALL
 } usecase_type_t;
 
 union stream_ptr {
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index a207c2f..da67145 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -109,6 +109,7 @@
     [USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE,
                                   MULTIMEDIA2_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_FM] = {FM_PLAYBACK_PCM_DEVICE, FM_CAPTURE_PCM_DEVICE},
+    [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX},
     [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE, VOICE_CALL_PCM_DEVICE},
     [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE},
     [USECASE_VOLTE_CALL] = {VOLTE_CALL_PCM_DEVICE, VOLTE_CALL_PCM_DEVICE},
@@ -989,6 +990,7 @@
             } else if (my_data->fluence_type == FLUENCE_NONE ||
                 my_data->fluence_in_voice_call == false) {
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
+                set_echo_reference(adev->mixer, "SLIM_RX");
             } else {
                 snd_device = SND_DEVICE_IN_VOICE_DMIC;
                 adev->acdb_settings |= DMIC_FLAG;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 7d80906..e1ae77b 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -170,6 +170,10 @@
 #define MULTIMEDIA2_PCM_DEVICE 1
 #define FM_PLAYBACK_PCM_DEVICE 5
 #define FM_CAPTURE_PCM_DEVICE  6
+#define HFP_PCM_RX 5
+#define HFP_SCO_RX 35
+#define HFP_ASM_RX_TX 36
+
 #define INCALL_MUSIC_UPLINK_PCM_DEVICE 1
 #define INCALL_MUSIC_UPLINK2_PCM_DEVICE 16
 #define SPKR_PROT_CALIB_RX_PCM_DEVICE 5