summaryrefslogtreecommitdiff
path: root/audio/hal/voice_extn/compress_voip.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/hal/voice_extn/compress_voip.c')
-rw-r--r--audio/hal/voice_extn/compress_voip.c811
1 files changed, 811 insertions, 0 deletions
diff --git a/audio/hal/voice_extn/compress_voip.c b/audio/hal/voice_extn/compress_voip.c
new file mode 100644
index 0000000..26636db
--- /dev/null
+++ b/audio/hal/voice_extn/compress_voip.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2013-2014, 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 "compress_voip"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <cutils/properties.h>
+
+#include "audio_hw.h"
+#include "platform_api.h"
+#include "platform.h"
+#include "voice_extn.h"
+
+#define COMPRESS_VOIP_IO_BUF_SIZE_NB 320
+#define COMPRESS_VOIP_IO_BUF_SIZE_WB 640
+
+struct pcm_config pcm_config_voip_nb = {
+ .channels = 1,
+ .rate = 8000, /* changed when the stream is opened */
+ .period_size = COMPRESS_VOIP_IO_BUF_SIZE_NB/2,
+ .period_count = 10,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+struct pcm_config pcm_config_voip_wb = {
+ .channels = 1,
+ .rate = 16000, /* changed when the stream is opened */
+ .period_size = COMPRESS_VOIP_IO_BUF_SIZE_WB/2,
+ .period_count = 10,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+struct voip_data {
+ struct pcm *pcm_rx;
+ struct pcm *pcm_tx;
+ struct stream_out *out_stream;
+ uint32_t out_stream_count;
+ uint32_t in_stream_count;
+ uint32_t sample_rate;
+};
+
+#define MODE_IS127 0x2
+#define MODE_4GV_NB 0x3
+#define MODE_4GV_WB 0x4
+#define MODE_AMR 0x5
+#define MODE_AMR_WB 0xD
+#define MODE_PCM 0xC
+#define MODE_4GV_NW 0xE
+
+#define AUDIO_PARAMETER_KEY_VOIP_RATE "voip_rate"
+#define AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN "evrc_rate_min"
+#define AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX "evrc_rate_max"
+#define AUDIO_PARAMETER_KEY_VOIP_DTX_MODE "dtx_on"
+#define AUDIO_PARAMETER_VALUE_VOIP_TRUE "true"
+#define AUDIO_PARAMETER_KEY_VOIP_CHECK "voip_flag"
+#define AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT "voip_out_stream_count"
+#define AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE "voip_sample_rate"
+
+static struct voip_data voip_data = {
+ .pcm_rx = NULL,
+ .pcm_tx = NULL,
+ .out_stream = NULL,
+ .out_stream_count = 0,
+ .in_stream_count = 0,
+ .sample_rate = 0
+};
+
+static int voip_set_volume(struct audio_device *adev, int volume);
+static int voip_set_mic_mute(struct audio_device *adev, bool state);
+static int voip_set_mode(struct audio_device *adev, int format);
+static int voip_set_rate(struct audio_device *adev, int rate);
+static int voip_set_evrc_min_max_rate(struct audio_device *adev, int min_rate,
+ int max_rate);
+static int voip_set_dtx(struct audio_device *adev, bool enable);
+static int voip_stop_call(struct audio_device *adev);
+static int voip_start_call(struct audio_device *adev,
+ struct pcm_config *voip_config);
+
+static int audio_format_to_voip_mode(int format)
+{
+ int mode;
+
+ switch(format) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ mode = MODE_PCM;
+ break;
+ case AUDIO_FORMAT_AMR_NB:
+ mode = MODE_AMR;
+ break;
+ case AUDIO_FORMAT_AMR_WB:
+ mode = MODE_AMR_WB;
+ break;
+ case AUDIO_FORMAT_EVRC:
+ mode = MODE_IS127;
+ break;
+ case AUDIO_FORMAT_EVRCB:
+ mode = MODE_4GV_NB;
+ break;
+ case AUDIO_FORMAT_EVRCWB:
+ mode = MODE_4GV_WB;
+ break;
+ case AUDIO_FORMAT_EVRCNW:
+ mode = MODE_4GV_NW;
+ break;
+ default:
+ mode = MODE_PCM;
+ }
+ return mode;
+}
+
+static int voip_set_volume(struct audio_device *adev, int volume)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Rx Gain";
+ int vol_index = 0;
+ uint32_t set_values[ ] = {0,
+ DEFAULT_VOLUME_RAMP_DURATION_MS};
+
+ ALOGV("%s: enter", __func__);
+
+ /* Voice volume levels are mapped to adsp volume levels as follows.
+ * 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1 0 -> 0
+ * But this values don't changed in kernel. So, below change is need.
+ */
+ vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX);
+ set_values[0] = vol_index;
+
+ 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;
+ }
+ ALOGV("%s: Setting voip volume index: %d", __func__, set_values[0]);
+ mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_set_mic_mute(struct audio_device *adev, bool state)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Tx Mute";
+ uint32_t set_values[ ] = {0,
+ DEFAULT_VOLUME_RAMP_DURATION_MS};
+
+ ALOGV("%s: enter, state=%d", __func__, state);
+
+ if (adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+ set_values[0] = state;
+ 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_array(ctl, set_values, ARRAY_SIZE(set_values));
+ }
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_set_mode(struct audio_device *adev, int format)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Mode Config";
+ uint32_t set_values[ ] = {0};
+ int mode;
+
+ ALOGD("%s: enter, format=%d", __func__, format);
+
+ mode = audio_format_to_voip_mode(format);
+ ALOGD("%s: Derived mode = %d", __func__, mode);
+
+ set_values[0] = mode;
+ 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_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_set_rate(struct audio_device *adev, int rate)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Rate Config";
+ uint32_t set_values[ ] = {0};
+
+ ALOGD("%s: enter, rate=%d", __func__, rate);
+
+ set_values[0] = rate;
+ 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_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_set_evrc_min_max_rate(struct audio_device *adev, int min_rate,
+ int max_rate)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Evrc Min Max Rate Config";
+ uint32_t set_values[ ] = {0, 0};
+
+ ALOGD("%s: enter, min_rate=%d, max_rate=%d",
+ __func__, min_rate, max_rate);
+
+ set_values[0] = min_rate;
+ set_values[1] = max_rate;
+ 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_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_set_dtx(struct audio_device *adev, bool enable)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "Voip Dtx Mode";
+ uint32_t set_values[ ] = {0};
+
+ ALOGD("%s: enter, enable=%d", __func__, enable);
+
+ set_values[0] = enable;
+ 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_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int voip_stop_call(struct audio_device *adev)
+{
+ int i, ret = 0;
+ struct audio_usecase *uc_info;
+
+ ALOGD("%s: enter, out_stream_count=%d, in_stream_count=%d",
+ __func__, voip_data.out_stream_count, voip_data.in_stream_count);
+
+ if (!voip_data.out_stream_count && !voip_data.in_stream_count) {
+ voip_data.sample_rate = 0;
+ uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__, USECASE_COMPRESS_VOIP_CALL);
+ return -EINVAL;
+ }
+
+ /* 1. Close the PCM devices */
+ if (voip_data.pcm_rx) {
+ pcm_close(voip_data.pcm_rx);
+ voip_data.pcm_rx = NULL;
+ }
+ if (voip_data.pcm_tx) {
+ pcm_close(voip_data.pcm_tx);
+ voip_data.pcm_tx = NULL;
+ }
+
+ /* 2. Get and set stream specific mixer controls */
+ disable_audio_route(adev, uc_info);
+
+ /* 3. Disable the rx and tx devices */
+ disable_snd_device(adev, uc_info->out_snd_device);
+ disable_snd_device(adev, uc_info->in_snd_device);
+
+ list_remove(&uc_info->list);
+ free(uc_info);
+ } else
+ ALOGV("%s: NO-OP because out_stream_count=%d, in_stream_count=%d",
+ __func__, voip_data.out_stream_count, voip_data.in_stream_count);
+
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+static int voip_start_call(struct audio_device *adev,
+ struct pcm_config *voip_config)
+{
+ int i, ret = 0;
+ struct audio_usecase *uc_info;
+ int pcm_dev_rx_id, pcm_dev_tx_id;
+
+ ALOGD("%s: enter", __func__);
+
+ uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL);
+ if (uc_info == NULL) {
+ ALOGV("%s: voip usecase is added to the list", __func__);
+ uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+
+ if (!uc_info) {
+ ALOGE("failed to allocate voip usecase mem");
+ return -ENOMEM;
+ }
+
+ uc_info->id = USECASE_COMPRESS_VOIP_CALL;
+ uc_info->type = VOIP_CALL;
+ if (voip_data.out_stream)
+ uc_info->stream.out = voip_data.out_stream;
+ else
+ uc_info->stream.out = adev->primary_output;
+ 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_COMPRESS_VOIP_CALL);
+
+ 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);
+
+ if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) {
+ ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
+ __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+ ret = -EIO;
+ goto error_start_voip;
+ }
+
+ ALOGD("%s: Opening PCM playback device card_id(%d) device_id(%d)",
+ __func__, adev->snd_card, pcm_dev_rx_id);
+ voip_data.pcm_rx = pcm_open(adev->snd_card,
+ pcm_dev_rx_id,
+ PCM_OUT, voip_config);
+ if (voip_data.pcm_rx && !pcm_is_ready(voip_data.pcm_rx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_rx));
+ pcm_close(voip_data.pcm_rx);
+ voip_data.pcm_rx = NULL;
+ ret = -EIO;
+ goto error_start_voip;
+ }
+
+ ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+ __func__, adev->snd_card, pcm_dev_tx_id);
+ voip_data.pcm_tx = pcm_open(adev->snd_card,
+ pcm_dev_tx_id,
+ PCM_IN, voip_config);
+ if (voip_data.pcm_tx && !pcm_is_ready(voip_data.pcm_tx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_tx));
+ pcm_close(voip_data.pcm_rx);
+ voip_data.pcm_tx = NULL;
+ if (voip_data.pcm_rx) {
+ pcm_close(voip_data.pcm_rx);
+ voip_data.pcm_rx = NULL;
+ }
+ ret = -EIO;
+ goto error_start_voip;
+ }
+ pcm_start(voip_data.pcm_rx);
+ pcm_start(voip_data.pcm_tx);
+
+ voice_extn_compress_voip_set_volume(adev, adev->voice.volume);
+
+ if (ret < 0) {
+ ALOGE("%s: error %d\n", __func__, ret);
+ goto error_start_voip;
+ }
+ } else {
+ ALOGV("%s: voip usecase is already enabled", __func__);
+ if (voip_data.out_stream)
+ uc_info->stream.out = voip_data.out_stream;
+ else
+ uc_info->stream.out = adev->primary_output;
+ select_devices(adev, USECASE_COMPRESS_VOIP_CALL);
+ }
+
+ return 0;
+
+error_start_voip:
+ voip_stop_call(adev);
+
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+int voice_extn_compress_voip_set_parameters(struct audio_device *adev,
+ struct str_parms *parms)
+{
+ char *str;
+ char value[32]={0};
+ int ret = 0, err, rate;
+ int min_rate, max_rate;
+ bool flag;
+ char *kv_pairs = str_parms_to_str(parms);
+
+ ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_RATE,
+ value, sizeof(value));
+ if (err >= 0) {
+ rate = atoi(value);
+ voip_set_rate(adev, rate);
+ voip_set_evrc_min_max_rate(adev, rate, rate);
+ }
+
+ memset(value, 0, sizeof(value));
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN,
+ value, sizeof(value));
+ if (err >= 0) {
+ min_rate = atoi(value);
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN);
+ memset(value, 0, sizeof(value));
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX,
+ value, sizeof(value));
+ if (err >= 0) {
+ max_rate = atoi(value);
+ voip_set_evrc_min_max_rate(adev, min_rate, max_rate);
+ } else {
+ ALOGE("%s: AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX not found", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ memset(value, 0, sizeof(value));
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_DTX_MODE,
+ value, sizeof(value));
+ if (err >= 0) {
+ flag = false;
+ if (strcmp(value, AUDIO_PARAMETER_VALUE_VOIP_TRUE) == 0)
+ flag = true;
+ voip_set_dtx(adev, flag);
+ }
+
+done:
+ ALOGV("%s: exit", __func__);
+ free(kv_pairs);
+ return ret;
+}
+
+void voice_extn_compress_voip_get_parameters(struct str_parms *query,
+ struct str_parms *reply)
+{
+ int ret;
+ char value[32]={0};
+ char *str = NULL;
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT,
+ value, sizeof(value));
+ if (ret >= 0) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT,
+ voip_data.out_stream_count);
+ }
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE,
+ value, sizeof(value));
+ if (ret >= 0) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE,
+ voip_data.sample_rate);
+ }
+}
+
+void voice_extn_compress_voip_out_get_parameters(struct stream_out *out,
+ struct str_parms *query,
+ struct str_parms *reply)
+{
+ int ret, val;
+ char value[32]={0};
+
+ ALOGD("%s: enter", __func__);
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value));
+
+ if (ret >= 0) {
+ if (out->usecase == USECASE_COMPRESS_VOIP_CALL)
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true);
+ else
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false);
+ }
+
+ ALOGV("%s: exit", __func__);
+}
+
+void voice_extn_compress_voip_in_get_parameters(struct stream_in *in,
+ struct str_parms *query,
+ struct str_parms *reply)
+{
+ int ret, val;
+ char value[32]={0};
+ char *kv_pairs = NULL;
+
+ ALOGV("%s: enter", __func__);
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value));
+
+ if (ret >= 0) {
+ if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true);
+ else
+ str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false);
+ }
+
+ kv_pairs = str_parms_to_str(reply);
+ ALOGD_IF(kv_pairs != NULL, "%s: exit: return - %s", __func__, kv_pairs);
+ free(kv_pairs);
+}
+
+int voice_extn_compress_voip_out_get_buffer_size(struct stream_out *out)
+{
+ if (out->config.rate == 16000)
+ return COMPRESS_VOIP_IO_BUF_SIZE_WB;
+ else
+ return COMPRESS_VOIP_IO_BUF_SIZE_NB;
+}
+
+int voice_extn_compress_voip_in_get_buffer_size(struct stream_in *in)
+{
+ if (in->config.rate == 16000)
+ return COMPRESS_VOIP_IO_BUF_SIZE_WB;
+ else
+ return COMPRESS_VOIP_IO_BUF_SIZE_NB;
+}
+
+int voice_extn_compress_voip_start_output_stream(struct stream_out *out)
+{
+ int ret = 0;
+ struct audio_device *adev = out->dev;
+ struct audio_usecase *uc_info;
+ int snd_card_status = get_snd_card_state(adev);
+
+ ALOGD("%s: enter", __func__);
+
+ if (SND_CARD_STATE_OFFLINE == snd_card_status) {
+ ret = -ENETRESET;
+ ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret);
+ goto error;
+ }
+
+ if (!voip_data.out_stream_count)
+ ret = voice_extn_compress_voip_open_output_stream(out);
+
+ ret = voip_start_call(adev, &out->config);
+ out->pcm = voip_data.pcm_rx;
+ uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL);
+ if (uc_info) {
+ uc_info->stream.out = out;
+ uc_info->devices = out->devices;
+ } else {
+ ret = -EINVAL;
+ ALOGE("%s: exit(%d): failed to get use case info", __func__, ret);
+ goto error;
+ }
+
+error:
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+int voice_extn_compress_voip_start_input_stream(struct stream_in *in)
+{
+ int ret = 0;
+ struct audio_usecase *uc_info;
+ struct audio_device *adev = in->dev;
+ int snd_card_status = get_snd_card_state(adev);
+
+ ALOGD("%s: enter", __func__);
+
+ if (SND_CARD_STATE_OFFLINE == snd_card_status) {
+ ret = -ENETRESET;
+ ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret);
+ goto error;
+ }
+
+ if (!voip_data.in_stream_count)
+ ret = voice_extn_compress_voip_open_input_stream(in);
+
+ adev->active_input = in;
+ ret = voip_start_call(adev, &in->config);
+ in->pcm = voip_data.pcm_tx;
+
+error:
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+int voice_extn_compress_voip_close_output_stream(struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ int ret = 0;
+
+ ALOGD("%s: enter", __func__);
+ if (voip_data.out_stream_count > 0) {
+ voip_data.out_stream_count--;
+ ret = voip_stop_call(adev);
+ voip_data.out_stream = NULL;
+ out->pcm = NULL;
+ }
+
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+int voice_extn_compress_voip_open_output_stream(struct stream_out *out)
+{
+ int mode, ret;
+
+ ALOGD("%s: enter", __func__);
+
+ out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO;
+ out->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+ out->usecase = USECASE_COMPRESS_VOIP_CALL;
+ if (out->sample_rate == 16000)
+ out->config = pcm_config_voip_wb;
+ else
+ out->config = pcm_config_voip_nb;
+
+ voip_data.out_stream = out;
+ voip_data.out_stream_count++;
+ voip_data.sample_rate = out->sample_rate;
+ ret = voip_set_mode(out->dev, out->format);
+
+ ALOGV("%s: exit", __func__);
+ return ret;
+}
+
+int voice_extn_compress_voip_close_input_stream(struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+ int status = 0;
+
+ ALOGD("%s: enter", __func__);
+
+ if(voip_data.in_stream_count > 0) {
+ adev->active_input = NULL;
+ voip_data.in_stream_count--;
+ status = voip_stop_call(adev);
+ in->pcm = NULL;
+ }
+
+ ALOGV("%s: exit: status(%d)", __func__, status);
+ return status;
+}
+
+int voice_extn_compress_voip_open_input_stream(struct stream_in *in)
+{
+ int sample_rate;
+ int buffer_size,frame_size;
+ int mode, ret;
+
+ ALOGD("%s: enter", __func__);
+
+ if ((voip_data.sample_rate != 0) &&
+ (voip_data.sample_rate != in->config.rate)) {
+ ret = -ENOTSUP;
+ goto done;
+ } else {
+ voip_data.sample_rate = in->config.rate;
+ }
+
+ in->usecase = USECASE_COMPRESS_VOIP_CALL;
+ if (in->config.rate == 16000)
+ in->config = pcm_config_voip_wb;
+ else
+ in->config = pcm_config_voip_nb;
+
+ voip_data.in_stream_count++;
+ ret = voip_set_mode(in->dev, in->format);
+
+done:
+ ALOGV("%s: exit, ret=%d", __func__, ret);
+ return ret;
+}
+
+int voice_extn_compress_voip_set_volume(struct audio_device *adev, float volume)
+{
+ int vol, err = 0;
+
+ ALOGV("%s: enter", __func__);
+
+ if (volume < 0.0) {
+ volume = 0.0;
+ } else if (volume > 1.0) {
+ volume = 1.0;
+ }
+
+ vol = lrint(volume * 100.0);
+
+ /* Voice volume levels from android are mapped to driver volume levels as follows.
+ * 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
+ * So adjust the volume to get the correct volume index in driver
+ */
+ vol = 100 - vol;
+
+ err = voip_set_volume(adev, vol);
+
+ ALOGV("%s: exit: status(%d)", __func__, err);
+
+ return err;
+}
+
+int voice_extn_compress_voip_set_mic_mute(struct audio_device *adev, bool state)
+{
+ int err = 0;
+
+ ALOGV("%s: enter", __func__);
+
+ err = voip_set_mic_mute(adev, state);
+
+ ALOGV("%s: exit: status(%d)", __func__, err);
+ return err;
+}
+
+bool voice_extn_compress_voip_pcm_prop_check()
+{
+ char prop_value[PROPERTY_VALUE_MAX] = {0};
+
+ property_get("use.voice.path.for.pcm.voip", prop_value, "0");
+ if (!strncmp("true", prop_value, sizeof("true")))
+ {
+ ALOGD("%s: VoIP PCM property is enabled", __func__);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool voice_extn_compress_voip_is_active(struct audio_device *adev)
+{
+ struct audio_usecase *voip_usecase = NULL;
+ voip_usecase = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL);
+
+ if (voip_usecase != NULL)
+ return true;
+ else
+ return false;
+}
+
+bool voice_extn_compress_voip_is_format_supported(audio_format_t format)
+{
+ switch (format) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (voice_extn_compress_voip_pcm_prop_check())
+ return true;
+ else
+ return false;
+ case AUDIO_FORMAT_AMR_NB:
+ case AUDIO_FORMAT_AMR_WB:
+ case AUDIO_FORMAT_EVRC:
+ case AUDIO_FORMAT_EVRCB:
+ case AUDIO_FORMAT_EVRCWB:
+ case AUDIO_FORMAT_EVRCNW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool voice_extn_compress_voip_is_config_supported(struct audio_config *config)
+{
+ bool ret = false;
+
+ ret = voice_extn_compress_voip_is_format_supported(config->format);
+ if (ret) {
+ if ((popcount(config->channel_mask) == 1) &&
+ (config->sample_rate == 8000 || config->sample_rate == 16000))
+ ret = ((voip_data.sample_rate == 0) ? true:
+ (voip_data.sample_rate == config->sample_rate));
+ else
+ ret = false;
+ }
+ return ret;
+}