audio: unify hal

Unify audio hal components

CRs-Fixed: 2380934
Change-Id: Iacafdc44d935de5f343240421a1572a0a3241bd0
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 375f221..f8c5563 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -55,7 +55,7 @@
 #include <sys/resource.h>
 #include <sys/prctl.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/trace.h>
 #include <cutils/str_parms.h>
 #include <cutils/properties.h>
@@ -93,10 +93,17 @@
 #define PCM_PLAYBACK_VOLUME_MAX 0x2000
 #define DSD_VOLUME_MIN_DB (-110)
 
+#define RECORD_GAIN_MIN 0.0f
+#define RECORD_GAIN_MAX 1.0f
+#define RECORD_VOLUME_CTL_MAX 0x2000
+
+/* treat as unsigned Q1.13 */
+#define APP_TYPE_GAIN_DEFAULT         0x2000
+
 #define PROXY_OPEN_RETRY_COUNT           100
 #define PROXY_OPEN_WAIT_TIME             20
 
-#ifdef USE_LL_AS_PRIMARY_OUTPUT
+#ifndef USE_DEEP_AS_PRIMARY_OUTPUT
 #define USECASE_AUDIO_PLAYBACK_PRIMARY USECASE_AUDIO_PLAYBACK_LOW_LATENCY
 #define PCM_CONFIG_AUDIO_PLAYBACK_PRIMARY pcm_config_low_latency
 #else
@@ -125,6 +132,14 @@
 #define DEFAULT_CHANNEL_COUNT            2
 #define MAX_HIFI_CHANNEL_COUNT           8
 
+#ifndef MAX_TARGET_SPECIFIC_CHANNEL_CNT
+#define MAX_CHANNEL_COUNT 1
+#else
+#define MAX_CHANNEL_COUNT atoi(XSTR(MAX_TARGET_SPECIFIC_CHANNEL_CNT))
+#define XSTR(x) STR(x)
+#define STR(x) #x
+#endif
+
 static unsigned int configured_low_latency_capture_period_size =
         LOW_LATENCY_CAPTURE_PERIOD_SIZE;
 
@@ -133,6 +148,11 @@
 #define MMAP_PERIOD_COUNT_MAX 512
 #define MMAP_PERIOD_COUNT_DEFAULT (MMAP_PERIOD_COUNT_MAX)
 
+/* This constant enables extended precision handling.
+ * TODO The flag is off until more testing is done.
+ */
+static const bool k_enable_extended_precision = false;
+
 struct pcm_config pcm_config_deep_buffer = {
     .channels = 2,
     .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
@@ -210,19 +230,6 @@
     .format = PCM_FORMAT_S16_LE,
 };
 
-struct pcm_config pcm_config_audio_capture_rt = {
-    .channels = 2,
-    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
-    .period_size = ULL_PERIOD_SIZE,
-    .period_count = 512,
-    .format = PCM_FORMAT_S16_LE,
-    .start_threshold = 0,
-    .stop_threshold = INT_MAX,
-    .silence_threshold = 0,
-    .silence_size = 0,
-    .avail_min = ULL_PERIOD_SIZE, //1 ms
-};
-
 struct pcm_config pcm_config_mmap_capture = {
     .channels = 2,
     .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
@@ -256,6 +263,19 @@
 #define AFE_PROXY_RECORD_PERIOD_SIZE  768
 #define AFE_PROXY_RECORD_PERIOD_COUNT 4
 
+struct pcm_config pcm_config_audio_capture_rt = {
+    .channels = 2,
+    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
+    .period_size = ULL_PERIOD_SIZE,
+    .period_count = 512,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
+    .silence_threshold = 0,
+    .silence_size = 0,
+    .avail_min = ULL_PERIOD_SIZE, //1 ms
+};
+
 struct pcm_config pcm_config_afe_proxy_record = {
     .channels = AFE_PROXY_CHANNEL_COUNT,
     .rate = AFE_PROXY_SAMPLING_RATE,
@@ -297,6 +317,7 @@
     [USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
     [USECASE_AUDIO_PLAYBACK_MMAP] = "mmap-playback",
     [USECASE_AUDIO_PLAYBACK_HIFI] = "hifi-playback",
+    [USECASE_AUDIO_PLAYBACK_TTS] = "audio-tts-playback",
 
     [USECASE_AUDIO_RECORD] = "audio-record",
     [USECASE_AUDIO_RECORD_COMPRESS] = "audio-record-compress",
@@ -662,6 +683,29 @@
      return ret_val;
 }
 
+#ifdef MAXXAUDIO_QDSP_ENABLED
+bool audio_hw_send_ma_parameter(int stream_type, float vol, bool active)
+{
+    bool ret = false;
+    ALOGV("%s: enter ...", __func__);
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if (adev != NULL && adev->platform != NULL) {
+        pthread_mutex_lock(&adev->lock);
+        ret = audio_extn_ma_set_state(adev, stream_type, vol, active);
+        pthread_mutex_unlock(&adev->lock);
+    }
+
+    pthread_mutex_unlock(&adev_init_lock);
+
+    ALOGV("%s: exit with ret %d", __func__, ret);
+    return ret;
+}
+#else
+#define audio_hw_send_ma_parameter(stream_type, vol, active) (0)
+#endif
+
 static bool is_supported_format(audio_format_t format)
 {
     if (format == AUDIO_FORMAT_MP3 ||
@@ -1074,6 +1118,8 @@
     if (audio_extn_spkr_prot_is_enabled())
          audio_extn_spkr_prot_calib_cancel(adev);
 
+    audio_extn_dsm_feedback_enable(adev, snd_device, true);
+
     if (platform_can_enable_spkr_prot_on_device(snd_device) &&
          audio_extn_spkr_prot_is_enabled()) {
        if (platform_get_spkr_prot_acdb_id(snd_device) < 0) {
@@ -1093,6 +1139,7 @@
         for (i = 0; i < num_devices; i++) {
             enable_snd_device(adev, new_snd_devices[i]);
         }
+        platform_set_speaker_gain_in_combo(adev, snd_device, true);
     } else {
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name);
 
@@ -1172,6 +1219,8 @@
     if (adev->snd_dev_ref_cnt[snd_device] == 0) {
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name);
 
+        audio_extn_dsm_feedback_enable(adev, snd_device, false);
+
         if (platform_can_enable_spkr_prot_on_device(snd_device) &&
              audio_extn_spkr_prot_is_enabled()) {
             audio_extn_spkr_prot_stop_processing(snd_device);
@@ -1186,6 +1235,7 @@
             for (i = 0; i < num_devices; i++) {
                 disable_snd_device(adev, new_snd_devices[i]);
             }
+            platform_set_speaker_gain_in_combo(adev, snd_device, false);
         } else {
             audio_route_reset_and_update_path(adev->audio_route, device_name);
         }
@@ -2008,6 +2058,11 @@
     return ret;
 }
 
+static void stream_app_type_cfg_init(struct stream_app_type_cfg *cfg)
+{
+    cfg->gain[0] = cfg->gain[1] = APP_TYPE_GAIN_DEFAULT;
+}
+
 bool is_btsco_device(snd_device_t out_snd_device, snd_device_t in_snd_device)
 {
    bool ret=false;
@@ -2171,7 +2226,8 @@
                      voip_usecase != NULL &&
                      usecase->stream.out->usecase == voip_usecase->id) &&
                     adev->active_input &&
-                    adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
+                    (adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
+                     adev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
                     out_snd_device != usecase->out_snd_device) {
                     select_devices(adev, adev->active_input->usecase);
                 }
@@ -2216,9 +2272,37 @@
           return 0;
     }
 
-    ALOGD("%s: out_snd_device(%d: %s) in_snd_device(%d: %s)", __func__,
-          out_snd_device, platform_get_snd_device_name(out_snd_device),
-          in_snd_device,  platform_get_snd_device_name(in_snd_device));
+    if (out_snd_device != SND_DEVICE_NONE &&
+            out_snd_device != adev->last_logged_snd_device[uc_id][0]) {
+        ALOGD("%s: changing use case %s output device from(%d: %s, acdb %d) to (%d: %s, acdb %d)",
+              __func__,
+              use_case_table[uc_id],
+              adev->last_logged_snd_device[uc_id][0],
+              platform_get_snd_device_name(adev->last_logged_snd_device[uc_id][0]),
+              adev->last_logged_snd_device[uc_id][0] != SND_DEVICE_NONE ?
+                      platform_get_snd_device_acdb_id(adev->last_logged_snd_device[uc_id][0]) :
+                      -1,
+              out_snd_device,
+              platform_get_snd_device_name(out_snd_device),
+              platform_get_snd_device_acdb_id(out_snd_device));
+        adev->last_logged_snd_device[uc_id][0] = out_snd_device;
+    }
+    if (in_snd_device != SND_DEVICE_NONE &&
+            in_snd_device != adev->last_logged_snd_device[uc_id][1]) {
+        ALOGD("%s: changing use case %s input device from(%d: %s, acdb %d) to (%d: %s, acdb %d)",
+              __func__,
+              use_case_table[uc_id],
+              adev->last_logged_snd_device[uc_id][1],
+              platform_get_snd_device_name(adev->last_logged_snd_device[uc_id][1]),
+              adev->last_logged_snd_device[uc_id][1] != SND_DEVICE_NONE ?
+                      platform_get_snd_device_acdb_id(adev->last_logged_snd_device[uc_id][1]) :
+                      -1,
+              in_snd_device,
+              platform_get_snd_device_name(in_snd_device),
+              platform_get_snd_device_acdb_id(in_snd_device));
+        adev->last_logged_snd_device[uc_id][1] = in_snd_device;
+    }
+
 
     /*
      * Limitation: While in call, to do a device switch we need to disable
@@ -2244,10 +2328,14 @@
             voice_check_and_update_aanc_path(adev, usecase->out_snd_device, false);
     }
 
-    if ((out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP) &&
+    if ((out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP ||
+         out_snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP) &&
         (!audio_extn_a2dp_source_is_ready())) {
         ALOGW("%s: A2DP profile is not ready, routing to speaker only", __func__);
-        out_snd_device = SND_DEVICE_OUT_SPEAKER;
+        if (out_snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)
+            out_snd_device = SND_DEVICE_OUT_SPEAKER_SAFE;
+        else
+            out_snd_device = SND_DEVICE_OUT_SPEAKER;
     }
 
     /* Disable current sound devices */
@@ -2329,6 +2417,8 @@
     }
     enable_audio_route(adev, usecase);
 
+    audio_extn_ma_set_device(usecase);
+
     /* If input stream is already running then effect needs to be
        applied on the new input device that's being enabled here.  */
     if ((in_snd_device != SND_DEVICE_NONE) && (adev->active_input != NULL) &&
@@ -2395,6 +2485,13 @@
         pthread_mutex_lock(&adev->lock);
     }
 
+    if (usecase == voip_usecase) {
+        struct stream_out *voip_out = voip_usecase->stream.out;
+        audio_extn_utils_send_app_type_gain(adev,
+                                            voip_out->app_type_cfg.app_type,
+                                            &voip_out->app_type_cfg.gain[0]);
+    }
+
     ALOGD("%s: done",__func__);
 
     return status;
@@ -2944,6 +3041,8 @@
     int ret = 0;
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
+    bool has_voip_usecase =
+        get_usecase_from_list(adev, USECASE_AUDIO_PLAYBACK_VOIP) != NULL;
 
     ALOGV("%s: enter: usecase(%d: %s)", __func__,
           out->usecase, use_case_table[out->usecase]);
@@ -2979,6 +3078,8 @@
     /* 2. Disable the rx device */
     disable_snd_device(adev, uc_info->out_snd_device);
 
+    audio_extn_extspk_update(adev->extspk);
+
     if (is_offload_usecase(out->usecase)) {
         audio_enable_asm_bit_width_enforce_mode(adev->mixer,
                                                 adev->dsp_bit_width_enforce_mode,
@@ -3016,6 +3117,22 @@
             ALOGE("%s: audio_extn_ip_hdlr_intf_close failed %d",__func__, ret);
     }
 
+    if (has_voip_usecase ||
+            out->devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
+        struct listnode *node;
+        struct audio_usecase *usecase;
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->type == PCM_CAPTURE || usecase == uc_info)
+                continue;
+
+            ALOGD("%s: select_devices at usecase(%d: %s) after removing the usecase(%d: %s)",
+                __func__, usecase->id, use_case_table[usecase->id],
+                out->usecase, use_case_table[out->usecase]);
+            select_devices(adev, usecase->id);
+        }
+    }
+
     free(uc_info);
     ALOGV("%s: exit: status(%d)", __func__, ret);
     return ret;
@@ -3050,7 +3167,8 @@
 
     if (out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
         if (!audio_extn_a2dp_source_is_ready()) {
-            if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+            if (out->devices &
+                (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
                 a2dp_combo = true;
             } else {
                 if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
@@ -3124,7 +3242,10 @@
             check_a2dp_restore_l(adev, out, false);
         } else {
             audio_devices_t dev = out->devices;
-            out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+            if (dev & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+            else
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
             select_devices(adev, out->usecase);
             out->devices = dev;
         }
@@ -3355,7 +3476,8 @@
 
 static int check_input_parameters(uint32_t sample_rate,
                                   audio_format_t format,
-                                  int channel_count)
+                                  int channel_count,
+                                  bool is_usb_hifi)
 {
     int ret = 0;
 
@@ -3367,6 +3489,13 @@
         !audio_extn_cin_format_supported(format))
             ret = -EINVAL;
 
+    int max_channel_count = is_usb_hifi ? MAX_HIFI_CHANNEL_COUNT : MAX_CHANNEL_COUNT;
+    if ((channel_count < MIN_CHANNEL_COUNT) || (channel_count > max_channel_count)) {
+        ALOGE("%s: unsupported channel count (%d) passed  Min / Max (%d / %d)", __func__,
+               channel_count, MIN_CHANNEL_COUNT, max_channel_count);
+        return -EINVAL;
+    }
+
     switch (channel_count) {
     case 1:
     case 2:
@@ -3480,23 +3609,21 @@
     return num;
 }
 
-static size_t get_input_buffer_size(uint32_t sample_rate,
-                                    audio_format_t format,
-                                    int channel_count,
-                                    bool is_low_latency)
+static size_t get_stream_buffer_size(size_t duration_ms,
+                                     uint32_t sample_rate,
+                                     audio_format_t format,
+                                     int channel_count,
+                                     bool is_low_latency)
 {
     size_t size = 0;
     uint32_t bytes_per_period_sample = 0;
 
-    if (check_input_parameters(sample_rate, format, channel_count) != 0)
-        return 0;
-
-    size = (sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) / 1000;
+    size = (sample_rate * duration_ms) / 1000;
     if (is_low_latency)
         size = configured_low_latency_capture_period_size;
 
     bytes_per_period_sample = audio_bytes_per_sample(format) * channel_count;
-    size *= bytes_per_period_sample;
+    size *= audio_bytes_per_sample(format) * channel_count;
 
     /* make sure the size is multiple of 32 bytes and additionally multiple of
      * the frame_size (required for 24bit samples and non-power-of-2 channel counts)
@@ -3510,6 +3637,23 @@
     return size;
 }
 
+static size_t get_input_buffer_size(uint32_t sample_rate,
+                                    audio_format_t format,
+                                    int channel_count,
+                                    bool is_low_latency)
+{
+    /* Don't know if USB HIFI in this context so use true to be conservative */
+    if (check_input_parameters(sample_rate, format, channel_count,
+                               true /*is_usb_hifi */) != 0)
+        return 0;
+
+    return get_stream_buffer_size(AUDIO_CAPTURE_PERIOD_DURATION_MSEC,
+                                  sample_rate,
+                                  format,
+                                  channel_count,
+                                  is_low_latency);
+}
+
 static size_t get_output_period_size(uint32_t sample_rate,
                                     audio_format_t format,
                                     int channel_count,
@@ -3764,9 +3908,25 @@
     return 0;
 }
 
-static int out_dump(const struct audio_stream *stream __unused,
-                    int fd __unused)
+static int out_dump(const struct audio_stream *stream, int fd)
 {
+    struct stream_out *out = (struct stream_out *)stream;
+
+    // We try to get the lock for consistency,
+    // but it isn't necessary for these variables.
+    // If we're not in standby, we may be blocked on a write.
+    const bool locked = (pthread_mutex_trylock(&out->lock) == 0);
+    dprintf(fd, "      Standby: %s\n", out->standby ? "yes" : "no");
+    dprintf(fd, "      Frames written: %lld\n", (long long)out->written);
+
+    if (locked) {
+        pthread_mutex_unlock(&out->lock);
+    }
+
+    // dump error info
+    (void)error_log_dump(
+            out->error_log, fd, "      " /* prefix */, 0 /* lines */, 0 /* limit_ns */);
+
     return 0;
 }
 
@@ -3912,7 +4072,8 @@
          */
         if (val & AUDIO_DEVICE_OUT_ALL_A2DP) {
             if (!audio_extn_a2dp_source_is_ready()) {
-                if (val & AUDIO_DEVICE_OUT_SPEAKER) {
+                if (val &
+                    (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
                     //combo usecase just by pass a2dp
                     ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
                     bypass_a2dp = true;
@@ -4000,7 +4161,10 @@
                 if (!bypass_a2dp) {
                     select_devices(adev, out->usecase);
                 } else {
-                    out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                    if (new_dev & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+                        out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+                    else
+                        out->devices = AUDIO_DEVICE_OUT_SPEAKER;
                     select_devices(adev, out->usecase);
                     out->devices = new_dev;
                 }
@@ -4026,6 +4190,9 @@
 
         pthread_mutex_unlock(&adev->lock);
         pthread_mutex_unlock(&out->lock);
+
+        /*handles device and call state changes*/
+        audio_extn_extspk_update(adev->extspk);
     }
     routing_fail:
 
@@ -4504,8 +4671,14 @@
             return ret;
         }
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
-        if (!out->standby)
+        out->app_type_cfg.gain[0] = (int)(left * VOIP_PLAYBACK_VOLUME_MAX);
+        out->app_type_cfg.gain[1] = (int)(right * VOIP_PLAYBACK_VOLUME_MAX);
+        if (!out->standby) {
+            audio_extn_utils_send_app_type_gain(out->dev,
+                                                out->app_type_cfg.app_type,
+                                                &out->app_type_cfg.gain[0]);
             ret = out_set_voip_volume(stream, left, right);
+        }
         out->volume_l = left;
         out->volume_r = right;
         return ret;
@@ -4545,6 +4718,24 @@
     pthread_mutex_unlock(&out->position_query_lock);
 }
 
+#ifdef NO_AUDIO_OUT
+static ssize_t out_write_for_no_output(struct audio_stream_out *stream,
+                                       const void *buffer __unused, size_t bytes)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+
+    /* No Output device supported other than BT for playback.
+     * Sleep for the amount of buffer duration
+     */
+    lock_output_stream(out);
+    usleep(bytes * 1000000 / audio_stream_out_frame_size(
+            (const struct audio_stream_out *)&out->stream) /
+            out_get_sample_rate(&out->stream.common));
+    pthread_mutex_unlock(&out->lock);
+    return bytes;
+}
+#endif
+
 static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
                          size_t bytes)
 {
@@ -4632,7 +4823,8 @@
 
     if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
         (audio_extn_a2dp_source_is_suspended())) {
-        if (!(out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
+        if (!(out->devices &
+            (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE))) {
             if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
                 ret = -EIO;
                 goto exit;
@@ -4777,14 +4969,19 @@
             ALOGV("%s: frames=%zu, frame_size=%zu, bytes_to_write=%zu",
                      __func__, frames, frame_size, bytes_to_write);
 
-            if (out->usecase == USECASE_INCALL_MUSIC_UPLINK) {
+            if (out->usecase == USECASE_INCALL_MUSIC_UPLINK ||
+#ifndef COMPRESS_VOIP_ENABLED
+                out->usecase == USECASE_AUDIO_PLAYBACK_VOIP ||
+#endif
+                out->usecase == USECASE_INCALL_MUSIC_UPLINK2) {
                 size_t channel_count = audio_channel_count_from_out_mask(out->channel_mask);
                 int16_t *src = (int16_t *)buffer;
                 int16_t *dst = (int16_t *)buffer;
 
                 LOG_ALWAYS_FATAL_IF(out->config.channels != 1 || channel_count != 2 ||
                                     out->format != AUDIO_FORMAT_PCM_16_BIT,
-                                    "out_write called for incall music use case with wrong properties");
+                                    "out_write called for %s use case with wrong properties",
+                                    use_case_table[out->usecase]);
 
                 /*
                  * FIXME: this can be removed once audio flinger mixer supports
@@ -5446,6 +5643,8 @@
                 ATRACE_END();
                 in->pcm = NULL;
             }
+            adev->enable_voicerx = false;
+            platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE);
             status = stop_input_stream(in);
         }
         pthread_mutex_unlock(&adev->lock);
@@ -5455,9 +5654,27 @@
     return status;
 }
 
-static int in_dump(const struct audio_stream *stream __unused,
-                   int fd __unused)
+static int in_dump(const struct audio_stream *stream,
+                   int fd)
 {
+    struct stream_in *in = (struct stream_in *)stream;
+
+    // We try to get the lock for consistency,
+    // but it isn't necessary for these variables.
+    // If we're not in standby, we may be blocked on a read.
+    const bool locked = (pthread_mutex_trylock(&in->lock) == 0);
+    dprintf(fd, "      Standby: %s\n", in->standby ? "yes" : "no");
+    dprintf(fd, "      Frames read: %lld\n", (long long)in->frames_read);
+    dprintf(fd, "      Frames muted: %lld\n", (long long)in->frames_muted);
+
+    if (locked) {
+        pthread_mutex_unlock(&in->lock);
+    }
+
+    // dump error info
+    (void)error_log_dump(
+            in->error_log, fd, "      " /* prefix */, 0 /* lines */, 0 /* limit_ns */);
+
     return 0;
 }
 
@@ -5622,9 +5839,40 @@
     return str;
 }
 
-static int in_set_gain(struct audio_stream_in *stream __unused,
-                       float gain __unused)
+static int in_set_gain(struct audio_stream_in *stream,
+                       float gain)
 {
+    struct stream_in *in = (struct stream_in *)stream;
+    char mixer_ctl_name[128];
+    struct mixer_ctl *ctl;
+    int ctl_value;
+
+    ALOGV("%s: gain %f", __func__, gain);
+
+    if (stream == NULL)
+        return -EINVAL;
+
+    /* in_set_gain() only used to silence MMAP capture for now */
+    if (in->usecase != USECASE_AUDIO_RECORD_MMAP)
+        return -ENOSYS;
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "Capture %d Volume", in->pcm_device_id);
+
+    ctl = mixer_get_ctl_by_name(in->dev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGW("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -ENOSYS;
+    }
+
+    if (gain < RECORD_GAIN_MIN)
+        gain  = RECORD_GAIN_MIN;
+    else if (gain > RECORD_GAIN_MAX)
+         gain = RECORD_GAIN_MAX;
+    ctl_value = (int)(RECORD_VOLUME_CTL_MAX * gain);
+
+    mixer_ctl_set_value(ctl, 0, ctl_value);
+
     return 0;
 }
 
@@ -5747,6 +5995,38 @@
     return 0;
 }
 
+static int in_get_capture_position(const struct audio_stream_in *stream,
+                                   int64_t *frames, int64_t *time)
+{
+    if (stream == NULL || frames == NULL || time == NULL) {
+        return -EINVAL;
+    }
+    struct stream_in *in = (struct stream_in *)stream;
+    int ret = -ENOSYS;
+
+    lock_input_stream(in);
+    // note: ST sessions do not close the alsa pcm driver synchronously
+    // on standby. Therefore, we may return an error even though the
+    // pcm stream is still opened.
+    if (in->standby) {
+        ALOGE_IF(in->pcm != NULL && !in->is_st_session,
+                 "%s stream in standby but pcm not NULL for non ST session", __func__);
+        goto exit;
+    }
+    if (in->pcm) {
+        struct timespec timestamp;
+        unsigned int avail;
+        if (pcm_get_htimestamp(in->pcm, &avail, &timestamp) == 0) {
+            *frames = in->frames_read + avail;
+            *time = timestamp.tv_sec * 1000000000LL + timestamp.tv_nsec;
+            ret = 0;
+        }
+    }
+exit:
+    pthread_mutex_unlock(&in->lock);
+    return ret;
+}
+
 static int add_remove_audio_effect(const struct audio_stream *stream,
                                    effect_handle_t effect,
                                    bool enable)
@@ -5764,10 +6044,24 @@
     lock_input_stream(in);
     pthread_mutex_lock(&in->dev->lock);
     if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
+         in->source == AUDIO_SOURCE_VOICE_RECOGNITION ||
          in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
             in->enable_aec != enable &&
             (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0)) {
         in->enable_aec = enable;
+        if (!enable)
+            platform_set_echo_reference(in->dev, enable, AUDIO_DEVICE_NONE);
+        if (in->source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
+            in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+            in->dev->enable_voicerx = enable;
+            struct audio_usecase *usecase;
+            struct listnode *node;
+            list_for_each(node, &in->dev->usecase_list) {
+                usecase = node_to_item(node, struct audio_usecase, list);
+                if (usecase->type == PCM_PLAYBACK)
+                    select_devices(in->dev, usecase->id);
+            }
+        }
         if (!in->standby) {
             if (enable_disable_effect(in->dev, EFFECT_AEC, enable) == ENOSYS)
                 select_devices(in->dev, in->usecase);
@@ -6069,13 +6363,25 @@
         (property_get_bool("vendor.audio.matrix.limiter.enable", false)))
         platform_set_device_params(out, DEVICE_PARAM_LIMITER_ID, 1);
 
-    if (audio_is_linear_pcm(out->format) &&
-        out->flags == AUDIO_OUTPUT_FLAG_NONE && direct_dev) {
-       pthread_mutex_lock(&adev->lock);
-       if (is_hdmi) {
-           ALOGV("AUDIO_DEVICE_OUT_AUX_DIGITAL and DIRECT|OFFLOAD, check hdmi caps");
-           ret = read_hdmi_sink_caps(out);
-       } else if (is_usb_dev) {
+    if (direct_dev &&
+        (audio_is_linear_pcm(out->format) ||
+         config->format == AUDIO_FORMAT_DEFAULT) &&
+        out->flags == AUDIO_OUTPUT_FLAG_NONE) {
+        audio_format_t req_format = config->format;
+        audio_channel_mask_t req_channel_mask = config->channel_mask;
+        uint32_t req_sample_rate = config->sample_rate;
+
+        pthread_mutex_lock(&adev->lock);
+        if (is_hdmi) {
+            ALOGV("AUDIO_DEVICE_OUT_AUX_DIGITAL and DIRECT|OFFLOAD, check hdmi caps");
+            ret = read_hdmi_sink_caps(out);
+            if (config->sample_rate == 0)
+                config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+            if (config->channel_mask == AUDIO_CHANNEL_NONE)
+                config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+            if (config->format == AUDIO_FORMAT_DEFAULT)
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+        } else if (is_usb_dev) {
             ret = read_usb_sup_params_and_compare(true /*is_playback*/,
                                                   &config->format,
                                                   &out->supported_formats[0],
@@ -6087,19 +6393,55 @@
                                                   &out->supported_sample_rates[0],
                                                   MAX_SUPPORTED_SAMPLE_RATES);
             ALOGV("plugged dev USB ret %d", ret);
-       } else {
-           ret = -1;
        }
+
        pthread_mutex_unlock(&adev->lock);
        if (ret != 0) {
             if (ret == -ENOSYS) {
                 /* ignore and go with default */
                 ret = 0;
-            } else {
+            }
+            // For MMAP NO IRQ, allow conversions in ADSP
+            else if (is_hdmi || (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) == 0)
+                goto error_open;
+            else {
                 ALOGE("error reading direct dev sink caps");
                 goto error_open;
             }
+
+            if (req_sample_rate != 0 && config->sample_rate != req_sample_rate)
+                config->sample_rate = req_sample_rate;
+            if (req_channel_mask != AUDIO_CHANNEL_NONE && config->channel_mask != req_channel_mask)
+                config->channel_mask = req_channel_mask;
+            if (req_format != AUDIO_FORMAT_DEFAULT && config->format != req_format)
+                config->format = req_format;
         }
+
+        out->sample_rate = config->sample_rate;
+        out->channel_mask = config->channel_mask;
+        out->format = config->format;
+        if (is_hdmi) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
+            out->config = pcm_config_hdmi_multi;
+        } else if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_MMAP;
+            out->config = pcm_config_mmap_playback;
+            out->stream.start = out_start;
+            out->stream.stop = out_stop;
+            out->stream.create_mmap_buffer = out_create_mmap_buffer;
+            out->stream.get_mmap_position = out_get_mmap_position;
+        } else {
+            out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
+            out->config = pcm_config_hifi;
+        }
+
+        out->config.rate = out->sample_rate;
+        out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
+        if (is_hdmi) {
+            out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels *
+                                                         audio_bytes_per_sample(out->format));
+        }
+        out->config.format = pcm_format_from_audio_format(out->format);
     }
 
     /* Init use case and pcm_config */
@@ -6107,9 +6449,10 @@
     if (out->flags == (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_VOIP_RX) &&
         (out->sample_rate == 8000 || out->sample_rate == 16000 ||
          out->sample_rate == 32000 || out->sample_rate == 48000)) {
-        out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO;
-        out->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+        //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+        out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
         out->usecase = USECASE_AUDIO_PLAYBACK_VOIP;
+        out->format = AUDIO_FORMAT_PCM_16_BIT;
 
         out->config = default_pcm_config_voip_copp;
         out->config.period_size = VOIP_IO_BUF_SIZE(out->sample_rate, DEFAULT_VOIP_BUF_DURATION_MS, DEFAULT_VOIP_BIT_DEPTH_BYTE)/2;
@@ -6442,26 +6785,60 @@
             goto error_open;
         }
     } else if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
-        if (config->sample_rate == 0)
-            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
-        if (config->sample_rate != 48000 && config->sample_rate != 16000 &&
-                config->sample_rate != 8000) {
-            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
-            ret = -EINVAL;
-            goto error_open;
+        switch (config->sample_rate) {
+            case 0:
+                out->sample_rate = AFE_PROXY_SAMPLING_RATE;
+                break;
+            case 8000:
+            case 16000:
+            case 48000:
+                out->sample_rate = config->sample_rate;
+                break;
+            default:
+                ALOGE("%s: Unsupported sampling rate %d for Telephony TX", __func__,
+                      config->sample_rate);
+                config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+                ret = -EINVAL;
+                break;
         }
-        out->sample_rate = config->sample_rate;
-        out->config.rate = config->sample_rate;
-        if (config->format == AUDIO_FORMAT_DEFAULT)
-            config->format = AUDIO_FORMAT_PCM_16_BIT;
-        if (config->format != AUDIO_FORMAT_PCM_16_BIT) {
-            config->format = AUDIO_FORMAT_PCM_16_BIT;
-            ret = -EINVAL;
-            goto error_open;
+        //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+        switch (config->channel_mask) {
+            case AUDIO_CHANNEL_NONE:
+                out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                break;
+            case AUDIO_CHANNEL_OUT_STEREO:
+                out->channel_mask = config->channel_mask;
+                break;
+            default:
+                ALOGE("%s: Unsupported channel mask %#x for Telephony TX", __func__,
+                      config->channel_mask);
+                config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                ret = -EINVAL;
+                break;
         }
-        out->format = config->format;
+        switch (config->format) {
+            case AUDIO_FORMAT_DEFAULT:
+                out->format = AUDIO_FORMAT_PCM_16_BIT;
+                break;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                out->format = config->format;
+                break;
+            default:
+                ALOGE("%s: Unsupported format %#x for Telephony TX", __func__,
+                      config->format);
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+                ret = -EINVAL;
+                break;
+        }
+        if (ret != 0)
+            goto error_open;
+
         out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;
         out->config = pcm_config_afe_proxy_playback;
+        out->config.rate = out->sample_rate;
+        out->config.channels =
+                audio_channel_count_from_out_mask(out->channel_mask);
+        out->config.format = pcm_format_from_audio_format(out->format);
         adev->voice_tx_output = out;
     } else {
         unsigned int channels = 0;
@@ -6518,6 +6895,9 @@
                 ret = -EINVAL;
                 goto error_open;
             }
+        } else if (flags & AUDIO_OUTPUT_FLAG_TTS) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_TTS;
+            out->config = pcm_config_deep_buffer;
         } else {
             /* primary path is the default path selected if no other outputs are available/suitable */
             out->usecase = USECASE_AUDIO_PLAYBACK_PRIMARY;
@@ -6594,7 +6974,11 @@
     out->stream.common.remove_audio_effect = out_remove_audio_effect;
     out->stream.get_latency = out_get_latency;
     out->stream.set_volume = out_set_volume;
+#ifdef NO_AUDIO_OUT
+    out->stream.write = out_write_for_no_output;
+#else
     out->stream.write = out_write;
+#endif
     out->stream.get_render_position = out_get_render_position;
     out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
     out->stream.get_presentation_position = out_get_presentation_position;
@@ -6615,6 +6999,10 @@
     register_channel_mask(out->channel_mask, out->supported_channel_masks);
     register_sample_rate(out->sample_rate, out->supported_sample_rates);
 
+    out->error_log = error_log_create(
+            ERROR_LOG_ENTRIES,
+            1000000000 /* aggregate consecutive identical errors within one second in ns */);
+
     /*
        By locking output stream before registering, we allow the callback
        to update stream's state only after stream's initial state is set to
@@ -6627,6 +7015,8 @@
     pthread_mutex_unlock(&adev->lock);
     pthread_mutex_unlock(&out->lock);
 
+    stream_app_type_cfg_init(&out->app_type_cfg);
+
     *stream_out = &out->stream;
     ALOGD("%s: Stream (%p) picks up usecase (%s)", __func__, &out->stream,
            use_case_table[out->usecase]);
@@ -6727,6 +7117,9 @@
     if (adev->voice_tx_output == out)
         adev->voice_tx_output = NULL;
 
+    error_log_destroy(out->error_log);
+    out->error_log = NULL;
+
     if (adev->primary_output == out)
         adev->primary_output = NULL;
 
@@ -6744,6 +7137,7 @@
     int val;
     int ret;
     int status = 0;
+    bool a2dp_reconfig = false;
 
     ALOGD("%s: enter: %s", __func__, kvpairs);
     parms = str_parms_create_str(kvpairs);
@@ -6786,6 +7180,7 @@
             adev->screen_off = true;
     }
 
+#ifndef MAXXAUDIO_QDSP_ENABLED
     ret = str_parms_get_int(parms, "rotation", &val);
     if (ret >= 0) {
         bool reverse_speakers = false;
@@ -6811,6 +7206,7 @@
             platform_check_and_set_swap_lr_channels(adev, reverse_speakers);
         }
     }
+#endif
 
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
     if (ret >= 0) {
@@ -6874,8 +7270,11 @@
         }
     }
 
-    ret = str_parms_get_str(parms,"reconfigA2dp", value, sizeof(value));
-    if (ret >= 0) {
+    audio_extn_hfp_set_parameters(adev, parms);
+    audio_extn_ma_set_parameters(adev, parms);
+
+    status = audio_extn_a2dp_set_parameters(parms, &a2dp_reconfig);
+    if (ret >= 0 && a2dp_reconfig) {
         struct audio_usecase *usecase;
         struct listnode *node;
         list_for_each(node, &adev->usecase_list) {
@@ -6973,6 +7372,7 @@
     pthread_mutex_lock(&adev->lock);
     audio_extn_get_parameters(adev, query, reply);
     voice_get_parameters(adev, query, reply);
+    audio_extn_a2dp_get_parameters(query, reply);
     platform_get_parameters(adev->platform, query, reply);
     pthread_mutex_unlock(&adev->lock);
 
@@ -6994,6 +7394,9 @@
 {
     int ret;
     struct audio_device *adev = (struct audio_device *)dev;
+
+    audio_extn_extspk_set_voice_vol(adev->extspk, volume);
+
     pthread_mutex_lock(&adev->lock);
     /* cache volume */
     ret = voice_set_volume(adev, volume);
@@ -7066,12 +7469,16 @@
 static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
 {
     int ret;
+    struct audio_device *adev = (struct audio_device *)dev;
 
     pthread_mutex_lock(&adev->lock);
     ALOGD("%s state %d\n", __func__, state);
     ret = voice_set_mic_mute((struct audio_device *)dev, state);
+
     if (adev->ext_hw_plugin)
         ret = audio_extn_ext_hw_plugin_set_mic_mute(adev->ext_hw_plugin, state);
+
+    adev->mic_muted = state;
     pthread_mutex_unlock(&adev->lock);
 
     return ret;
@@ -7088,6 +7495,11 @@
 {
     int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
 
+    /* Don't know if USB HIFI in this context so use true to be conservative */
+    if (check_input_parameters(config->sample_rate, config->format, channel_count,
+                               true /*is_usb_hifi */) != 0)
+        return 0;
+
     return get_input_buffer_size(config->sample_rate, config->format, channel_count,
             false /* is_low_latency: since we don't know, be conservative */);
 }
@@ -7211,7 +7623,8 @@
 
         channel_count = audio_channel_count_from_in_mask(config->channel_mask);
 
-        if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
+        if (check_input_parameters(config->sample_rate, config->format, channel_count,
+                                   false) != 0)
             return -EINVAL;
     }
 
@@ -7243,6 +7656,7 @@
     in->stream.set_gain = in_set_gain;
     in->stream.read = in_read;
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
+    in->stream.get_capture_position = in_get_capture_position;
     in->stream.get_active_microphones = in_get_active_microphones;
 
     in->device = devices;
@@ -7254,6 +7668,18 @@
     in->bit_width = 16;
     in->af_period_multiplier = 1;
 
+    ALOGV("%s: source = %d, config->channel_mask = %d", __func__, source, config->channel_mask);
+    if (source == AUDIO_SOURCE_VOICE_UPLINK ||
+        source == AUDIO_SOURCE_VOICE_DOWNLINK) {
+        /* Force channel config requested to mono if incall
+           record is being requested for only uplink/downlink */
+        if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) {
+            config->channel_mask = AUDIO_CHANNEL_IN_MONO;
+            ret = -EINVAL;
+            goto err_open;
+        }
+    }
+
     /* Update config params with the requested sample rate and channels */
     if ((in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) &&
           (adev->mode != AUDIO_MODE_IN_CALL)) {
@@ -7329,6 +7755,21 @@
         in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY;
 #endif
         in->realtime = may_use_noirq_mode(adev, in->usecase, in->flags);
+        if (!in->realtime) {
+            in->config = pcm_config_audio_capture;
+            frame_size = audio_stream_in_frame_size(&in->stream);
+            buffer_size = get_input_buffer_size(config->sample_rate,
+                                                config->format,
+                                                channel_count,
+                                                is_low_latency);
+            in->config.period_size = buffer_size / frame_size;
+            in->config.rate = config->sample_rate;
+            in->af_period_multiplier = 1;
+        } else {
+            // period size is left untouched for rt mode playback
+            in->config = pcm_config_audio_capture_rt;
+            in->af_period_multiplier = af_period_multiplier;
+        }
     }
 
     if ((config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE) &&
@@ -7397,6 +7838,25 @@
         in->config.channels = channel_count;
         in->config.rate = config->sample_rate;
         in->sample_rate = config->sample_rate;
+        in->af_period_multiplier = 1;
+    } else if (in->source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
+               in->flags & AUDIO_INPUT_FLAG_VOIP_TX &&
+               (config->sample_rate == 8000 ||
+                config->sample_rate == 16000 ||
+                config->sample_rate == 32000 ||
+                config->sample_rate == 48000) &&
+               channel_count == 1) {
+        in->usecase = USECASE_AUDIO_RECORD_VOIP;
+        in->config = pcm_config_audio_capture;
+        frame_size = audio_stream_in_frame_size(&in->stream);
+        buffer_size = get_stream_buffer_size(VOIP_CAPTURE_PERIOD_DURATION_MSEC,
+                                             config->sample_rate,
+                                             config->format,
+                                             channel_count, false /*is_low_latency*/);
+        in->config.period_size = buffer_size / frame_size;
+        in->config.period_count = VOIP_CAPTURE_PERIOD_COUNT;
+        in->config.rate = config->sample_rate;
+        in->af_period_multiplier = 1;
     } else {
         int ret_val;
         pthread_mutex_lock(&adev->lock);
@@ -7441,6 +7901,7 @@
             }
 
             in->config.period_size = buffer_size / frame_size;
+            in->af_period_multiplier = 1;
 
             if (in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
                 /* optionally use VOIP usecase depending on config(s) */
@@ -7462,6 +7923,10 @@
     register_channel_mask(in->channel_mask, in->supported_channel_masks);
     register_sample_rate(in->sample_rate, in->supported_sample_rates);
 
+    in->error_log = error_log_create(
+            ERROR_LOG_ENTRIES,
+            1000000000 /* aggregate consecutive identical errors within one second */);
+
     /* This stream could be for sound trigger lab,
        get sound trigger pcm if present */
     audio_extn_sound_trigger_check_and_get_session(in);
@@ -7473,6 +7938,8 @@
     pthread_mutex_unlock(&adev->lock);
     pthread_mutex_unlock(&in->lock);
 
+    stream_app_type_cfg_init(&in->app_type_cfg);
+
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
     return ret;
@@ -7501,6 +7968,9 @@
     if (!adev->active_input && !audio_extn_hfp_is_active(adev))
         platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE);
 
+    error_log_destroy(in->error_log);
+    in->error_log = NULL;
+
     if (in == NULL) {
         ALOGE("%s: audio_stream_in ptr is NULL", __func__);
         return;
@@ -7541,6 +8011,137 @@
     return;
 }
 
+/* verifies input and output devices and their capabilities.
+ *
+ * This verification is required when enabling extended bit-depth or
+ * sampling rates, as not all qcom products support it.
+ *
+ * Suitable for calling only on initialization such as adev_open().
+ * It fills the audio_device use_case_table[] array.
+ *
+ * Has a side-effect that it needs to configure audio routing / devices
+ * in order to power up the devices and read the device parameters.
+ * It does not acquire any hw device lock. Should restore the devices
+ * back to "normal state" upon completion.
+ */
+static int adev_verify_devices(struct audio_device *adev)
+{
+    /* enumeration is a bit difficult because one really wants to pull
+     * the use_case, device id, etc from the hidden pcm_device_table[].
+     * In this case there are the following use cases and device ids.
+     *
+     * [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {0, 0},
+     * [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {15, 15},
+     * [USECASE_AUDIO_PLAYBACK_HIFI] = {1, 1},
+     * [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {9, 9},
+     * [USECASE_AUDIO_RECORD] = {0, 0},
+     * [USECASE_AUDIO_RECORD_LOW_LATENCY] = {15, 15},
+     * [USECASE_VOICE_CALL] = {2, 2},
+     *
+     * USECASE_AUDIO_PLAYBACK_OFFLOAD, USECASE_AUDIO_PLAYBACK_HIFI omitted.
+     * USECASE_VOICE_CALL omitted, but possible for either input or output.
+     */
+
+    /* should be the usecases enabled in adev_open_input_stream() */
+    static const int test_in_usecases[] = {
+             USECASE_AUDIO_RECORD,
+             USECASE_AUDIO_RECORD_LOW_LATENCY, /* does not appear to be used */
+    };
+    /* should be the usecases enabled in adev_open_output_stream()*/
+    static const int test_out_usecases[] = {
+            USECASE_AUDIO_PLAYBACK_DEEP_BUFFER,
+            USECASE_AUDIO_PLAYBACK_LOW_LATENCY,
+    };
+    static const usecase_type_t usecase_type_by_dir[] = {
+            PCM_PLAYBACK,
+            PCM_CAPTURE,
+    };
+    static const unsigned flags_by_dir[] = {
+            PCM_OUT,
+            PCM_IN,
+    };
+
+    size_t i;
+    unsigned dir;
+    const unsigned card_id = adev->snd_card;
+
+    for (dir = 0; dir < 2; ++dir) {
+        const usecase_type_t usecase_type = usecase_type_by_dir[dir];
+        const unsigned flags_dir = flags_by_dir[dir];
+        const size_t testsize =
+                dir ? ARRAY_SIZE(test_in_usecases) : ARRAY_SIZE(test_out_usecases);
+        const int *testcases =
+                dir ? test_in_usecases : test_out_usecases;
+        const audio_devices_t audio_device =
+                dir ? AUDIO_DEVICE_IN_BUILTIN_MIC : AUDIO_DEVICE_OUT_SPEAKER;
+
+        for (i = 0; i < testsize; ++i) {
+            const audio_usecase_t audio_usecase = testcases[i];
+            int device_id;
+            struct pcm_params **pparams;
+            struct stream_out out;
+            struct stream_in in;
+            struct audio_usecase uc_info;
+            int retval;
+
+            pparams = &adev->use_case_table[audio_usecase];
+            pcm_params_free(*pparams); /* can accept null input */
+            *pparams = NULL;
+
+            /* find the device ID for the use case (signed, for error) */
+            device_id = platform_get_pcm_device_id(audio_usecase, usecase_type);
+            if (device_id < 0)
+                continue;
+
+            /* prepare structures for device probing */
+            memset(&uc_info, 0, sizeof(uc_info));
+            uc_info.id = audio_usecase;
+            uc_info.type = usecase_type;
+            if (dir) {
+                adev->active_input = &in;
+                memset(&in, 0, sizeof(in));
+                in.device = audio_device;
+                in.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
+                uc_info.stream.in = &in;
+            }  else {
+                adev->active_input = NULL;
+            }
+            memset(&out, 0, sizeof(out));
+            out.devices = audio_device; /* only field needed in select_devices */
+            uc_info.stream.out = &out;
+            uc_info.devices = audio_device;
+            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 device - similar to start_(in/out)put_stream() */
+            retval = select_devices(adev, audio_usecase);
+            if (retval >= 0) {
+                *pparams = pcm_params_get(card_id, device_id, flags_dir);
+#if LOG_NDEBUG == 0
+                if (*pparams) {
+                    ALOGV("%s: (%s) card %d  device %d", __func__,
+                            dir ? "input" : "output", card_id, device_id);
+                    pcm_params_to_string(*pparams, info, ARRAY_SIZE(info));
+                } else {
+                    ALOGV("%s: cannot locate card %d  device %d", __func__, card_id, device_id);
+                }
+#endif
+            }
+
+            /* deselect device - similar to stop_(in/out)put_stream() */
+            /* 1. Get and set stream specific mixer controls */
+            retval = disable_audio_route(adev, &uc_info);
+            /* 2. Disable the rx device */
+            retval = disable_snd_device(adev,
+                    dir ? uc_info.in_snd_device : uc_info.out_snd_device);
+            list_remove(&uc_info.list);
+        }
+    }
+    adev->active_input = NULL; /* restore adev state */
+    return 0;
+}
+
 int adev_create_audio_patch(struct audio_hw_device *dev,
                             unsigned int num_sources,
                             const struct audio_port_config *sources,
@@ -7584,6 +8185,7 @@
 
 static int adev_close(hw_device_t *device)
 {
+    size_t i;
     struct audio_device *adev = (struct audio_device *)device;
 
     if (!adev)
@@ -7595,6 +8197,8 @@
         audio_extn_snd_mon_unregister_listener(adev);
         audio_extn_sound_trigger_deinit(adev);
         audio_extn_listen_deinit(adev);
+        audio_extn_ma_deinit();
+        audio_extn_extspk_deinit(adev->extspk);
         audio_extn_utils_release_streams_cfg_lists(
                       &adev->streams_output_cfg_list,
                       &adev->streams_input_cfg_list);
@@ -7604,6 +8208,9 @@
         audio_extn_gef_deinit();
         free(adev->snd_dev_ref_cnt);
         platform_deinit(adev->platform);
+        for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
+            pcm_params_free(adev->use_case_table[i]);
+        }
         if (adev->adm_deinit)
             adev->adm_deinit(adev->adm_data);
         qahwi_deinit(device);
@@ -7854,6 +8461,8 @@
         return -EINVAL;
     }
 
+    adev->extspk = audio_extn_extspk_init(adev);
+
     if (audio_extn_qaf_is_enabled()) {
         ret = audio_extn_qaf_init(adev);
         if (ret < 0) {
@@ -7950,6 +8559,7 @@
         }
     }
 
+    adev->enable_voicerx = false;
     adev->bt_wb_speech_enabled = false;
     //initialize this to false for now,
     //this will be set to true through set param
@@ -7957,6 +8567,10 @@
 
     audio_extn_ds2_enable(adev);
     *device = &adev->device.common;
+
+    if (k_enable_extended_precision)
+        adev_verify_devices(adev);
+
     adev->dsp_bit_width_enforce_mode =
         adev_init_dsp_bit_width_enforce_mode(adev->mixer);
 
@@ -7968,7 +8582,8 @@
 
     char value[PROPERTY_VALUE_MAX];
     int trial;
-    if (property_get("vendor.audio_hal.period_size", value, NULL) > 0) {
+    if ((property_get("vendor.audio_hal.period_size", value, NULL) > 0) ||
+        (property_get("audio_hal.period_size", value, NULL) > 0)) {
         trial = atoi(value);
         if (period_size_is_plausible_for_low_latency(trial)) {
             pcm_config_low_latency.period_size = trial;
@@ -7977,7 +8592,8 @@
             configured_low_latency_capture_period_size = trial;
         }
     }
-    if (property_get("vendor.audio_hal.in_period_size", value, NULL) > 0) {
+    if ((property_get("vendor.audio_hal.in_period_size", value, NULL) > 0) ||
+        (property_get("audio_hal.in_period_size", value, NULL) > 0)) {
         trial = atoi(value);
         if (period_size_is_plausible_for_low_latency(trial)) {
             configured_low_latency_capture_period_size = trial;
@@ -7986,7 +8602,8 @@
 
     adev->mic_break_enabled = property_get_bool("vendor.audio.mic_break", false);
 
-    if (property_get("vendor.audio_hal.period_multiplier", value, NULL) > 0) {
+    if ((property_get("vendor.audio_hal.period_multiplier",value,NULL) > 0) ||
+        (property_get("audio_hal.period_multiplier",value,NULL) > 0)) {
         af_period_multiplier = atoi(value);
         if (af_period_multiplier < 0)
             af_period_multiplier = 2;
@@ -7996,6 +8613,8 @@
         ALOGV("new period_multiplier = %d", af_period_multiplier);
     }
 
+    audio_extn_ma_init(adev->platform);
+
     adev->multi_offload_enable = property_get_bool("vendor.audio.offload.multiple.enabled", false);
     pthread_mutex_unlock(&adev_init_lock);