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.
CRs-Fixed: 1077122
Bug: 31671778
Change-Id: Ic0fd4e8d2c1119e7198dc5bb5e5a51817f0110c1
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 13e4ae9..bf2908f 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -796,8 +796,10 @@
audio_extn_dev_arbi_release(snd_device);
return -EINVAL;
}
- } else if (platform_can_split_snd_device(adev->platform, snd_device,
- &num_devices, new_snd_devices)) {
+ } else if (platform_split_snd_device(adev->platform,
+ snd_device,
+ &num_devices,
+ new_snd_devices) == 0) {
for (i = 0; i < num_devices; i++) {
enable_snd_device(adev, new_snd_devices[i]);
}
@@ -870,8 +872,10 @@
if (platform_can_enable_spkr_prot_on_device(snd_device) &&
audio_extn_spkr_prot_is_enabled()) {
audio_extn_spkr_prot_stop_processing(snd_device);
- } else if (platform_can_split_snd_device(adev->platform, snd_device,
- &num_devices, new_snd_devices)) {
+ } else if (platform_split_snd_device(adev->platform,
+ snd_device,
+ &num_devices,
+ new_snd_devices) == 0) {
for (i = 0; i < num_devices; i++) {
disable_snd_device(adev, new_snd_devices[i]);
}
@@ -908,6 +912,115 @@
return 0;
}
+/*
+ legend:
+ uc - existing usecase
+ new_uc - new usecase
+ d1, d11, d2 - SND_DEVICE enums
+ a1, a2 - corresponding ANDROID device enums
+ 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) B1
+ new_uc->dev d2 (a2) B1
+
+ 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) B1
+ new_uc->dev d1 (a1), d2 (a2) B1
+
+ 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 (a1) B1
+
+ resolution: no need to switch
+
+*/
+static snd_device_t derive_playback_snd_device(void * platform,
+ 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_split_snd_device(platform,
+ 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_usecases_codec_backend(struct audio_device *adev,
struct audio_usecase *uc_info,
snd_device_t snd_device)
@@ -963,7 +1076,9 @@
platform_check_backends_match(snd_device, usecase->out_snd_device));
if (usecase->type != PCM_CAPTURE &&
usecase != uc_info &&
- (usecase->out_snd_device != snd_device || force_routing) &&
+ (derive_playback_snd_device(adev->platform,
+ usecase, uc_info,
+ snd_device) != usecase->out_snd_device || force_routing) &&
((usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
(usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
(usecase->devices & AUDIO_DEVICE_OUT_USB_DEVICE) ||
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index a42f984..fe533ac 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2556,7 +2556,8 @@
snd_device = usecase->in_snd_device;
acdb_dev_id = acdb_device_table[platform_get_spkr_prot_snd_device(snd_device)];
- if(!platform_can_split_snd_device(platform, snd_device, &num_devices, new_snd_device)) {
+ if (platform_split_snd_device(platform, snd_device, &num_devices,
+ new_snd_device) < 0) {
new_snd_device[0] = snd_device;
}
@@ -2858,22 +2859,21 @@
return ret;
}
-bool platform_can_split_snd_device(void *platform,
- snd_device_t snd_device,
- int *num_devices,
- snd_device_t *new_snd_devices)
+int platform_split_snd_device(void *platform,
+ snd_device_t snd_device,
+ int *num_devices,
+ snd_device_t *new_snd_devices)
{
- bool status = false;
+ int ret = -EINVAL;
struct platform_data *my_data = (struct platform_data *)platform;
-
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)) {
@@ -2887,7 +2887,7 @@
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_HDMI &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HDMI)) {
*num_devices = 2;
@@ -2900,7 +2900,7 @@
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_HDMI;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_DISPLAY_PORT &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_DISPLAY_PORT)) {
*num_devices = 2;
@@ -2913,24 +2913,24 @@
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_DISPLAY_PORT;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_USB_HEADSET)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
- status = true;
+ ret = 0;
} else if (SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP == snd_device) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_BT_A2DP;
- status = true;
+ ret = 0;
}
ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__,
snd_device, *num_devices, *new_snd_devices);
- return status;
+ return ret;
}
int platform_get_ext_disp_type(void *platform)
@@ -4753,8 +4753,8 @@
backend_cfg.sample_rate, backend_cfg.channels, backend_idx, usecase->id,
platform_get_snd_device_name(snd_device));
- if (!platform_can_split_snd_device(adev->platform, snd_device,
- &num_devices, new_snd_devices))
+ if (platform_split_snd_device(adev->platform, snd_device,
+ &num_devices, new_snd_devices) < 0)
new_snd_devices[0] = snd_device;
for (i = 0; i < num_devices; i++) {
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index e025772..33bbc2f 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1237,12 +1237,14 @@
int platform_spkr_prot_is_wsa_analog_mode(void *adev __unused)
{
return 0;
-bool platform_can_split_snd_device(void *platform __unused,
- snd_device_t in_snd_device __unused,
- int *num_devices __unused,
- snd_device_t *out_snd_devices __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 e947f91..697888e 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2393,8 +2393,8 @@
return -EINVAL;
}
- if(!platform_can_split_snd_device(my_data, snd_device,
- &num_devices, new_snd_device)) {
+ if (platform_split_snd_device(my_data, snd_device,
+ &num_devices, new_snd_device) < 0) {
new_snd_device[0] = snd_device;
}
@@ -2697,58 +2697,58 @@
return ret;
}
-bool platform_can_split_snd_device(void *platform,
- snd_device_t snd_device,
- int *num_devices,
- snd_device_t *new_snd_devices)
+int platform_split_snd_device(void *platform,
+ snd_device_t snd_device,
+ int *num_devices,
+ snd_device_t *new_snd_devices)
{
- bool status = false;
+ int ret = -EINVAL;
struct platform_data *my_data = (struct platform_data *)platform;
if ( NULL == num_devices || NULL == new_snd_devices || NULL == my_data) {
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_HDMI &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HDMI)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_HDMI;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_DISPLAY_PORT &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_DISPLAY_PORT)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_DISPLAY_PORT;
- status = true;
+ ret = 0;
} else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET &&
!platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_USB_HEADSET)) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
- status = true;
+ ret = 0;
} else if (SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP == snd_device) {
*num_devices = 2;
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
new_snd_devices[1] = SND_DEVICE_OUT_BT_A2DP;
- status = true;
+ ret = 0;
}
ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__,
snd_device, *num_devices, *new_snd_devices);
- return status;
+ return ret;
}
int platform_get_ext_disp_type(void *platform)
@@ -4829,7 +4829,8 @@
backend_cfg.sample_rate, backend_cfg.channels, backend_idx, usecase->id,
platform_get_snd_device_name(snd_device));
- if (!platform_can_split_snd_device(my_data, snd_device, &num_devices, new_snd_devices))
+ if (platform_split_snd_device(my_data, snd_device, &num_devices,
+ new_snd_devices) < 0)
new_snd_devices[0] = snd_device;
for (i = 0; i < num_devices; i++) {
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 61f42de..6e50e72 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -147,10 +147,10 @@
int platform_get_spkr_prot_snd_device(snd_device_t snd_device);
int platform_get_vi_feedback_snd_device(snd_device_t snd_device);
int platform_spkr_prot_is_wsa_analog_mode(void *adev);
-bool platform_can_split_snd_device(void *platform,
- snd_device_t snd_device,
- int *num_devices,
- snd_device_t *new_snd_devices);
+int platform_split_snd_device(void *platform,
+ snd_device_t snd_device,
+ int *num_devices,
+ snd_device_t *new_snd_devices);
bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2);
int platform_set_sidetone(struct audio_device *adev,