summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher N. Hesse <raymanfx@gmail.com>2017-02-02 20:49:55 +0100
committerChristopher N. Hesse <raymanfx@gmail.com>2017-02-10 21:41:49 +0100
commit696959dda1cd4437dd672a61d7a3266169ad3019 (patch)
treecd9520cfd640c79efa5dfc90d58d85b9bbdc78ea
parent5a2f10031a49f65b98ae0f495197689ee5c01cf8 (diff)
downloadhardware_samsung-696959dda1cd4437dd672a61d7a3266169ad3019.tar.gz
hardware_samsung-696959dda1cd4437dd672a61d7a3266169ad3019.tar.bz2
hardware_samsung-696959dda1cd4437dd672a61d7a3266169ad3019.zip
audio: Enable voice call support
Pair-Programmed-With: Andreas Schneider <asn@cryptomilk.org> Change-Id: I284f21240e56dda93cb8a2ab98b903ff712504a6
-rw-r--r--audio/audio_hw.c44
-rw-r--r--audio/include/samsung_audio.h4
-rw-r--r--audio/voice.c270
-rw-r--r--audio/voice.h9
4 files changed, 314 insertions, 13 deletions
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index 986ef0a..c147acf 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -502,11 +502,11 @@ static struct audio_usecase *get_usecase_from_type(struct audio_device *adev,
static int set_voice_volume_l(struct audio_device *adev, float volume)
{
int err = 0;
- (void)volume;
if (adev->mode == AUDIO_MODE_IN_CALL) {
- /* TODO */
+ set_voice_session_volume(adev->voice.session, volume);
}
+
return err;
}
@@ -861,6 +861,7 @@ static int select_devices(struct audio_device *adev,
if (usecase->type == VOICE_CALL) {
out_snd_device = get_output_snd_device(adev, active_out->devices);
+ prepare_voice_session(adev->voice.session, active_out->devices);
in_snd_device = get_input_snd_device(adev, active_out->devices);
usecase->devices = active_out->devices;
} else {
@@ -925,6 +926,11 @@ static int select_devices(struct audio_device *adev,
/* Enable new sound devices */
if (out_snd_device != SND_DEVICE_NONE) {
+ /* We need to update the audio path if we switch the out devices */
+ if (adev->voice.in_call) {
+ set_voice_session_audio_path(adev->voice.session);
+ }
+
enable_snd_device(adev, usecase, out_snd_device, false);
}
@@ -2079,6 +2085,11 @@ static int start_input_stream(struct stream_in *in)
#endif
#endif
+ if (in->dev->voice.in_call) {
+ ALOGV("%s: in_call, not handling PCMs", __func__);
+ goto skip_pcm_handling;
+ }
+
/* Open the PCM device.
* The HW is limited to support only the default pcm_profile settings.
* As such a change in aux_channels will not have an effect.
@@ -2116,6 +2127,7 @@ static int start_input_stream(struct stream_in *in)
}
}
+skip_pcm_handling:
/* force read and proc buffer reallocation in case of frame size or
* channel count change */
in->proc_buf_frames = 0;
@@ -2250,6 +2262,11 @@ static int out_open_pcm_devices(struct stream_out *out)
if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
pcm_device_id = pcm_device_deep_buffer.id;
+ if (out->dev->voice.in_call) {
+ ALOGV("%s: in_call, not opening PCMs", __func__);
+ return ret;
+ }
+
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
__func__, pcm_device_card, pcm_device_id);
@@ -2396,14 +2413,14 @@ error_config:
return ret;
}
-static int stop_voice_call(struct audio_device *adev)
+int stop_voice_call(struct audio_device *adev)
{
struct audio_usecase *uc_info;
ALOGV("%s: enter", __func__);
adev->voice.in_call = false;
- /* TODO: implement voice call stop */
+ stop_voice_session(adev->voice.session);
uc_info = get_usecase_from_id(adev, USECASE_VOICE_CALL);
if (uc_info == NULL) {
@@ -2415,7 +2432,6 @@ static int stop_voice_call(struct audio_device *adev)
disable_snd_device(adev, uc_info, uc_info->out_snd_device, false);
disable_snd_device(adev, uc_info, uc_info->in_snd_device, true);
- uc_release_pcm_devices(uc_info);
list_remove(&uc_info->adev_list_node);
free(uc_info);
@@ -2424,7 +2440,7 @@ static int stop_voice_call(struct audio_device *adev)
}
/* always called with adev lock held */
-static int start_voice_call(struct audio_device *adev)
+int start_voice_call(struct audio_device *adev)
{
struct audio_usecase *uc_info;
int ret = 0;
@@ -2436,6 +2452,12 @@ static int start_voice_call(struct audio_device *adev)
ret = -ENOMEM;
goto exit;
}
+ /*
+ * We set this early so that functions called after this is being set
+ * can use it. It is e.g. needed in select_devices() to inform the RILD
+ * which output device we use.
+ */
+ adev->voice.in_call = true;
uc_info->id = USECASE_VOICE_CALL;
uc_info->type = VOICE_CALL;
@@ -2444,19 +2466,19 @@ static int start_voice_call(struct audio_device *adev)
uc_info->in_snd_device = SND_DEVICE_NONE;
uc_info->out_snd_device = SND_DEVICE_NONE;
- uc_select_pcm_devices(uc_info);
+ list_init(&uc_info->mixer_list);
+ list_add_tail(&uc_info->mixer_list,
+ &adev_get_mixer_for_card(adev, SOUND_CARD)->uc_list_node[uc_info->id]);
list_add_tail(&adev->usecase_list, &uc_info->adev_list_node);
select_devices(adev, USECASE_VOICE_CALL);
-
- /* TODO: implement voice call start */
+ start_voice_session(adev->voice.session);
/* set cached volume */
set_voice_volume_l(adev, adev->voice.volume);
- adev->voice.in_call = true;
exit:
ALOGV("%s: exit", __func__);
return ret;
@@ -4317,7 +4339,7 @@ static int adev_open(const hw_module_t *module, const char *name,
}
}
- adev->voice.session = voice_session_init();
+ adev->voice.session = voice_session_init(adev);
if (adev->voice.session == NULL) {
ALOGE("%s: Failed to initialize voice session data", __func__);
diff --git a/audio/include/samsung_audio.h b/audio/include/samsung_audio.h
index 1bd094a..1e84c05 100644
--- a/audio/include/samsung_audio.h
+++ b/audio/include/samsung_audio.h
@@ -39,6 +39,10 @@
#define SOUND_CAPTURE_DEVICE 0
#define SOUND_CAPTURE_SCO_DEVICE 2
+/* Voice calls */
+#define SOUND_PLAYBACK_VOICE_DEVICE 1
+#define SOUND_CAPTURE_VOICE_DEVICE 1
+
/* Unusupported
#define SOUND_CAPTURE_LOOPBACK_AEC_DEVICE 1
#define SOUND_CAPTURE_HOTWORD_DEVICE 0
diff --git a/audio/voice.c b/audio/voice.c
index 57fcc60..f10d8ed 100644
--- a/audio/voice.c
+++ b/audio/voice.c
@@ -26,11 +26,252 @@
#include <stdlib.h>
#include <pthread.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <samsung_audio.h>
+
#include "audio_hw.h"
#include "voice.h"
-struct voice_session *voice_session_init(void)
+static struct pcm_config pcm_config_voicecall = {
+ .channels = 2,
+ .rate = 8000,
+ .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
+ .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+static struct pcm_config pcm_config_voicecall_wideband = {
+ .channels = 2,
+ .rate = 16000,
+ .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
+ .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+/* Prototypes */
+int start_voice_call(struct audio_device *adev);
+int stop_voice_call(struct audio_device *adev);
+
+void set_voice_session_audio_path(struct voice_session *session)
+{
+ enum _AudioPath device_type;
+
+ switch(session->out_device) {
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ device_type = SOUND_AUDIO_PATH_SPEAKER;
+ break;
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ device_type = SOUND_AUDIO_PATH_HANDSET;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+ device_type = SOUND_AUDIO_PATH_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+ device_type = SOUND_AUDIO_PATH_HEADPHONE;
+ break;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ device_type = SOUND_AUDIO_PATH_BLUETOOTH;
+ break;
+ default:
+ /* if output device isn't supported, use handset by default */
+ device_type = SOUND_AUDIO_PATH_HANDSET;
+ break;
+ }
+
+ ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
+
+ ril_set_call_audio_path(&session->ril, device_type);
+}
+
+/*
+ * This decides based on the output device, if we enable
+ * two mic control
+ */
+void prepare_voice_session(struct voice_session *session,
+ audio_devices_t active_out_devices)
+{
+ ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
+
+ session->out_device = active_out_devices;
+
+ switch (session->out_device) {
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ session->two_mic_control = true;
+ break;
+ default:
+ session->two_mic_control = false;
+ break;
+ }
+
+ if (session->two_mic_disabled) {
+ session->two_mic_control = false;
+ }
+}
+
+/*
+ * This function must be called with hw device mutex locked, OK to hold other
+ * mutexes
+ */
+int start_voice_session(struct voice_session *session)
{
+ struct pcm_config *voice_config;
+
+ if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
+ ALOGW("%s: Voice PCMs already open!\n", __func__);
+ return 0;
+ }
+
+ ALOGV("%s: Opening voice PCMs", __func__);
+
+ if (session->wb_amr) {
+ ALOGV("%s: pcm_config wideband", __func__);
+ voice_config = &pcm_config_voicecall_wideband;
+ } else {
+ ALOGV("%s: pcm_config narrowband", __func__);
+ voice_config = &pcm_config_voicecall;
+ }
+
+ /* Open modem PCM channels */
+ session->pcm_voice_rx = pcm_open(SOUND_CARD,
+ SOUND_PLAYBACK_VOICE_DEVICE,
+ PCM_OUT|PCM_MONOTONIC,
+ voice_config);
+ if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
+ ALOGE("%s: cannot open PCM voice RX stream: %s",
+ __func__,
+ pcm_get_error(session->pcm_voice_rx));
+
+ pcm_close(session->pcm_voice_tx);
+ session->pcm_voice_tx = NULL;
+
+ return -ENOMEM;
+ }
+
+ session->pcm_voice_tx = pcm_open(SOUND_CARD,
+ SOUND_CAPTURE_VOICE_DEVICE,
+ PCM_IN|PCM_MONOTONIC,
+ voice_config);
+ if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
+ ALOGE("%s: cannot open PCM voice TX stream: %s",
+ __func__,
+ pcm_get_error(session->pcm_voice_tx));
+
+ pcm_close(session->pcm_voice_rx);
+ session->pcm_voice_rx = NULL;
+
+ return -ENOMEM;
+ }
+
+ pcm_start(session->pcm_voice_rx);
+ pcm_start(session->pcm_voice_tx);
+
+ /* TODO: handle SCO */
+
+ if (session->two_mic_control) {
+ ALOGV("%s: enabling two mic control", __func__);
+ ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
+ } else {
+ ALOGV("%s: disabling two mic control", __func__);
+ ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
+ }
+
+ ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
+
+ return 0;
+}
+
+/*
+ * This function must be called with hw device mutex locked, OK to hold other
+ * mutexes
+ */
+void stop_voice_session(struct voice_session *session)
+{
+ int status = 0;
+
+ ALOGV("%s: Closing active PCMs", __func__);
+
+ if (session->pcm_voice_rx != NULL) {
+ pcm_stop(session->pcm_voice_rx);
+ pcm_close(session->pcm_voice_rx);
+ session->pcm_voice_rx = NULL;
+ status++;
+ }
+
+ if (session->pcm_voice_tx != NULL) {
+ pcm_stop(session->pcm_voice_tx);
+ pcm_close(session->pcm_voice_tx);
+ session->pcm_voice_tx = NULL;
+ status++;
+ }
+
+ /* TODO: handle SCO */
+
+ session->out_device = AUDIO_DEVICE_NONE;
+
+ ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
+}
+
+void set_voice_session_volume(struct voice_session *session, float volume)
+{
+ enum _SoundType sound_type;
+
+ switch (session->out_device) {
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ sound_type = SOUND_TYPE_VOICE;
+ break;
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ sound_type = SOUND_TYPE_SPEAKER;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+ sound_type = SOUND_TYPE_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ case AUDIO_DEVICE_OUT_ALL_SCO:
+ sound_type = SOUND_TYPE_BTVOICE;
+ break;
+ default:
+ sound_type = SOUND_TYPE_VOICE;
+ }
+
+ ril_set_call_volume(&session->ril, sound_type, volume);
+}
+
+static void voice_session_wb_amr_callback(void *data, int enable)
+{
+ struct audio_device *adev = (struct audio_device *)data;
+ struct voice_session *session =
+ (struct voice_session *)adev->voice.session;
+
+ pthread_mutex_lock(&adev->lock);
+
+ if (session->wb_amr != enable) {
+ session->wb_amr = enable;
+
+ /* reopen the modem PCMs at the new rate */
+ if (adev->voice.in_call) {
+ ALOGV("%s: %s wide band voice call",
+ __func__,
+ enable ? "Enable" : "Disable");
+
+ stop_voice_call(adev);
+ start_voice_call(adev);
+ }
+ }
+
+ pthread_mutex_unlock(&adev->lock);
+}
+
+struct voice_session *voice_session_init(struct audio_device *adev)
+{
+ char voice_config[PROPERTY_VALUE_MAX];
struct voice_session *session;
int ret;
@@ -39,6 +280,12 @@ struct voice_session *voice_session_init(void)
return NULL;
}
+ /* Two mic control */
+ ret = property_get_bool("audio_hal.disable_two_mic", false);
+ if (ret > 0) {
+ session->two_mic_disabled = true;
+ }
+
/* Do this as the last step so we do not have to close it on error */
ret = ril_open(&session->ril);
if (ret != 0) {
@@ -46,6 +293,27 @@ struct voice_session *voice_session_init(void)
return NULL;
}
+ ret = property_get("audio_hal.force_voice_config", voice_config, "");
+ if (ret > 0) {
+ if ((strncmp(voice_config, "narrow", 6)) == 0)
+ session->wb_amr = false;
+ else if ((strncmp(voice_config, "wide", 4)) == 0)
+ session->wb_amr = true;
+ ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
+ } else {
+ /* register callback for wideband AMR setting */
+ ret = ril_set_wb_amr_callback(&session->ril,
+ voice_session_wb_amr_callback,
+ (void *)adev);
+ if (ret != 0) {
+ ALOGE("%s: Failed to register WB_AMR callback", __func__);
+ free(session);
+ return NULL;
+ }
+
+ ALOGV("%s: Registered WB_AMR callback", __func__);
+ }
+
return session;
}
diff --git a/audio/voice.h b/audio/voice.h
index 40bd1b1..0e32f0a 100644
--- a/audio/voice.h
+++ b/audio/voice.h
@@ -33,7 +33,14 @@ struct voice_session {
audio_devices_t out_device;
};
-struct voice_session *voice_session_init(void);
+void prepare_voice_session(struct voice_session *session,
+ audio_devices_t active_out_devices);
+int start_voice_session(struct voice_session *session);
+void stop_voice_session(struct voice_session *session);
+void set_voice_session_volume(struct voice_session *session, float volume);
+void set_voice_session_audio_path(struct voice_session *session);
+
+struct voice_session *voice_session_init(struct audio_device *adev);
void voice_session_deinit(struct voice_session *s);
#endif /* VOICE_CALL_H */