Merge tag 'android-7.1.2_r5' of https://android.googlesource.com/platform/hardware/qcom/audio into HEAD
Android 7.1.2 Release 5 (N2G47J)
Change-Id: Ic8c1180a568dde687a79e580eb3db59f08d6061a
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 5ce3093..f53c9f2 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -57,6 +57,8 @@
#define audio_extn_hfp_is_active(adev) (0)
#define audio_extn_hfp_get_usecase() (-1)
#define audio_extn_hfp_set_parameters(adev, params) (0)
+#define audio_extn_hfp_set_mic_mute(adev, state) (0)
+
#else
bool audio_extn_hfp_is_active(struct audio_device *adev);
@@ -64,6 +66,8 @@
void audio_extn_hfp_set_parameters(struct audio_device *adev,
struct str_parms *parms);
+int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state);
+
#endif
#ifndef SOUND_TRIGGER_ENABLED
@@ -105,6 +109,9 @@
bool benable);
#endif
+void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer);
+int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
+ struct audio_usecase *usecase);
#ifndef HWDEP_CAL_ENABLED
#define audio_extn_hwdep_cal_send(snd_card, acdb_handle) (0)
#else
diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c
index ebc0fd2..07d5469 100644
--- a/hal/audio_extn/hfp.c
+++ b/hal/audio_extn/hfp.c
@@ -27,6 +27,7 @@
#include "platform_api.h"
#include <stdlib.h>
#include <cutils/str_parms.h>
+#include "audio_extn/tfa_98xx.h"
#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable"
#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
@@ -34,6 +35,10 @@
#define AUDIO_PARAMETER_HFP_VOL_MIXER_CTL "hfp_vol_mixer_ctl"
#define AUDIO_PARAMATER_HFP_VALUE_MAX 128
+#define AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME "hfp_mic_volume"
+#define PLAYBACK_VOLUME_MAX 0x2000
+#define CAPTURE_VOLUME_DEFAULT (15.0)
+
static int32_t start_hfp(struct audio_device *adev,
struct str_parms *parms);
@@ -45,8 +50,10 @@
struct pcm *hfp_pcm_rx;
struct pcm *hfp_pcm_tx;
float hfp_volume;
+ float mic_volume;
char hfp_vol_mixer_ctl[AUDIO_PARAMATER_HFP_VALUE_MAX];
bool is_hfp_running;
+ bool mic_mute;
audio_usecase_t ucid;
};
@@ -56,8 +63,10 @@
.hfp_pcm_rx = NULL,
.hfp_pcm_tx = NULL,
.hfp_volume = 0,
+ .mic_volume = CAPTURE_VOLUME_DEFAULT,
.hfp_vol_mixer_ctl = {0, },
.is_hfp_running = 0,
+ .mic_mute = 0,
.ucid = USECASE_AUDIO_HFP_SCO,
};
static struct pcm_config pcm_config_hfp = {
@@ -79,6 +88,9 @@
ALOGV("%s: entry", __func__);
ALOGD("%s: (%f)\n", __func__, value);
+ hfpmod.hfp_volume = value;
+ audio_extn_tfa_98xx_set_voice_vol(value);
+
if (value < 0.0) {
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
value = 0.0;
@@ -87,7 +99,6 @@
ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
}
vol = lrint((value * 0x2000) + 0.5);
- hfpmod.hfp_volume = value;
if (!hfpmod.is_hfp_running) {
ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
@@ -119,6 +130,115 @@
return ret;
}
+
+/*Set mic volume to value.
+*
+* This interface is used for mic volume control, set mic volume as value(range 0 ~ 15).
+*/
+static int hfp_set_mic_volume(struct audio_device *adev, float value)
+{
+ int volume, ret = 0;
+ char mixer_ctl_name[128];
+ struct mixer_ctl *ctl;
+ int pcm_device_id = HFP_ASM_RX_TX;
+
+ if (!hfpmod.is_hfp_running) {
+ ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
+ return -EIO;
+ }
+
+ if (value < 0.0) {
+ ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
+ value = 0.0;
+ } else if (value > CAPTURE_VOLUME_DEFAULT) {
+ value = CAPTURE_VOLUME_DEFAULT;
+ ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
+ }
+
+ value = value / CAPTURE_VOLUME_DEFAULT;
+ memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Playback %d Volume", pcm_device_id);
+ 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;
+ }
+ volume = (int)(value * PLAYBACK_VOLUME_MAX);
+
+ ALOGD("%s: Setting volume to %d (%s)\n", __func__, volume, mixer_ctl_name);
+ if (mixer_ctl_set_value(ctl, 0, volume) < 0) {
+ ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static float hfp_get_mic_volume(struct audio_device *adev)
+{
+ int volume, ret = 0;
+ char mixer_ctl_name[128];
+ struct mixer_ctl *ctl;
+ int pcm_device_id = HFP_ASM_RX_TX;
+ float value = 0.0;
+
+ if (!hfpmod.is_hfp_running) {
+ ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
+ return -EIO;
+ }
+
+ memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Playback %d Volume", pcm_device_id);
+ 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;
+ }
+
+ volume = mixer_ctl_get_value(ctl, 0);
+ if ( volume < 0) {
+ ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
+ return -EINVAL;
+ }
+ ALOGD("%s: getting mic volume %d \n", __func__, volume);
+
+ value = (volume / PLAYBACK_VOLUME_MAX) * CAPTURE_VOLUME_DEFAULT;
+ if (value < 0.0) {
+ ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
+ value = 0.0;
+ } else if (value > CAPTURE_VOLUME_DEFAULT) {
+ value = CAPTURE_VOLUME_DEFAULT;
+ ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
+ }
+
+ return value;
+}
+
+/*Set mic mute state.
+*
+* This interface is used for mic mute state control
+*/
+int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state)
+{
+ int rc = 0;
+
+ if (state == hfpmod.mic_mute)
+ return rc;
+
+ if (state == true) {
+ hfpmod.mic_volume = hfp_get_mic_volume(adev);
+ }
+ rc = hfp_set_mic_volume(adev, (state == true) ? 0.0 : hfpmod.mic_volume);
+ adev->voice.mic_mute = state;
+ hfpmod.mic_mute = state;
+ ALOGD("%s: Setting mute state %d, rc %d\n", __func__, state, rc);
+ return rc;
+}
+
static int32_t start_hfp(struct audio_device *adev,
struct str_parms *parms __unused)
{
@@ -140,6 +260,8 @@
list_add_tail(&adev->usecase_list, &uc_info->list);
+ audio_extn_tfa_98xx_set_mode_bt();
+
select_devices(adev, hfpmod.ucid);
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
@@ -169,13 +291,16 @@
}
ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
__func__, adev->snd_card, pcm_dev_tx_id);
- hfpmod.hfp_pcm_rx = pcm_open(adev->snd_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;
+
+ if (audio_extn_tfa_98xx_is_supported() == false) {
+ hfpmod.hfp_pcm_rx = pcm_open(adev->snd_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(adev->snd_card,
pcm_dev_asm_tx_id,
@@ -187,22 +312,33 @@
}
ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
__func__, adev->snd_card, pcm_dev_tx_id);
- hfpmod.hfp_pcm_tx = pcm_open(adev->snd_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;
+
+ if (audio_extn_tfa_98xx_is_supported() == false) {
+ hfpmod.hfp_pcm_tx = pcm_open(adev->snd_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);
+ if (audio_extn_tfa_98xx_is_supported() == false) {
+ pcm_start(hfpmod.hfp_pcm_rx);
+ pcm_start(hfpmod.hfp_pcm_tx);
+ }
+
+ audio_extn_tfa_98xx_enable_speaker();
hfpmod.is_hfp_running = true;
hfp_set_volume(adev, hfpmod.hfp_volume);
+ /* Set mic volume by mute status, we don't provide set mic volume in phone app, only
+ provide mute and unmute. */
+ audio_extn_hfp_set_mic_mute(adev, adev->mic_muted);
+
ALOGD("%s: exit: status(%d)", __func__, ret);
return 0;
@@ -348,6 +484,20 @@
ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
hfp_set_volume(adev, vol);
}
+
+ memset(value, 0, sizeof(value));
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME,
+ value, sizeof(value));
+ if (ret >= 0) {
+ if (sscanf(value, "%f", &vol) != 1){
+ ALOGE("%s: error in retrieving hfp mic volume", __func__);
+ ret = -EIO;
+ goto exit;
+ }
+ ALOGD("%s: set_hfp_mic_volume usecase, Vol: [%f]", __func__, vol);
+ hfp_set_mic_volume(adev, vol);
+ }
+
exit:
ALOGV("%s Exit",__func__);
}
diff --git a/hal/audio_extn/tfa_98xx.c b/hal/audio_extn/tfa_98xx.c
new file mode 100644
index 0000000..41523b5
--- /dev/null
+++ b/hal/audio_extn/tfa_98xx.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2013-2016 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 "tfa_98xx"
+/*#define LOG_NDEBUG 0*/
+#include <cutils/log.h>
+
+#include <stdlib.h>
+#include <audio_hw.h>
+#include <dlfcn.h>
+#include "audio_extn.h"
+#include <platform.h>
+#include <math.h>
+
+#define LIB_SPEAKER_BUNDLE "/system/lib/libexTfa98xx.so"
+
+
+enum exTfa98xx_Audio_Mode
+{
+ Audio_Mode_None = -1,
+ Audio_Mode_Music_Normal,
+ Audio_Mode_Hfp_Client,
+ Audio_Mode_Voice,
+ Audio_Mode_Hs_Hfp,
+ Audio_Mode_Max
+};
+typedef enum exTfa98xx_Audio_Mode exTfa98xx_audio_mode_t;
+
+enum exTfa98xx_Func_Mode
+{
+ Func_Mode_None = -1,
+ Func_Mode_Speaker,
+ Func_Mode_BT
+};
+typedef enum exTfa98xx_Func_Mode exTfa98xx_func_mode_t;
+
+#define I2S_CLOCK_ENABLE 1
+#define I2S_CLOCK_DISABLE 0
+#define HFP_MAX_VOLUME (15.000000)
+#define TFA_98XX_HFP_VSETPS (5.0)
+
+exTfa98xx_audio_mode_t current_audio_mode = Audio_Mode_None;
+
+typedef int (*set_speaker_on_t)(exTfa98xx_audio_mode_t);
+typedef int (*set_speaker_off_t)(void);
+typedef int (*set_speaker_calibration_t)(int);
+typedef void (*set_speaker_volume_step_t)(int, int);
+
+
+struct speaker_data {
+ struct audio_device *adev;
+ void *speaker_bundle;
+ set_speaker_on_t set_speaker_on;
+ set_speaker_off_t set_speaker_off;
+ set_speaker_calibration_t set_speaker_calibration;
+ set_speaker_volume_step_t set_speaker_volume_step;
+ int ref_cnt[Audio_Mode_Max];
+ int route_cnt[Audio_Mode_Max];
+ bool update_ref_cnt;
+};
+
+struct speaker_data *tfa98xx_speaker_data = NULL;
+
+static struct speaker_data* open_speaker_bundle()
+{
+ struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
+
+ sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
+ if (sd->speaker_bundle == NULL) {
+ ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
+ goto error;
+ } else {
+ ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
+
+ sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
+ "exTfa98xx_speakeron");
+ if (sd->set_speaker_on == NULL) {
+ ALOGE("%s: dlsym error %s for exTfa98xx_speakeron", __func__,
+ dlerror());
+ goto error;
+ }
+ sd->set_speaker_off = (set_speaker_off_t)dlsym(sd->speaker_bundle,
+ "exTfa98xx_speakeroff");
+ if (sd->set_speaker_off == NULL) {
+ ALOGE("%s: dlsym error %s for exTfa98xx_speakeroff", __func__,
+ dlerror());
+ goto error;
+ }
+ sd->set_speaker_volume_step = (set_speaker_volume_step_t)dlsym(sd->speaker_bundle,
+ "exTfa98xx_setvolumestep");
+ if (sd->set_speaker_volume_step == NULL) {
+ ALOGE("%s: dlsym error %s for exTfa98xx_setvolumestep",
+ __func__, dlerror());
+ goto error;
+ }
+ sd->set_speaker_calibration = (set_speaker_calibration_t)dlsym(sd->speaker_bundle,
+ "exTfa98xx_calibration");
+ if (sd->set_speaker_calibration == NULL) {
+ ALOGE("%s: dlsym error %s for exTfa98xx_calibration",
+ __func__, dlerror());
+ goto error;
+ }
+ }
+ return sd;
+
+error:
+ free(sd);
+ return 0;
+}
+
+static void close_speaker_bundle(struct speaker_data *sd)
+{
+ if (sd != NULL) {
+ dlclose(sd->speaker_bundle);
+ free(sd);
+ sd = NULL;
+ }
+}
+
+static int adev_i2s_clock_operation(int enable, struct audio_device *adev, char *paths)
+{
+ int ret = -1;
+
+ ALOGD("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+ if(I2S_CLOCK_ENABLE == enable) {
+ ret = audio_route_apply_and_update_path(adev->audio_route, paths);
+ if(ret) {
+ ALOGE("%s: audio_route_apply_and_update_path return %d\n", __func__, ret);
+ return ret;
+ }
+ } else {
+ ret = audio_route_reset_and_update_path(adev->audio_route, paths);
+ if(ret) {
+ ALOGE("%s: audio_route_reset_and_update_path return %d\n", __func__, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int tfa_98xx_set_audio_mode(int enable, struct audio_device *adev, exTfa98xx_audio_mode_t audio_mode)
+{
+ char paths[32] = "init_smart_pa";
+
+ switch(audio_mode) {
+ case Audio_Mode_Music_Normal:
+ strcat(paths, " music");
+ break;
+ case Audio_Mode_Voice:
+ case Audio_Mode_Hfp_Client:
+ case Audio_Mode_Hs_Hfp:
+ strcat(paths, " voice");
+ break;
+ default:
+ ALOGE("%s: function %d not support!\n",__func__, audio_mode);
+ return -EINVAL;
+ }
+
+ ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+ adev_i2s_clock_operation(enable, adev, paths);
+ return 0;
+
+}
+
+static exTfa98xx_audio_mode_t tfa_98xx_get_audio_mode(struct speaker_data *data)
+{
+ exTfa98xx_audio_mode_t tfa_98xx_audio_mode = Audio_Mode_None;
+ struct listnode *node;
+ struct audio_usecase *usecase;
+ audio_mode_t mode = data->adev->mode;
+ int i = 0;
+
+ ALOGV("%s: enter\n", __func__);
+
+ for (i = 0; i < Audio_Mode_Max; i++)
+ data->route_cnt[i] = 0;
+
+ list_for_each(node, &data->adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+ tfa_98xx_audio_mode = Audio_Mode_Hs_Hfp;
+ data->route_cnt[tfa_98xx_audio_mode]++;
+ ALOGV("%s: audio_mode hs_hfp\n", __func__);
+ }
+ } else if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ if ((mode == AUDIO_MODE_IN_CALL) || audio_extn_hfp_is_active(data->adev)) {
+ if (audio_extn_hfp_is_active(data->adev)) {
+ if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+ tfa_98xx_audio_mode = Audio_Mode_Hfp_Client;
+ data->route_cnt[tfa_98xx_audio_mode]++;
+ ALOGV("%s: audio_mode hfp client\n", __func__);
+ }
+ } else {
+ if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+ tfa_98xx_audio_mode = Audio_Mode_Voice;
+ data->route_cnt[tfa_98xx_audio_mode]++;
+ ALOGV("%s: audio_mode voice\n", __func__);
+ }
+ }
+ } else {
+ if (data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+ tfa_98xx_audio_mode = Audio_Mode_Music_Normal;
+ data->route_cnt[tfa_98xx_audio_mode]++;
+ ALOGV("%s: tfa_98xx_audio_mode music\n", __func__);
+ }
+ }
+ } else {
+ ALOGE("%s: no device match \n", __func__);
+ }
+ }
+ ALOGV("%s: tfa_98xx_audio_mode %d exit\n", __func__, tfa_98xx_audio_mode);
+
+ return tfa_98xx_audio_mode;
+}
+
+static int tfa_98xx_set_func_mode(int enable, struct audio_device *adev, exTfa98xx_func_mode_t func_mode)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ char paths[32] = "init_smart_pa";
+
+ if (data) {
+ switch(func_mode) {
+ case Func_Mode_Speaker:
+ strcat(paths, " func_speaker");
+ break;
+ case Func_Mode_BT:
+ strcat(paths, " func_bt");
+ break;
+ default:
+ ALOGE("%s: function %d not support!\n",__func__, func_mode);
+ return -EINVAL;
+ }
+
+ ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+ adev_i2s_clock_operation(enable, adev, paths);
+ }
+ return 0;
+}
+
+static exTfa98xx_func_mode_t tfa_98xx_get_func_mode(exTfa98xx_audio_mode_t audio_mode)
+{
+ exTfa98xx_func_mode_t func_mode = Func_Mode_None;
+
+ switch(audio_mode) {
+ case Audio_Mode_Music_Normal:
+ case Audio_Mode_Voice:
+ ALOGV("%s: tfa_98xx_func_mode speaker \n", __func__);
+ func_mode = Func_Mode_Speaker;
+ break;
+ case Audio_Mode_Hfp_Client:
+ case Audio_Mode_Hs_Hfp:
+ ALOGV("%s: tfa_98xx_func_mode bt \n", __func__);
+ func_mode = Func_Mode_BT;
+ break;
+ default:
+ break;
+ }
+ return func_mode;
+}
+
+static void tfa_98xx_disable_speaker(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ int ret = 0;
+
+ ret = data->set_speaker_off();
+ if (ret) {
+ ALOGE("%s: exTfa98xx_speakeroff failed result = %d\n", __func__, ret);
+ goto on_error;
+ }
+
+ ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, data->adev, current_audio_mode);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_audio_mode disable failed return %d\n", __func__, ret);
+ goto on_error;
+ }
+ current_audio_mode = Audio_Mode_None;
+on_error:
+ return;
+
+}
+
+
+void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ int i = 0;
+ exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
+
+ ALOGV("%s: enter\n", __func__);
+
+ if (data) {
+ if ((current_audio_mode == Audio_Mode_None) || (snd_device > SND_DEVICE_OUT_END))
+ goto on_exit;
+
+ switch(snd_device) {
+ case SND_DEVICE_OUT_SPEAKER:
+ new_audio_mode = Audio_Mode_Music_Normal;
+ break;
+ case SND_DEVICE_OUT_VOICE_SPEAKER:
+ new_audio_mode = Audio_Mode_Voice;
+ break;
+ case SND_DEVICE_OUT_VOICE_SPEAKER_HFP:
+ new_audio_mode = Audio_Mode_Hfp_Client;
+ break;
+ case SND_DEVICE_OUT_BT_SCO:
+ new_audio_mode = Audio_Mode_Hs_Hfp;
+ break;
+ default:
+ break;
+ }
+
+ if ((new_audio_mode == Audio_Mode_None) || (data->ref_cnt[new_audio_mode] <= 0)) {
+ ALOGE("%s: device ref cnt is already 0", __func__);
+ goto on_exit;
+ }
+
+ data->ref_cnt[new_audio_mode]--;
+
+ for (i = 0; i < Audio_Mode_Max; i++) {
+ if (data->ref_cnt[i] > 0) {
+ ALOGD("%s: exTfa98xx_speaker still in use\n", __func__);
+ goto on_exit;
+ }
+ }
+
+ if (data->adev->enable_hfp)
+ data->set_speaker_volume_step(0, 0);
+
+ tfa_98xx_disable_speaker();
+ }
+
+ ALOGV("%s: exit\n", __func__);
+on_exit:
+ return;
+}
+
+int audio_extn_tfa_98xx_enable_speaker(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
+ int ret = 0;
+ int i = 0;
+
+ ALOGV("%s: enter\n", __func__);
+
+ if (data) {
+
+ new_audio_mode = tfa_98xx_get_audio_mode(data);
+ if ((new_audio_mode != Audio_Mode_None) && (data->ref_cnt[new_audio_mode] >= 1)) {
+ ALOGD("%s, mode %d already active!", __func__, new_audio_mode);
+ data->ref_cnt[new_audio_mode]++;
+ goto on_exit;
+ }
+
+ ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, data->adev, new_audio_mode);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_audio_mode enable failed return %d\n", __func__, ret);
+ goto on_exit;
+ }
+
+ ret = data->set_speaker_on(new_audio_mode);
+ if (ret) {
+ ALOGE("%s: exTfa98xx_speakeron failed result = %d\n", __func__, ret);
+ goto on_exit;
+ }
+
+ current_audio_mode = new_audio_mode;
+ for (i = 0; i < Audio_Mode_Max; i++) {
+ data->ref_cnt[i] = data->route_cnt[i];
+ }
+ data->update_ref_cnt = false;
+ }
+
+ ALOGV("%s: exit\n", __func__);
+
+on_exit:
+ return ret;
+
+}
+
+void audio_extn_tfa_98xx_set_mode(void)
+{
+ int ret = 0;
+ struct speaker_data *data = tfa98xx_speaker_data;
+ exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
+ exTfa98xx_func_mode_t new_func_mode = Func_Mode_None;
+
+ ALOGV("%s: enter\n", __func__);
+
+ if (data) {
+ new_audio_mode = tfa_98xx_get_audio_mode(data);
+
+ new_func_mode = tfa_98xx_get_func_mode(new_audio_mode);
+ if (new_func_mode == Func_Mode_None)
+ return;
+
+ ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, new_func_mode);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
+ }
+ data->update_ref_cnt = true;
+ }
+
+ ALOGV("%s: exit\n", __func__);
+}
+
+void audio_extn_tfa_98xx_set_mode_bt(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ int ret = 0;
+
+ if (data) {
+ ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, Func_Mode_BT);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
+ }
+ }
+}
+
+void audio_extn_tfa_98xx_update(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
+
+ ALOGD("%s: enter\n", __func__);
+
+ if (data) {
+
+ new_audio_mode = tfa_98xx_get_audio_mode(data);
+ if (new_audio_mode <= current_audio_mode) {
+ ALOGE("%s: audio_extn_tfa_98xx_update same mode\n", __func__);
+ if (data->update_ref_cnt == true) {
+ data->ref_cnt[new_audio_mode]++;
+ data->update_ref_cnt = false;
+ }
+ goto on_error;
+ }
+
+ if (current_audio_mode != Audio_Mode_None) {
+ tfa_98xx_disable_speaker();
+ }
+
+ audio_extn_tfa_98xx_enable_speaker();
+
+ }
+
+ ALOGV("%s: exit\n", __func__);
+on_error:
+ return;
+
+}
+
+void audio_extn_tfa_98xx_set_voice_vol(float vol)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ int vsteps = 0;
+
+ if (data) {
+ if (data->adev->enable_hfp) {
+ if (vol < 0.0) {
+ vol = 0.0;
+ } else {
+ vol = ((vol > HFP_MAX_VOLUME) ? 1.0 : (vol / HFP_MAX_VOLUME));
+ }
+ vsteps = (int)floorf((1.0 - vol) * TFA_98XX_HFP_VSETPS);
+ } else {
+ return;
+ }
+ ALOGD("%s: vsteps %d\n", __func__, vsteps);
+ data->set_speaker_volume_step(vsteps, vsteps);
+ }
+}
+
+bool audio_extn_tfa_98xx_is_supported(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+ if (data)
+ return true;
+ else
+ return false;
+}
+
+int audio_extn_tfa_98xx_init(struct audio_device *adev)
+{
+ int ret = 0;
+ struct speaker_data *data = open_speaker_bundle();
+
+ ALOGV("%s: enter\n", __func__);
+
+ if (data) {
+ ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, adev, Audio_Mode_Music_Normal);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_audio_mode enable return %d\n", __func__, ret);
+ goto err_init;
+ }
+
+ ret = data->set_speaker_calibration(0);
+ if (ret) {
+ ALOGE("%s: exTfa98xx_calibration return %d\n", __func__, ret);
+ }
+
+ ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, adev, Audio_Mode_Music_Normal);
+ if (ret) {
+ ALOGE("%s: tfa_98xx_set_audio_mode disable return %d\n", __func__, ret);
+ goto err_init;
+ }
+
+ data->adev = adev;
+ tfa98xx_speaker_data = data;
+ ALOGV("%s: exit\n", __func__);
+ return 0;
+
+ }
+
+err_init:
+ close_speaker_bundle(data);
+ return -EINVAL;
+}
+
+void audio_extn_tfa_98xx_deinit(void)
+{
+ struct speaker_data *data = tfa98xx_speaker_data;
+
+ if (data) {
+ data->set_speaker_off();
+ close_speaker_bundle(data);
+ tfa98xx_speaker_data = NULL;
+ }
+}
+
diff --git a/hal/audio_extn/tfa_98xx.h b/hal/audio_extn/tfa_98xx.h
new file mode 100644
index 0000000..444429a
--- /dev/null
+++ b/hal/audio_extn/tfa_98xx.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013-2016 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 TFA_98XX_H
+#define TFA_98XX_H
+
+#ifdef SMART_PA_TFA_98XX_SUPPORTED
+int audio_extn_tfa_98xx_enable_speaker(void);
+void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device);
+void audio_extn_tfa_98xx_set_mode();
+void audio_extn_tfa_98xx_set_mode_bt(void);
+void audio_extn_tfa_98xx_update(void);
+void audio_extn_tfa_98xx_set_voice_vol(float vol);
+int audio_extn_tfa_98xx_init(struct audio_device *adev);
+void audio_extn_tfa_98xx_deinit(void);
+bool audio_extn_tfa_98xx_is_supported(void);
+#else
+#define audio_extn_tfa_98xx_enable_speaker(void) (0)
+#define audio_extn_tfa_98xx_disable_speaker(snd_device) (0)
+#define audio_extn_tfa_98xx_set_mode() (0)
+#define audio_extn_tfa_98xx_set_mode_bt() (0)
+#define audio_extn_tfa_98xx_update(void) (0)
+#define audio_extn_tfa_98xx_set_voice_vol(vol) (0)
+#define audio_extn_tfa_98xx_init(adev) (0)
+#define audio_extn_tfa_98xx_deinit(void) (0)
+#define audio_extn_tfa_98xx_is_supported(void) (false)
+#endif
+
+#endif /* TFA_98XX_H */
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
new file mode 100644
index 0000000..d2bb42c
--- /dev/null
+++ b/hal/audio_extn/utils.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 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_utils"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <cutils/properties.h>
+#include <cutils/config_utils.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+#include <cutils/misc.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "audio_extn.h"
+
+#define MAX_LENGTH_MIXER_CONTROL_IN_INT 128
+
+static int set_mixer_ctrl(struct audio_device *adev,
+ int pcm_device_id, int app_type,
+ int acdb_dev_id, int sample_rate, int stream_type)
+{
+
+ char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
+ struct mixer_ctl *ctl;
+ int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc = 0;
+
+ if (stream_type == PCM_PLAYBACK) {
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Audio Stream %d App Type Cfg", pcm_device_id);
+ } else if (stream_type == PCM_CAPTURE) {
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Audio Stream Capture %d App Type Cfg", pcm_device_id);
+ }
+
+ 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);
+ rc = -EINVAL;
+ goto exit;
+ }
+ app_type_cfg[len++] = app_type;
+ app_type_cfg[len++] = acdb_dev_id;
+ app_type_cfg[len++] = sample_rate;
+ ALOGV("%s: stream type %d app_type %d, acdb_dev_id %d sample rate %d",
+ __func__, stream_type, app_type, acdb_dev_id, sample_rate);
+ mixer_ctl_set_array(ctl, app_type_cfg, len);
+
+exit:
+ return rc;
+}
+
+void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer)
+{
+ int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
+ int length = 0, app_type = 0,rc = 0;
+ struct mixer_ctl *ctl = NULL;
+ const char *mixer_ctl_name = "App Type Config";
+
+ ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
+ return;
+ }
+ rc = platform_get_default_app_type_v2(platform, PCM_PLAYBACK, &app_type);
+ if (rc == 0) {
+ app_type_cfg[length++] = 1;
+ app_type_cfg[length++] = app_type;
+ app_type_cfg[length++] = 48000;
+ app_type_cfg[length++] = 16;
+ mixer_ctl_set_array(ctl, app_type_cfg, length);
+ }
+ return;
+}
+
+int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
+ struct audio_usecase *usecase)
+{
+ struct mixer_ctl *ctl;
+ int pcm_device_id, acdb_dev_id = 0, snd_device = usecase->out_snd_device;
+ int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+ int app_type = 0, rc = 0;
+
+ ALOGV("%s", __func__);
+
+ if (usecase->type != PCM_HFP_CALL) {
+ ALOGV("%s: not a playback or HFP path, no need to cfg app type", __func__);
+ rc = 0;
+ goto exit_send_app_type_cfg;
+ }
+ if ((usecase->id != USECASE_AUDIO_HFP_SCO) &&
+ (usecase->id != USECASE_AUDIO_HFP_SCO_WB)) {
+ ALOGV("%s: a playback path where app type cfg is not required", __func__);
+ rc = 0;
+ goto exit_send_app_type_cfg;
+ }
+
+ snd_device = usecase->out_snd_device;
+ pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
+
+ snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
+ audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
+ acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
+ if (acdb_dev_id < 0) {
+ ALOGE("%s: Couldn't get the acdb dev id", __func__);
+ rc = -EINVAL;
+ goto exit_send_app_type_cfg;
+ }
+
+ if (usecase->type == PCM_HFP_CALL) {
+
+ /* config HFP session:1 playback path */
+ rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
+ if (rc < 0)
+ goto exit_send_app_type_cfg;
+
+ sample_rate= CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ rc = set_mixer_ctrl(adev, pcm_device_id, app_type,
+ acdb_dev_id, sample_rate, PCM_PLAYBACK);
+ if (rc < 0)
+ goto exit_send_app_type_cfg;
+ /* config HFP session:1 capture path */
+ rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
+
+ if (rc == 0) {
+ rc = set_mixer_ctrl(adev, pcm_device_id, app_type,
+ acdb_dev_id, sample_rate, PCM_CAPTURE);
+ if (rc < 0)
+ goto exit_send_app_type_cfg;
+ }
+ /* config HFP session:2 capture path */
+ pcm_device_id = HFP_ASM_RX_TX;
+ snd_device = usecase->in_snd_device;
+ acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
+ if (acdb_dev_id <= 0) {
+ ALOGE("%s: Couldn't get the acdb dev id", __func__);
+ rc = -EINVAL;
+ goto exit_send_app_type_cfg;
+ }
+ rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
+ if (rc == 0) {
+ rc = set_mixer_ctrl(adev, pcm_device_id, app_type,
+ acdb_dev_id, sample_rate, PCM_CAPTURE);
+ if (rc < 0)
+ goto exit_send_app_type_cfg;
+ }
+
+ /* config HFP session:2 playback path */
+ rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
+ if (rc == 0) {
+ rc = set_mixer_ctrl(adev, pcm_device_id, app_type,
+ acdb_dev_id, sample_rate, PCM_PLAYBACK);
+ if (rc < 0)
+ goto exit_send_app_type_cfg;
+ }
+ }
+
+ rc = 0;
+exit_send_app_type_cfg:
+ return rc;
+}