audio: Parse USB data service interval
Parse data service interval for all stream descriptors
and use minimum of them to notify ALSA drivers
Bug: 77867216
Test: Make. manual audio playback capture with/without USB headset
Change-Id: I9bae50d4635b5126bc1dd5783b7a33d142c77342
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 231b159..cdd10b3 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1869,6 +1869,54 @@
return 0;
}
+static int check_and_set_usb_service_interval(struct audio_device *adev,
+ struct audio_usecase *uc_info,
+ bool min)
+{
+ struct listnode *node;
+ struct audio_usecase *usecase;
+ bool switch_usecases = false;
+ bool reconfig = false;
+
+ if ((uc_info->id != USECASE_AUDIO_PLAYBACK_MMAP) &&
+ (uc_info->id != USECASE_AUDIO_PLAYBACK_ULL))
+ return -1;
+
+ /* set if the valid usecase do not already exist */
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type == PCM_PLAYBACK &&
+ (audio_is_usb_out_device(usecase->devices & AUDIO_DEVICE_OUT_ALL_USB))) {
+ switch (usecase->id) {
+ case USECASE_AUDIO_PLAYBACK_MMAP:
+ case USECASE_AUDIO_PLAYBACK_ULL:
+ // cannot reconfig while mmap/ull is present.
+ return -1;
+ default:
+ switch_usecases = true;
+ break;
+ }
+ }
+ if (switch_usecases)
+ break;
+ }
+ /*
+ * client can try to set service interval in start_output_stream
+ * to min or to 0 (i.e reset) in stop_output_stream .
+ */
+ unsigned long service_interval =
+ audio_extn_usb_find_service_interval(min, true /*playback*/);
+ int ret = platform_set_usb_service_interval(adev->platform,
+ true /*playback*/,
+ service_interval,
+ &reconfig);
+ /* no change or not supported or no active usecases */
+ if (ret || !reconfig || !switch_usecases)
+ return -1;
+ return 0;
+#undef VALID_USECASE
+}
+
static int stop_output_stream(struct stream_out *out)
{
int i, ret = 0;
@@ -1901,7 +1949,6 @@
disable_snd_device(adev, uc_info->out_snd_device);
list_remove(&uc_info->list);
- free(uc_info);
audio_extn_extspk_update(adev->extspk);
@@ -1916,8 +1963,17 @@
if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)
select_devices(adev, usecase->id);
}
+ } else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
+ ret = check_and_set_usb_service_interval(adev, uc_info, false /*min*/);
+ if (ret == 0) {
+ /* default service interval was successfully updated,
+ reopen USB backend with new service interval */
+ check_and_route_playback_usecases(adev, uc_info, uc_info->out_snd_device);
+ }
+ ret = 0;
}
+ free(uc_info);
ALOGV("%s: exit: status(%d)", __func__, ret);
return ret;
}
@@ -1971,6 +2027,11 @@
/* This must be called before adding this usecase to the list */
if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
check_and_set_hdmi_channels(adev, out->config.channels);
+ else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
+ check_and_set_usb_service_interval(adev, uc_info, true /*min*/);
+ /* USB backend is not reopened immediately.
+ This is eventually done as part of select_devices */
+ }
list_add_tail(&adev->usecase_list, &uc_info->list);