qcom/audio/hal: Fix the routing logic to route streams independently
- Current implementation assumes that the output devices for all
the output streams and voice call will be same. So it updates
the devices on all the output streams when out_set_parameters()
is called on any stream.
- Update the routing logic to support all the streams independently
based on the devices set by audio policy manager on each stream.
- However, on this target there is a limitation that earpiece,
speaker, and headset devices cannot be enabled concurrently as
they share the same backend. Updated routing logic takes care of
this limitation.
Bug: 8239898
Change-Id: I3091be6894210c77c479b872cec39d821d10bd90
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 1c1adb2..15b1be0 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -121,6 +121,7 @@
/* ACDB IDs (audio DSP path configuration IDs) for each sound device */
static const int acdb_device_table[SND_DEVICE_MAX] = {
+ [SND_DEVICE_NONE] = -1,
[SND_DEVICE_OUT_HANDSET] = 7,
[SND_DEVICE_OUT_SPEAKER] = 14,
[SND_DEVICE_OUT_HEADPHONES] = 10,
@@ -191,48 +192,37 @@
audio_usecase_t usecase,
int device_type)
{
- ALOGV("%s: enter: usecase(%d)", __func__, usecase);
int device_id;
if (device_type == PCM_PLAYBACK)
device_id = pcm_device_table[usecase][0];
else
device_id = pcm_device_table[usecase][1];
- ALOGV("%s: exit: device_id(%d)", __func__, device_id);
return device_id;
}
static int get_acdb_device_id(snd_device_t snd_device)
{
- ALOGV("%s: enter: snd_devie(%d)", __func__, snd_device);
- int acdb_dev_id = acdb_device_table[snd_device];
- ALOGV("%s: exit: acdb_dev_id(%d)", __func__, acdb_dev_id);
- return acdb_dev_id;
+ return acdb_device_table[snd_device];
}
static void add_backend_name(char *mixer_path,
- struct audio_usecase *usecase)
+ snd_device_t snd_device)
{
- audio_devices_t in_device;
- if (usecase->devices & AUDIO_DEVICE_BIT_IN) {
- in_device = usecase->devices & ~AUDIO_DEVICE_BIT_IN;
- if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
- strcat(mixer_path, " bt-sco");
- }
- } else {
- if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
- strcat(mixer_path, " bt-sco");
- } else if ((usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
- (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
- strcat(mixer_path, " speaker-and-hdmi");
- } else if (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
- strcat(mixer_path, " hdmi");
- }
- }
+ if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
+ strcat(mixer_path, " bt-sco");
+ else if(snd_device == SND_DEVICE_OUT_BT_SCO)
+ strcat(mixer_path, " bt-sco");
+ else if (snd_device == SND_DEVICE_OUT_HDMI)
+ strcat(mixer_path, " hdmi");
+ else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
+ strcat(mixer_path, " speaker-and-hdmi");
}
-static int enable_audio_route(struct audio_route *ar,
- struct audio_usecase *usecase)
+static int enable_audio_route(struct audio_device *adev,
+ struct audio_usecase *usecase,
+ bool update_mixer)
{
+ snd_device_t snd_device;
char mixer_path[50];
if (usecase == NULL)
@@ -240,91 +230,190 @@
ALOGV("%s: enter: usecase(%d)", __func__, usecase->id);
- /* Get the updated devices from associated stream */
if (usecase->type == PCM_CAPTURE)
- usecase->devices = usecase->stream.in->device;
+ snd_device = usecase->in_snd_device;
else
- usecase->devices = usecase->stream.out->devices;
+ snd_device = usecase->out_snd_device;
strcpy(mixer_path, use_case_table[usecase->id]);
- add_backend_name(mixer_path, usecase);
+ add_backend_name(mixer_path, snd_device);
ALOGD("%s: apply mixer path: %s", __func__, mixer_path);
- audio_route_apply_path(ar, mixer_path);
+ audio_route_apply_path(adev->audio_route, mixer_path);
+ if (update_mixer)
+ audio_route_update_mixer(adev->audio_route);
ALOGV("%s: exit", __func__);
return 0;
}
-static int disable_audio_route(struct audio_route *ar,
- struct audio_usecase *usecase)
+static int disable_audio_route(struct audio_device *adev,
+ struct audio_usecase *usecase,
+ bool update_mixer)
{
+ snd_device_t snd_device;
char mixer_path[50];
if (usecase == NULL)
return -EINVAL;
ALOGV("%s: enter: usecase(%d)", __func__, usecase->id);
-
+ if (usecase->type == PCM_CAPTURE)
+ snd_device = usecase->in_snd_device;
+ else
+ snd_device = usecase->out_snd_device;
strcpy(mixer_path, use_case_table[usecase->id]);
- add_backend_name(mixer_path, usecase);
+ add_backend_name(mixer_path, snd_device);
ALOGD("%s: reset mixer path: %s", __func__, mixer_path);
- audio_route_reset_path(ar, mixer_path);
+ audio_route_reset_path(adev->audio_route, mixer_path);
+ if (update_mixer)
+ audio_route_update_mixer(adev->audio_route);
ALOGV("%s: exit", __func__);
return 0;
}
static int enable_snd_device(struct audio_device *adev,
- snd_device_t snd_device)
+ snd_device_t snd_device,
+ bool update_mixer)
{
int acdb_dev_id, acdb_dev_type;
- ALOGD("%s: snd_device(%d: %s)", __func__,
- snd_device, device_table[snd_device]);
if (snd_device < SND_DEVICE_MIN ||
snd_device >= SND_DEVICE_MAX) {
ALOGE("%s: Invalid sound device %d", __func__, snd_device);
return -EINVAL;
}
+
+ adev->snd_dev_ref_cnt[snd_device]++;
+ if (adev->snd_dev_ref_cnt[snd_device] > 1) {
+ ALOGD("%s: snd_device(%d: %s) is already active",
+ __func__, snd_device, device_table[snd_device]);
+ return 0;
+ }
+
acdb_dev_id = get_acdb_device_id(snd_device);
if (acdb_dev_id < 0) {
ALOGE("%s: Could not find acdb id for device(%d)",
__func__, snd_device);
+ adev->snd_dev_ref_cnt[snd_device]--;
return -EINVAL;
}
- if (snd_device >= SND_DEVICE_OUT_BEGIN &&
- snd_device < SND_DEVICE_OUT_END) {
- acdb_dev_type = ACDB_DEV_TYPE_OUT;
- } else {
- acdb_dev_type = ACDB_DEV_TYPE_IN;
- }
if (adev->acdb_send_audio_cal) {
ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
__func__, snd_device, acdb_dev_id);
+ if (snd_device >= SND_DEVICE_OUT_BEGIN &&
+ snd_device < SND_DEVICE_OUT_END)
+ acdb_dev_type = ACDB_DEV_TYPE_OUT;
+ else
+ acdb_dev_type = ACDB_DEV_TYPE_IN;
adev->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type);
} else {
ALOGW("%s: Could find the symbol acdb_send_audio_cal from %s",
__func__, LIB_ACDB_LOADER);
}
+ ALOGD("%s: snd_device(%d: %s)", __func__,
+ snd_device, device_table[snd_device]);
audio_route_apply_path(adev->audio_route, device_table[snd_device]);
+ if (update_mixer)
+ audio_route_update_mixer(adev->audio_route);
+
return 0;
}
-static int disable_snd_device(struct audio_route *ar,
- snd_device_t snd_device)
+static int disable_snd_device(struct audio_device *adev,
+ snd_device_t snd_device,
+ bool update_mixer)
{
- ALOGD("%s: snd_device(%d: %s)", __func__,
- snd_device, device_table[snd_device]);
if (snd_device < SND_DEVICE_MIN ||
snd_device >= SND_DEVICE_MAX) {
ALOGE("%s: Invalid sound device %d", __func__, snd_device);
return -EINVAL;
}
- audio_route_reset_path(ar, device_table[snd_device]);
+ if (adev->snd_dev_ref_cnt[snd_device] <= 0) {
+ ALOGE("%s: device ref cnt is already 0", __func__);
+ return -EINVAL;
+ }
+ adev->snd_dev_ref_cnt[snd_device]--;
+ if (adev->snd_dev_ref_cnt[snd_device] == 0) {
+ ALOGD("%s: snd_device(%d: %s)", __func__,
+ snd_device, device_table[snd_device]);
+ audio_route_reset_path(adev->audio_route, device_table[snd_device]);
+ if (update_mixer)
+ audio_route_update_mixer(adev->audio_route);
+ }
return 0;
}
+static void check_usecases_codec_backend(struct audio_device *adev,
+ struct audio_usecase *uc_info,
+ snd_device_t snd_device)
+{
+ struct listnode *node;
+ struct audio_usecase *usecase;
+ bool switch_device[AUDIO_USECASE_MAX];
+ int i, num_uc_to_switch = 0;
+
+ /*
+ * This function is to make sure that all the usecases that are active on
+ * the hardware codec backend are always routed to any one device that is
+ * handled by the hardware codec.
+ * For example, if low-latency and deep-buffer usecases are currently active
+ * on speaker and out_set_parameters(headset) is received on low-latency
+ * output, then we have to make sure deep-buffer is also switched to headset,
+ * because of the limitation that both the devices cannot be enabled
+ * at the same time as they share the same backend.
+ */
+ /* Disable all the usecases on the shared backend other than the
+ specified usecase */
+ for (i = 0; i < AUDIO_USECASE_MAX; i++)
+ switch_device[i] = false;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type != PCM_CAPTURE &&
+ usecase != uc_info &&
+ usecase->out_snd_device != snd_device &&
+ usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+ ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
+ __func__, use_case_table[usecase->id],
+ device_table[usecase->out_snd_device]);
+ disable_audio_route(adev, usecase, false);
+ switch_device[usecase->id] = true;
+ num_uc_to_switch++;
+ }
+ }
+
+ if (num_uc_to_switch) {
+ /* Make sure all the streams are de-routed before disabling the device */
+ audio_route_update_mixer(adev->audio_route);
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (switch_device[usecase->id]) {
+ disable_snd_device(adev, usecase->out_snd_device, false);
+ enable_snd_device(adev, snd_device, false);
+ }
+ }
+
+ /* Make sure new snd device is enabled before re-routing the streams */
+ audio_route_update_mixer(adev->audio_route);
+
+ /* Re-route all the usecases on the shared backend other than the
+ specified usecase to new snd devices */
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ /* Update the out_snd_device only before enabling the audio route */
+ if (switch_device[usecase->id] ) {
+ usecase->out_snd_device = snd_device;
+ enable_audio_route(adev, usecase, false);
+ }
+ }
+
+ audio_route_update_mixer(adev->audio_route);
+ }
+}
+
static int set_hdmi_channels(struct mixer *mixer,
int channel_count)
{
@@ -386,13 +475,11 @@
ALOGV("%s: exit", __func__);
}
-static snd_device_t get_output_snd_device(struct audio_device *adev)
+static snd_device_t get_output_snd_device(struct audio_device *adev,
+ audio_devices_t devices)
{
- audio_source_t source = (adev->active_input == NULL) ?
- AUDIO_SOURCE_DEFAULT : adev->active_input->source;
- audio_mode_t mode = adev->mode;
- audio_devices_t devices = adev->out_device;
- snd_device_t snd_device = SND_DEVICE_NONE;
+ audio_mode_t mode = adev->mode;
+ snd_device_t snd_device = SND_DEVICE_NONE;
ALOGV("%s: enter: output devices(%#x)", __func__, devices);
if (devices == AUDIO_DEVICE_NONE ||
@@ -470,13 +557,13 @@
return snd_device;
}
-static snd_device_t get_input_snd_device(struct audio_device *adev)
+static snd_device_t get_input_snd_device(struct audio_device *adev,
+ audio_devices_t out_device)
{
audio_source_t source = (adev->active_input == NULL) ?
AUDIO_SOURCE_DEFAULT : adev->active_input->source;
audio_mode_t mode = adev->mode;
- audio_devices_t out_device = adev->out_device;
audio_devices_t in_device = ((adev->active_input == NULL) ?
AUDIO_DEVICE_NONE : adev->active_input->device)
& ~AUDIO_DEVICE_BIT_IN;
@@ -620,24 +707,72 @@
return snd_device;
}
-static int select_devices(struct audio_device *adev)
+static struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
+ audio_usecase_t uc_id)
+{
+ struct audio_usecase *usecase;
+ struct listnode *node;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->id == uc_id)
+ return usecase;
+ }
+ return NULL;
+}
+
+static int select_devices(struct audio_device *adev,
+ audio_usecase_t uc_id)
{
snd_device_t out_snd_device = SND_DEVICE_NONE;
snd_device_t in_snd_device = SND_DEVICE_NONE;
- struct audio_usecase *usecase;
+ struct audio_usecase *usecase = NULL;
+ struct audio_usecase *vc_usecase = NULL;
struct listnode *node;
- int status = 0;
int acdb_rx_id, acdb_tx_id;
- bool in_call_device_switch = false;
+ int status = 0;
- ALOGV("%s: enter", __func__);
- out_snd_device = get_output_snd_device(adev);
- in_snd_device = get_input_snd_device(adev);
+ usecase = get_usecase_from_list(adev, uc_id);
+ if (usecase == NULL) {
+ ALOGE("%s: Could not find the usecase(%d)", __func__, uc_id);
+ return -EINVAL;
+ }
- if (out_snd_device == adev->cur_out_snd_device && adev->out_snd_device_active &&
- in_snd_device == adev->cur_in_snd_device && adev->in_snd_device_active) {
- ALOGV("%s: exit: snd_devices (%d and %d) are already active",
- __func__, out_snd_device, in_snd_device);
+ if (usecase->type == VOICE_CALL) {
+ out_snd_device = get_output_snd_device(adev, usecase->stream.out->devices);
+ in_snd_device = get_input_snd_device(adev, usecase->stream.out->devices);
+ usecase->devices = usecase->stream.out->devices;
+ } else {
+ /*
+ * If the voice call is active, use the sound devices of voice call usecase
+ * so that it would not result any device switch. All the usecases will
+ * be switched to new device when select_devices() is called for voice call
+ * usecase. This is to avoid switching devices for voice call when
+ * check_usecases_codec_backend() is called below.
+ */
+ if (adev->in_call) {
+ vc_usecase = get_usecase_from_list(adev, USECASE_VOICE_CALL);
+ if (vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+ in_snd_device = vc_usecase->in_snd_device;
+ out_snd_device = vc_usecase->out_snd_device;
+ }
+ }
+ if (usecase->type == PCM_PLAYBACK) {
+ usecase->devices = usecase->stream.out->devices;
+ in_snd_device = SND_DEVICE_NONE;
+ if (out_snd_device == SND_DEVICE_NONE)
+ out_snd_device = get_output_snd_device(adev,
+ usecase->stream.out->devices);
+ } else if (usecase->type == PCM_CAPTURE) {
+ usecase->devices = usecase->stream.in->device;
+ out_snd_device = SND_DEVICE_NONE;
+ if (in_snd_device == SND_DEVICE_NONE)
+ in_snd_device = get_input_snd_device(adev, SND_DEVICE_NONE);
+ }
+ }
+
+ if (out_snd_device == usecase->out_snd_device &&
+ in_snd_device == usecase->in_snd_device) {
return 0;
}
@@ -650,8 +785,9 @@
* and enable both RX and TX devices though one of them is same as current
* device.
*/
- if (adev->in_call && adev->csd_client != NULL) {
- in_call_device_switch = true;
+ if (usecase->type == VOICE_CALL && adev->csd_client != NULL &&
+ usecase->in_snd_device != SND_DEVICE_NONE &&
+ usecase->out_snd_device != SND_DEVICE_NONE) {
/* This must be called before disabling the mixer controls on APQ side */
if (adev->csd_disable_device == NULL) {
ALOGE("%s: dlsym error for csd_client_disable_device", __func__);
@@ -664,66 +800,35 @@
}
}
- if ((out_snd_device != adev->cur_out_snd_device || in_call_device_switch)
- && adev->out_snd_device_active) {
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == PCM_PLAYBACK || usecase->type == VOICE_CALL)
- disable_audio_route(adev->audio_route, usecase);
- }
- audio_route_update_mixer(adev->audio_route);
- /* Disable current rx device */
- disable_snd_device(adev->audio_route, adev->cur_out_snd_device);
- adev->out_snd_device_active = false;
+ /* Disable current sound devices */
+ if (usecase->out_snd_device != SND_DEVICE_NONE) {
+ disable_audio_route(adev, usecase, true);
+ disable_snd_device(adev, usecase->out_snd_device, false);
}
- if ((in_snd_device != adev->cur_in_snd_device || in_call_device_switch)
- && adev->in_snd_device_active) {
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == PCM_CAPTURE)
- disable_audio_route(adev->audio_route, usecase);
- }
- audio_route_update_mixer(adev->audio_route);
- /* Disable current tx device */
- disable_snd_device(adev->audio_route, adev->cur_in_snd_device);
- adev->in_snd_device_active = false;
+ if (usecase->in_snd_device != SND_DEVICE_NONE) {
+ disable_audio_route(adev, usecase, true);
+ disable_snd_device(adev, usecase->in_snd_device, false);
}
- if (out_snd_device != SND_DEVICE_NONE && !adev->out_snd_device_active) {
- /* Enable new rx device */
- status = enable_snd_device(adev, out_snd_device);
- if (status != 0) {
- ALOGE("%s: Failed to set mixer ctls for snd_device(%d)",
- __func__, out_snd_device);
- return status;
- }
- adev->out_snd_device_active = true;
- adev->cur_out_snd_device = out_snd_device;
+ /* Enable new sound devices */
+ if (out_snd_device != SND_DEVICE_NONE) {
+ if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
+ check_usecases_codec_backend(adev, usecase, out_snd_device);
+ enable_snd_device(adev, out_snd_device, false);
}
- if (in_snd_device != SND_DEVICE_NONE && !adev->in_snd_device_active) {
- /* Enable new tx device */
- status = enable_snd_device(adev, in_snd_device);
- if (status != 0) {
- ALOGE("%s: Failed to set mixer ctls for snd_device(%d)",
- __func__, out_snd_device);
- return status;
- }
- adev->in_snd_device_active = true;
- adev->cur_in_snd_device = in_snd_device;
- }
+ if (in_snd_device != SND_DEVICE_NONE)
+ enable_snd_device(adev, in_snd_device, false);
+
audio_route_update_mixer(adev->audio_route);
- if (!list_empty(&adev->usecase_list)) {
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- enable_audio_route(adev->audio_route, usecase);
- }
- audio_route_update_mixer(adev->audio_route);
- }
+ usecase->in_snd_device = in_snd_device;
+ usecase->out_snd_device = out_snd_device;
- if (adev->mode == AUDIO_MODE_IN_CALL && adev->csd_client) {
+ enable_audio_route(adev, usecase, true);
+
+ if (usecase->type == VOICE_CALL && adev->csd_client) {
if (adev->csd_enable_device == NULL) {
ALOGE("%s: dlsym error for csd_client_enable_device",
__func__);
@@ -731,64 +836,23 @@
acdb_rx_id = get_acdb_device_id(out_snd_device);
acdb_tx_id = get_acdb_device_id(in_snd_device);
- status = adev->csd_enable_device(acdb_rx_id, acdb_tx_id,
- adev->acdb_settings);
- if (status < 0) {
- ALOGE("%s: csd_client_enable_device, failed, error %d",
- __func__, status);
+ if (acdb_rx_id > 0 || acdb_tx_id > 0) {
+ status = adev->csd_enable_device(acdb_rx_id, acdb_tx_id,
+ adev->acdb_settings);
+ if (status < 0) {
+ ALOGE("%s: csd_client_enable_device, failed, error %d",
+ __func__, status);
+ }
+ } else {
+ ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+ acdb_rx_id, acdb_tx_id);
}
}
}
- ALOGV("%s: exit: status(%d)", __func__, status);
return status;
}
-static struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
- audio_usecase_t uc_id)
-{
- struct audio_usecase *usecase = NULL;
- struct listnode *node;
-
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->id == uc_id)
- break;
- }
- return usecase;
-}
-
-static audio_devices_t get_active_out_devices(struct audio_device *adev,
- audio_usecase_t uc_id)
-{
- audio_devices_t devices = AUDIO_DEVICE_NONE;
- struct audio_usecase *usecase;
- struct listnode *node;
-
- /* Return the output devices of usecases other than given usecase */
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == PCM_PLAYBACK && usecase->id != uc_id)
- devices |= usecase->stream.out->devices;
- }
-
- return devices;
-}
-
-static audio_devices_t get_voice_call_out_device(struct audio_device *adev)
-{
- struct audio_usecase *usecase;
- struct listnode *node;
-
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == VOICE_CALL)
- return usecase->stream.out->devices;
- }
-
- return AUDIO_DEVICE_NONE;
-}
-
static int stop_input_stream(struct stream_in *in)
{
int i, ret = 0;
@@ -797,7 +861,8 @@
adev->active_input = NULL;
- ALOGD("%s: enter: usecase(%d)", __func__, in->usecase);
+ ALOGD("%s: enter: usecase(%d: %s)", __func__,
+ in->usecase, use_case_table[in->usecase]);
uc_info = get_usecase_from_list(adev, in->usecase);
if (uc_info == NULL) {
ALOGE("%s: Could not find the usecase (%d) in the list",
@@ -806,15 +871,14 @@
}
/* 1. Disable stream specific mixer controls */
- disable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
+ disable_audio_route(adev, uc_info, true);
+
+ /* 2. Disable the tx device */
+ disable_snd_device(adev, uc_info->in_snd_device, true);
list_remove(&uc_info->list);
free(uc_info);
- /* 2. Disable the tx device */
- ret = select_devices(adev);
-
ALOGD("%s: exit: status(%d)", __func__, ret);
return ret;
}
@@ -823,19 +887,10 @@
{
/* 1. Enable output device and stream routing controls */
int ret = 0;
- snd_device_t in_snd_device;
struct audio_usecase *uc_info;
struct audio_device *adev = in->dev;
ALOGD("%s: enter: usecase(%d)", __func__, in->usecase);
- adev->active_input = in;
- in_snd_device = get_input_snd_device(adev);
- if (in_snd_device == SND_DEVICE_NONE) {
- ALOGE("%s: Could not get valid input sound device", __func__);
- ret = -EINVAL;
- goto error_config;
- }
-
in->pcm_device_id = get_pcm_device_id(adev->audio_route,
in->usecase,
PCM_CAPTURE);
@@ -845,29 +900,19 @@
ret = -EINVAL;
goto error_config;
}
+
+ adev->active_input = in;
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
uc_info->id = in->usecase;
uc_info->type = PCM_CAPTURE;
- uc_info->devices = in->device;
uc_info->stream.in = in;
+ uc_info->devices = in->device;
+ uc_info->in_snd_device = SND_DEVICE_NONE;
+ uc_info->out_snd_device = SND_DEVICE_NONE;
- /* 1. Enable the TX device */
- ret = select_devices(adev);
- if (ret) {
- ALOGE("%s: Failed to enable device(%#x)",
- __func__, in->device);
- free(uc_info);
- goto error_config;
- }
-
- /* 2. Enable the mixer controls for the audio route */
- enable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
-
- /* 3. Add the usecase info to usecase list */
list_add_tail(&adev->usecase_list, &uc_info->list);
+ select_devices(adev, in->usecase);
- /* 2. Open the pcm device */
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
__func__, SOUND_CARD, in->pcm_device_id, in->config.channels);
in->pcm = pcm_open(SOUND_CARD, in->pcm_device_id,
@@ -887,7 +932,7 @@
error_config:
adev->active_input = NULL;
- ALOGV("%s: exit: status(%d)", __func__, ret);
+ ALOGD("%s: exit: status(%d)", __func__, ret);
return ret;
}
@@ -898,7 +943,8 @@
struct audio_usecase *uc_info;
struct audio_device *adev = out->dev;
- ALOGD("%s: enter: usecase(%d)", __func__, out->usecase);
+ ALOGD("%s: enter: usecase(%d: %s)", __func__,
+ out->usecase, use_case_table[out->usecase]);
uc_info = get_usecase_from_list(adev, out->usecase);
if (uc_info == NULL) {
ALOGE("%s: Could not find the usecase (%d) in the list",
@@ -907,40 +953,26 @@
}
/* 1. Get and set stream specific mixer controls */
- disable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
+ disable_audio_route(adev, uc_info, true);
+
+ /* 2. Disable the rx device */
+ disable_snd_device(adev, uc_info->out_snd_device, true);
list_remove(&uc_info->list);
free(uc_info);
- /* 2. Disable the rx device */
- adev->out_device = get_active_out_devices(adev, out->usecase);
- adev->out_device |= get_voice_call_out_device(adev);
- ret = select_devices(adev);
-
- ALOGD("%s: exit: status(%d) adev->out_device(%#x)",
- __func__, ret, adev->out_device);
+ ALOGD("%s: exit: status(%d)", __func__, ret);
return ret;
}
int start_output_stream(struct stream_out *out)
{
int ret = 0;
- snd_device_t out_snd_device;
struct audio_usecase *uc_info;
struct audio_device *adev = out->dev;
- /* 1. Enable output device and stream routing controls */
- ALOGD("%s: enter: usecase(%d) devices(%#x)",
- __func__, out->usecase, out->devices);
- adev->out_device |= out->devices;
- out_snd_device = get_output_snd_device(adev);
- if (out_snd_device == SND_DEVICE_NONE) {
- ALOGE("%s: Could not get valid output sound device", __func__);
- ret = -EINVAL;
- goto error_config;
- }
-
+ ALOGD("%s: enter: usecase(%d: %s) devices(%#x)",
+ __func__, out->usecase, use_case_table[out->usecase], out->devices);
out->pcm_device_id = get_pcm_device_id(adev->audio_route,
out->usecase,
PCM_PLAYBACK);
@@ -954,22 +986,15 @@
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
uc_info->id = out->usecase;
uc_info->type = PCM_PLAYBACK;
- uc_info->devices = out->devices;
uc_info->stream.out = out;
-
- ret = select_devices(adev);
- if (ret) {
- ALOGE("%s: Failed to enable device(%#x)",
- __func__, adev->out_device);
- free(uc_info);
- goto error_config;
- }
-
- enable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
+ uc_info->devices = out->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, out->usecase);
+
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
__func__, 0, out->pcm_device_id);
out->pcm = pcm_open(SOUND_CARD, out->pcm_device_id,
@@ -979,15 +1004,13 @@
pcm_close(out->pcm);
out->pcm = NULL;
ret = -EIO;
- goto error;
+ goto error_pcm_open;
}
ALOGD("%s: exit", __func__);
return 0;
-error:
+error_pcm_open:
stop_output_stream(out);
error_config:
- adev->out_device = get_active_out_devices(adev, out->usecase);
- adev->out_device |= get_voice_call_out_device(adev);
return ret;
}
@@ -1027,15 +1050,15 @@
}
/* 2. Get and set stream specific mixer controls */
- disable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
+ 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);
- /* 3. Disable the rx and tx devices */
- ret = select_devices(adev);
-
ALOGD("%s: exit: status(%d)", __func__, ret);
return ret;
}
@@ -1051,20 +1074,15 @@
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
uc_info->id = USECASE_VOICE_CALL;
uc_info->type = VOICE_CALL;
- uc_info->devices = adev->primary_output->devices;
uc_info->stream.out = adev->primary_output;
-
- ret = select_devices(adev);
- if (ret) {
- free(uc_info);
- return ret;
- }
-
- enable_audio_route(adev->audio_route, uc_info);
- audio_route_update_mixer(adev->audio_route);
+ 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_VOICE_CALL);
+
pcm_dev_rx_id = get_pcm_device_id(adev->audio_route, uc_info->id,
PCM_PLAYBACK);
pcm_dev_tx_id = get_pcm_device_id(adev->audio_route, uc_info->id,
@@ -1211,7 +1229,8 @@
{
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
- ALOGD("%s: enter: usecase(%d)", __func__, out->usecase);
+ ALOGD("%s: enter: usecase(%d: %s)", __func__,
+ out->usecase, use_case_table[out->usecase]);
pthread_mutex_lock(&out->lock);
if (!out->standby) {
@@ -1245,8 +1264,8 @@
int ret, val = 0;
bool select_new_device = false;
- ALOGD("%s: enter: usecase(%d) kvpairs: %s",
- __func__, out->usecase, kvpairs);
+ ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
+ __func__, out->usecase, use_case_table[out->usecase], kvpairs);
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
if (ret >= 0) {
@@ -1254,34 +1273,54 @@
pthread_mutex_lock(&out->lock);
pthread_mutex_lock(&adev->lock);
+ /*
+ * When HDMI cable is unplugged the music playback is paused and
+ * the policy manager sends routing=0. But the audioflinger
+ * continues to write data until standby time (3sec).
+ * As the HDMI core is turned off, the write gets blocked.
+ * Avoid this by routing audio to speaker until standby.
+ */
+ if (out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL &&
+ val == AUDIO_DEVICE_NONE) {
+ val = AUDIO_DEVICE_OUT_SPEAKER;
+ }
+
+ /*
+ * select_devices() call below switches all the usecases on the same
+ * backend to the new device. Refer to check_usecases_codec_backend() in
+ * the select_devices(). But how do we undo this?
+ *
+ * For example, music playback is active on headset (deep-buffer usecase)
+ * and if we go to ringtones and select a ringtone, low-latency usecase
+ * will be started on headset+speaker. As we can't enable headset+speaker
+ * and headset devices at the same time, select_devices() switches the music
+ * playback to headset+speaker while starting low-lateny usecase for ringtone.
+ * So when the ringtone playback is completed, how do we undo the same?
+ *
+ * We are relying on the out_set_parameters() call on deep-buffer output,
+ * once the ringtone playback is ended.
+ * NOTE: We should not check if the current devices are same as new devices.
+ * Because select_devices() must be called to switch back the music
+ * playback to headset.
+ */
if (val != 0) {
- /* ToDo: Fix device updation logic */
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if ((usecase->type == PCM_PLAYBACK || usecase->type == VOICE_CALL)
- && usecase->stream.out != out)
- usecase->stream.out->devices = val;
+ out->devices = val;
+
+ if (!out->standby)
+ select_devices(adev, out->usecase);
+
+ if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call &&
+ (out == adev->primary_output)) {
+ start_voice_call(adev);
+ } else if ((adev->mode == AUDIO_MODE_IN_CALL) && adev->in_call &&
+ (out == adev->primary_output)) {
+ select_devices(adev, USECASE_VOICE_CALL);
}
}
- adev->out_device = get_active_out_devices(adev, out->usecase) | val;
- if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call &&
- (out == adev->primary_output) && (val != 0)) {
- out->devices = val;
- start_voice_call(adev);
- } else if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
- (out == adev->primary_output)) {
- if (val != 0) {
- out->devices = val;
- }
+ if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
+ (out == adev->primary_output)) {
stop_voice_call(adev);
- } else if ((out->devices != (audio_devices_t)val) && (val != 0)) {
- /* Update the devices so that select_devices() enables the new devies */
- out->devices = val;
- if ((adev->in_call && (out == adev->primary_output)) ||
- !out->standby) {
- ret = select_devices(adev);
- }
}
pthread_mutex_unlock(&adev->lock);
@@ -1494,9 +1533,8 @@
if ((in->device != val) && (val != 0)) {
in->device = val;
/* If recording is in progress, change the tx device to new device */
- if (!in->standby) {
- ret = select_devices(adev);
- }
+ if (!in->standby)
+ ret = select_devices(adev, in->usecase);
}
}
@@ -1526,10 +1564,9 @@
struct audio_device *adev = in->dev;
int i, ret = -1;
- //ALOGV("%s: buffer(%p) bytes(%d)", __func__, buffer, bytes);
pthread_mutex_lock(&in->lock);
if (in->standby) {
- pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&adev->lock);
ret = start_input_stream(in);
pthread_mutex_unlock(&adev->lock);
if (ret != 0) {
@@ -1715,7 +1752,7 @@
adev->tty_mode = tty_mode;
adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
if (adev->in_call)
- select_devices(adev);
+ select_devices(adev, USECASE_VOICE_CALL);
}
pthread_mutex_unlock(&adev->lock);
}
@@ -1831,6 +1868,7 @@
struct audio_device *adev = (struct audio_device *)dev;
int err = 0;
+ pthread_mutex_lock(&adev->lock);
adev->mic_mute = state;
if (adev->mode == AUDIO_MODE_IN_CALL) {
if (adev->csd_client) {
@@ -1846,6 +1884,7 @@
ALOGE("%s: No CSD Client present", __func__);
}
}
+ pthread_mutex_unlock(&adev->lock);
return err;
}
@@ -1965,31 +2004,31 @@
adev->mic_type_analog = false;
property_get("persist.audio.handset.mic.type",value,"");
- if (!strncmp("analog", value, 6))
+ if (!strcmp("analog", value))
adev->mic_type_analog = true;
property_get("persist.audio.dualmic.config",value,"");
- if (!strncmp("broadside", value, 9)) {
+ if (!strcmp("broadside", value)) {
adev->dualmic_config = DUALMIC_CONFIG_BROADSIDE;
adev->acdb_settings |= DMIC_FLAG;
- } else if (!strncmp("endfire", value, 7)) {
+ } else if (!strcmp("endfire", value)) {
adev->dualmic_config = DUALMIC_CONFIG_ENDFIRE;
adev->acdb_settings |= DMIC_FLAG;
}
if (adev->dualmic_config != DUALMIC_CONFIG_NONE) {
property_get("persist.audio.fluence.voicecall",value,"");
- if (!strncmp("true", value, 4)) {
+ if (!strcmp("true", value)) {
adev->fluence_in_voice_call = true;
}
property_get("persist.audio.fluence.voicerec",value,"");
- if (!strncmp("true", value, 4)) {
+ if (!strcmp("true", value)) {
adev->fluence_in_voice_rec = true;
}
property_get("persist.audio.fluence.speaker",value,"");
- if (!strncmp("true", value, 4)) {
+ if (!strcmp("true", value)) {
adev->fluence_in_spkr_mode = true;
}
}
@@ -2056,7 +2095,7 @@
hw_device_t **device)
{
struct audio_device *adev;
- int ret;
+ int i, ret;
ALOGD("%s: enter", __func__);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
@@ -2111,12 +2150,11 @@
adev->voice_volume = 1.0f;
adev->tty_mode = TTY_MODE_OFF;
adev->bluetooth_nrec = true;
- adev->cur_out_snd_device = 0;
- adev->cur_in_snd_device = 0;
- adev->out_snd_device_active = false;
- adev->in_snd_device_active = false;
adev->in_call = false;
adev->acdb_settings = TTY_MODE_OFF;
+ for (i = 0; i < SND_DEVICE_MAX; i++) {
+ adev->snd_dev_ref_cnt[i] = 0;
+ }
list_init(&adev->usecase_list);
pthread_mutex_unlock(&adev->lock);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 9a47389..0432106 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -27,6 +27,15 @@
#define DUALMIC_CONFIG_ENDFIRE 1
#define DUALMIC_CONFIG_BROADSIDE 2
+/*
+ * Below are the devices for which is back end is same, SLIMBUS_0_RX.
+ * All these devices are handled by the internal HW codec. We can
+ * enable any one of these devices at any time
+ */
+#define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \
+ (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \
+ AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE)
+
/* Sound devices specific to the platform
* The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
* devices to enable corresponding mixer paths
@@ -184,6 +193,8 @@
audio_usecase_t id;
usecase_type_t type;
audio_devices_t devices;
+ snd_device_t out_snd_device;
+ snd_device_t in_snd_device;
union stream_ptr stream;
};
@@ -217,10 +228,7 @@
bool screen_off;
struct pcm *voice_call_rx;
struct pcm *voice_call_tx;
- snd_device_t cur_out_snd_device;
- snd_device_t cur_in_snd_device;
- bool out_snd_device_active;
- bool in_snd_device_active;
+ int snd_dev_ref_cnt[SND_DEVICE_MAX];
struct listnode usecase_list;
struct audio_route *audio_route;
int acdb_settings;