audio: derive sound device for concurrent playback usecases
When a new playback usecase has to be started, existing playback
usecases might have to be re-routed (or not) to the new device
based on usecase requirements or h/w limitations
(e.g headphones and speaker sharing the same backend).
This change addresses this requirement by deriving the
new device based on pre-defined cases.
Bug: 31671778
Change-Id: Ic0fd4e8d2c1119e7198dc5bb5e5a51817f0110c1
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 8585a76..21ce868 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -619,7 +619,9 @@
ALOGE("%s: spkr_start_processing failed", __func__);
goto on_error;
}
- } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+ } else if (platform_can_split_snd_device(snd_device,
+ &num_devices,
+ new_snd_devices) == 0) {
for (i = 0; i < num_devices; i++) {
enable_snd_device(adev, new_snd_devices[i]);
}
@@ -663,7 +665,9 @@
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
audio_extn_spkr_prot_is_enabled()) {
audio_extn_spkr_prot_stop_processing(snd_device);
- } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+ } else if (platform_can_split_snd_device(snd_device,
+ &num_devices,
+ new_snd_devices) == 0) {
for (i = 0; i < num_devices; i++) {
disable_snd_device(adev, new_snd_devices[i]);
}
@@ -685,6 +689,114 @@
return 0;
}
+/*
+ legend:
+ uc - existing usecase
+ new_uc - new usecase
+ d1, d11, d2 - SND_DEVICE enums
+ a1, a2 - corresponding ANDROID device enums
+ B, B1, B2 - backend strings
+
+case 1
+ uc->dev d1 (a1) B1
+ new_uc->dev d1 (a1), d2 (a2) B1, B2
+
+ resolution: disable and enable uc->dev on d1
+
+case 2
+ uc->dev d1 (a1) B1
+ new_uc->dev d11 (a1) B1
+
+ resolution: need to switch uc since d1 and d11 are related
+ (e.g. speaker and voice-speaker)
+ use ANDROID_DEVICE_OUT enums to match devices since SND_DEVICE enums may vary
+
+case 3
+ uc->dev d1 (a1) B1
+ new_uc->dev d2 (a2) B2
+
+ resolution: no need to switch uc
+
+case 4
+ uc->dev d1 (a1) B
+ new_uc->dev d2 (a2) B
+
+ resolution: disable enable uc-dev on d2 since backends match
+ we cannot enable two streams on two different devices if they
+ share the same backend. e.g. if offload is on speaker device using
+ QUAD_MI2S backend and a low-latency stream is started on voice-handset
+ using the same backend, offload must also be switched to voice-handset.
+
+case 5
+ uc->dev d1 (a1) B
+ new_uc->dev d1 (a1), d2 (a2) B
+
+ resolution: disable enable uc-dev on d2 since backends match
+ we cannot enable two streams on two different devices if they
+ share the same backend.
+
+case 6
+ uc->dev d1 a1 B1
+ new_uc->dev d2 a1 B2
+
+ resolution: no need to switch
+
+case 7
+
+ uc->dev d1 (a1), d2 (a2) B1, B2
+ new_uc->dev d1 B1
+
+ resolution: no need to switch
+
+*/
+static snd_device_t derive_playback_snd_device(struct audio_usecase *uc,
+ struct audio_usecase *new_uc,
+ snd_device_t new_snd_device)
+{
+ audio_devices_t a1 = uc->stream.out->devices;
+ audio_devices_t a2 = new_uc->stream.out->devices;
+
+ snd_device_t d1 = uc->out_snd_device;
+ snd_device_t d2 = new_snd_device;
+
+ // Treat as a special case when a1 and a2 are not disjoint
+ if ((a1 != a2) && (a1 & a2)) {
+ snd_device_t d3[2];
+ int num_devices = 0;
+ int ret = platform_can_split_snd_device(popcount(a1) > 1 ? d1 : d2,
+ &num_devices,
+ d3);
+ if (ret < 0) {
+ if (ret != -ENOSYS) {
+ ALOGW("%s failed to split snd_device %d",
+ __func__,
+ popcount(a1) > 1 ? d1 : d2);
+ }
+ goto end;
+ }
+
+ // NB: case 7 is hypothetical and isn't a practical usecase yet.
+ // But if it does happen, we need to give priority to d2 if
+ // the combo devices active on the existing usecase share a backend.
+ // This is because we cannot have a usecase active on a combo device
+ // and a new usecase requests one device in this combo pair.
+ if (platform_check_backends_match(d3[0], d3[1])) {
+ return d2; // case 5
+ } else {
+ return d1; // case 1
+ }
+ } else {
+ if (platform_check_backends_match(d1, d2)) {
+ return d2; // case 2, 4
+ } else {
+ return d1; // case 6, 3
+ }
+ }
+
+end:
+ return d2; // return whatever was calculated before.
+}
+
static void check_and_route_playback_usecases(struct audio_device *adev,
struct audio_usecase *uc_info,
snd_device_t snd_device)
@@ -733,10 +845,15 @@
}
}
+ snd_device_t d_device;
list_for_each(node, &adev->usecase_list) {
usecase = node_to_item(node, struct audio_usecase, list);
if (switch_device[usecase->id]) {
- enable_snd_device(adev, snd_device);
+ d_device = derive_playback_snd_device(usecase, uc_info,
+ snd_device);
+ enable_snd_device(adev, d_device);
+ /* Update the out_snd_device before enabling the audio route */
+ usecase->out_snd_device = d_device;
}
}
@@ -744,9 +861,7 @@
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);
}
}
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 52e9fd8..72d9591 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -1424,35 +1424,35 @@
return ret;
}
-bool platform_can_split_snd_device(snd_device_t snd_device,
+int platform_can_split_snd_device(snd_device_t snd_device,
int *num_devices,
snd_device_t *new_snd_devices)
{
- bool status = false;
+ int ret = -EINVAL;
if (NULL == num_devices || NULL == new_snd_devices) {
ALOGE("%s: NULL pointer ..", __func__);
- return false;
+ return -EINVAL;
}
/*
* If wired headset/headphones/line devices share the same backend
- * with speaker/earpiece this routine returns false.
+ * with speaker/earpiece this routine -EINVAL.
*/
if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_LINE;
- status = true;
+ ret = 0;
}
- return status;
+ return ret;
}
snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index d7fc84c..94f4621 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1075,11 +1075,11 @@
return true;
}
-bool platform_can_split_snd_device(snd_device_t in_snd_device __unused,
+int platform_can_split_snd_device(snd_device_t in_snd_device __unused,
int *num_devices __unused,
snd_device_t *out_snd_devices __unused)
{
- return false;
+ return -ENOSYS;
}
bool platform_check_backends_match(snd_device_t snd_device1 __unused,
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 5cdc394..a5cfc18 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -1863,47 +1863,46 @@
return ret;
}
-bool platform_can_split_snd_device(snd_device_t snd_device,
- int *num_devices,
- snd_device_t *new_snd_devices)
+int platform_can_split_snd_device(snd_device_t snd_device,
+ int *num_devices,
+ snd_device_t *new_snd_devices)
{
- bool status = false;
-
+ int ret = -EINVAL;
if (NULL == num_devices || NULL == new_snd_devices) {
ALOGE("%s: NULL pointer ..", __func__);
- return false;
+ return -EINVAL;
}
/*
* If wired headset/headphones/line devices share the same backend
- * with speaker/earpiece this routine returns false.
+ * with speaker/earpiece this routine returns -EINVAL.
*/
if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_LINE;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_HEADPHONES)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_LINE)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
new_snd_devices[1] = SND_DEVICE_OUT_LINE;
- status = true;
+ ret = 0;
}
- return status;
+ return ret;
}
snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 41e600e..fbc84c1 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -100,9 +100,9 @@
void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device);
int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels);
-bool platform_can_split_snd_device(snd_device_t in_snd_device,
- int *num_devices,
- snd_device_t *out_snd_devices);
+int platform_can_split_snd_device(snd_device_t in_snd_device,
+ int *num_devices,
+ snd_device_t *out_snd_devices);
bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2);