hal: add support for SWB codec for BT SCO device

Configure the codec of BT SCO device as SWB when sink
device supports this profile.

Change-Id: I68402d0f7040871f1d44a97ec14444456616dbc7
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 400d7d0..19c839c 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -62,6 +62,7 @@
 #define MEDIA_FMT_LDAC                                     0x00013224
 #define MEDIA_FMT_MP3                                      0x00010BE9
 #define MEDIA_FMT_APTX_ADAPTIVE                            0x00013204
+#define MEDIA_FMT_APTX_AD_SPEECH                           0x00013208
 #define MEDIA_FMT_AAC_AOT_LC                               2
 #define MEDIA_FMT_AAC_AOT_SBR                              5
 #define MEDIA_FMT_AAC_AOT_PS                               29
@@ -85,6 +86,7 @@
 #define MIXER_SAMPLE_RATE_DEFAULT  "BT SampleRate"
 #define MIXER_AFE_IN_CHANNELS      "AFE Input Channels"
 #define MIXER_ABR_TX_FEEDBACK_PATH "A2DP_SLIM7_UL_HL Switch"
+#define MIXER_ABR_RX_FEEDBACK_PATH "SCO_SLIM7_DL_HL Switch"
 #define MIXER_SET_FEEDBACK_CHANNEL "BT set feedback channel"
 #define MIXER_SINK_SAMPLE_RATE     "BT_TX SampleRate"
 #define MIXER_AFE_SINK_CHANNELS    "AFE Output Channels"
@@ -125,6 +127,9 @@
 // Slimbus Tx sample rate for ABR feedback channel
 #define ABR_TX_SAMPLE_RATE             "KHZ_8"
 
+// Slimbus Tx sample rate for APTX AD SPEECH
+#define SPEECH_TX_SAMPLE_RATE             "KHZ_96"
+
 // Purpose ID for Inter Module Communication (IMC) in AFE
 #define IMC_PURPOSE_ID_BT_INFO         0x000132E2
 
@@ -134,8 +139,13 @@
 // Instance identifier for A2DP
 #define MAX_INSTANCE_ID                (UINT32_MAX / 2)
 
+// Instance identifier for SWB
+#define APTX_AD_SPEECH_INSTANCE_ID                 37
+
+#define SAMPLING_RATE_96K               96000
 #define SAMPLING_RATE_48K               48000
 #define SAMPLING_RATE_441K              44100
+#define SAMPLING_RATE_32K               32000
 #define CH_STEREO                       2
 #define CH_MONO                         1
 #define SOURCE 0
@@ -172,6 +182,7 @@
     CODEC_TYPE_LDAC = AUDIO_FORMAT_LDAC, // 0x23000000UL
     CODEC_TYPE_CELT = 603979776u, // 0x24000000UL
     CODEC_TYPE_APTX_AD = 620756992u, // 0x25000000UL
+    CODEC_TYPE_APTX_AD_SPEECH = 637534208u, //0x26000000UL
     CODEC_TYPE_PCM = AUDIO_FORMAT_PCM_16_BIT, // 0x1u
 }codec_t;
 
@@ -237,6 +248,11 @@
 } imc_status_t;
 
 typedef enum {
+    SWAP_DISABLE,
+    SWAP_ENABLE,
+} swap_status_t;
+
+typedef enum {
     MTU_SIZE,
     PEAK_BIT_RATE,
 } frame_control_type_t;
@@ -265,6 +281,8 @@
     bool abr_started;
     /* ABR Tx path pcm handle */
     struct pcm *abr_tx_handle;
+    /* ABR Rx path pcm handle */
+    struct pcm *abr_rx_handle;
     /* ABR Inter Module Communication (IMC) instance ID */
     uint32_t imc_instance;
 };
@@ -319,6 +337,7 @@
     uint32_t dec_channels;
     bool a2dp_sink_started;
     int  a2dp_sink_total_active_session_requests;
+    bool swb_configured;
 };
 
 struct a2dp_data a2dp;
@@ -400,6 +419,18 @@
     struct imc_dec_enc_info imc_info;
 } __attribute__ ((packed));
 
+struct aptx_ad_speech_mode_cfg_t
+{
+    uint32_t mode;
+    uint32_t swapping;
+} __attribute__ ((packed));
+
+/* Structure for SWB voice dec config */
+struct aptx_ad_speech_dec_cfg_t {
+    struct abr_dec_cfg_t abr_cfg;
+    struct aptx_ad_speech_mode_cfg_t speech_mode;
+} __attribute__ ((packed));
+
 /* START of DSP configurable structures
  * These values should match with DSP interface defintion
  */
@@ -546,6 +577,15 @@
     struct abr_enc_cfg_t abr_cfg;
 } __attribute__ ((packed));
 
+/* APTX AD SPEECH structure */
+struct aptx_ad_speech_enc_cfg_t
+{
+    struct custom_enc_cfg_t  custom_cfg;
+    /* Information to set up IMC between decoder and encoder */
+    struct imc_dec_enc_info imc_info;
+    struct aptx_ad_speech_mode_cfg_t speech_mode;
+} __attribute__ ((packed));
+
 struct ldac_specific_enc_cfg_t
 {
     uint32_t      bit_rate;
@@ -715,7 +755,9 @@
 static int stop_abr()
 {
     struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_abr_rx_path = NULL;
     struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
+    int ret = 0;
 
     /* This function can be used if !abr_started for clean up */
     ALOGV("%s: enter", __func__);
@@ -725,6 +767,10 @@
         pcm_close(a2dp.abr_config.abr_tx_handle);
         a2dp.abr_config.abr_tx_handle = NULL;
     }
+    if (a2dp.abr_config.abr_rx_handle != NULL) {
+        pcm_close(a2dp.abr_config.abr_rx_handle);
+        a2dp.abr_config.abr_rx_handle = NULL;
+    }
     a2dp.abr_config.abr_started = false;
     a2dp.abr_config.imc_instance = 0;
 
@@ -733,11 +779,10 @@
                                         MIXER_SET_FEEDBACK_CHANNEL);
     if (!ctl_set_bt_feedback_channel) {
         ALOGE("%s: ERROR Set usecase mixer control not identifed", __func__);
-        return -ENOSYS;
-    }
-    if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 0) != 0) {
+        ret = -ENOSYS;
+    } else if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 0) != 0) {
         ALOGE("%s: Failed to set BT usecase", __func__);
-        return -ENOSYS;
+        ret = -ENOSYS;
     }
 
     // Reset ABR Tx feedback path
@@ -746,19 +791,31 @@
                                         MIXER_ABR_TX_FEEDBACK_PATH);
     if (!ctl_abr_tx_path) {
         ALOGE("%s: ERROR ABR Tx feedback path mixer control not identifed", __func__);
-        return -ENOSYS;
-    }
-    if (mixer_ctl_set_value(ctl_abr_tx_path, 0, 0) != 0) {
+        ret = -ENOSYS;
+    } else if (mixer_ctl_set_value(ctl_abr_tx_path, 0, 0) != 0) {
         ALOGE("%s: Failed to set ABR Tx feedback path", __func__);
-        return -ENOSYS;
+        ret = -ENOSYS;
     }
 
-   return 0;
+    // Reset ABR Rx feedback path
+    ALOGV("%s: Disable ABR Rx feedback path", __func__);
+    ctl_abr_rx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_ABR_RX_FEEDBACK_PATH);
+    if (!ctl_abr_rx_path) {
+        ALOGE("%s: ERROR ABR Rx feedback path mixer control not identifed", __func__);
+        ret = -ENOSYS;
+    } else if (mixer_ctl_set_value(ctl_abr_rx_path, 0, 0) != 0) {
+        ALOGE("%s: Failed to set ABR Rx feedback path", __func__);
+        ret = -ENOSYS;
+    }
+
+   return ret;
 }
 
 static int start_abr()
 {
     struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_abr_rx_path = NULL;
     struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
     int abr_device_id;
     int ret = 0;
@@ -792,11 +849,11 @@
                                         MIXER_SET_FEEDBACK_CHANNEL);
     if (!ctl_set_bt_feedback_channel) {
         ALOGE("%s: ERROR Set usecase mixer control not identifed", __func__);
-        return -ENOSYS;
+        goto fail;
     }
     if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 1) != 0) {
         ALOGE("%s: Failed to set BT usecase", __func__);
-        return -ENOSYS;
+        goto fail;
     }
 
     // Open hostless front end and prepare ABR Tx path
@@ -806,19 +863,60 @@
         a2dp.abr_config.abr_tx_handle = pcm_open(a2dp.adev->snd_card,
                                                  abr_device_id, PCM_IN,
                                                  &pcm_config_abr);
-        if (a2dp.abr_config.abr_tx_handle == NULL ||
-            !pcm_is_ready(a2dp.abr_config.abr_tx_handle))
+        if (a2dp.abr_config.abr_tx_handle == NULL) {
+            ALOGE("%s: Can't open abr tx device", __func__);
             goto fail;
+        }
+        if (!(pcm_is_ready(a2dp.abr_config.abr_tx_handle) &&
+              !pcm_start(a2dp.abr_config.abr_tx_handle))) {
+            ALOGE("%s: tx: %s", __func__, pcm_get_error(a2dp.abr_config.abr_tx_handle));
+            goto fail;
+        }
     }
-    ret = pcm_start(a2dp.abr_config.abr_tx_handle);
-    if (ret < 0)
-        goto fail;
+
+    // Enable Slimbus 7 Rx feedback path for HD Voice use case
+    if (a2dp.bt_encoder_format == CODEC_TYPE_APTX_AD_SPEECH) {
+        ALOGV("%s: Enable ABR Rx feedback path", __func__);
+        ctl_abr_rx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                            MIXER_ABR_RX_FEEDBACK_PATH);
+        if (!ctl_abr_rx_path) {
+            ALOGE("%s: ERROR ABR Rx feedback path mixer control not identifed", __func__);
+            goto fail;
+        }
+        if (mixer_ctl_set_value(ctl_abr_rx_path, 0, 1) != 0) {
+            ALOGE("%s: Failed to set ABR Rx feedback path", __func__);
+            goto fail;
+        }
+
+        if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 1) != 0) {
+            ALOGE("%s: Failed to set BT usecase", __func__);
+            goto fail;
+        }
+
+        // Open hostless front end and prepare ABR Rx path
+        abr_device_id = fp_platform_get_pcm_device_id(USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+                                                   PCM_PLAYBACK);
+        if (!a2dp.abr_config.abr_rx_handle) {
+            a2dp.abr_config.abr_rx_handle = pcm_open(a2dp.adev->snd_card,
+                                                     abr_device_id, PCM_OUT,
+                                                     &pcm_config_abr);
+            if (a2dp.abr_config.abr_rx_handle == NULL) {
+                ALOGE("%s: Can't open abr rx device", __func__);
+                goto fail;
+            }
+            if (!(pcm_is_ready(a2dp.abr_config.abr_rx_handle) &&
+                  !pcm_start(a2dp.abr_config.abr_rx_handle))) {
+                ALOGE("%s: rx: %s", __func__, pcm_get_error(a2dp.abr_config.abr_rx_handle));
+                goto fail;
+            }
+        }
+    }
+
     a2dp.abr_config.abr_started = true;
 
     return ret;
 
 fail:
-    ALOGE("%s: %s", __func__, pcm_get_error(a2dp.abr_config.abr_tx_handle));
     stop_abr();
     return -ENOSYS;
 }
@@ -965,6 +1063,7 @@
     a2dp.abr_config.abr_started = false;
     a2dp.abr_config.imc_instance = 0;
     a2dp.abr_config.abr_tx_handle = NULL;
+    a2dp.abr_config.abr_rx_handle = NULL;
     a2dp.bt_state_source = A2DP_STATE_DISCONNECTED;
 
     return 0;
@@ -1079,9 +1178,12 @@
 
         if (direction == SOURCE) {
             /* Set Tx backend sample rate */
-            if (a2dp.abr_config.is_abr_enabled)
-            rate_str = ABR_TX_SAMPLE_RATE;
-
+            if (a2dp.abr_config.is_abr_enabled) {
+                if (a2dp.bt_encoder_format == CODEC_TYPE_APTX_AD_SPEECH)
+                    rate_str = SPEECH_TX_SAMPLE_RATE;
+                else
+                    rate_str = ABR_TX_SAMPLE_RATE;
+            }
             ALOGD("%s: set backend tx sample rate = %s", __func__, rate_str);
             ctl_sample_rate = mixer_get_ctl_by_name(a2dp.adev->mixer,
                                             MIXER_SOURCE_SAMPLE_RATE_TX);
@@ -1282,17 +1384,18 @@
             ALOGE("%s: Failed to reset backend sample rate = %s", __func__, rate_str);
             return -ENOSYS;
         }
-
-        ctl_sample_rate_tx = mixer_get_ctl_by_name(a2dp.adev->mixer,
-                                        MIXER_SOURCE_SAMPLE_RATE_TX);
-        if (!ctl_sample_rate_tx) {
+        if (a2dp.abr_config.is_abr_enabled) {
+            ctl_sample_rate_tx = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                            MIXER_SOURCE_SAMPLE_RATE_TX);
+            if (!ctl_sample_rate_tx) {
                 ALOGE("%s: ERROR Tx backend sample rate mixer control not identifed", __func__);
                 return -ENOSYS;
-        }
+            }
 
-        if (mixer_ctl_set_enum_by_string(ctl_sample_rate_tx, rate_str) != 0) {
-            ALOGE("%s: Failed to reset Tx backend sample rate = %s", __func__, rate_str);
-            return -ENOSYS;
+            if (mixer_ctl_set_enum_by_string(ctl_sample_rate_tx, rate_str) != 0) {
+                ALOGE("%s: Failed to reset Tx backend sample rate = %s", __func__, rate_str);
+                return -ENOSYS;
+            }
         }
     } else {
 
@@ -1707,7 +1810,7 @@
 bool configure_aptx_enc_format(audio_aptx_encoder_config *aptx_bt_cfg)
 {
     struct mixer_ctl *ctl_enc_data = NULL;
-    int mixer_size;
+    int mixer_size = 0;
     bool is_configured = false;
     int ret = 0;
     int sample_rate_backup;
@@ -2209,7 +2312,7 @@
         return -ENOSYS;
     }
 
-    if (a2dp.a2dp_source_suspended == true) {
+    if (a2dp.a2dp_source_suspended || a2dp.swb_configured) {
         //session will be restarted after suspend completion
         ALOGD("a2dp start requested during suspend state");
         return -ENOSYS;
@@ -2434,6 +2537,16 @@
     }
 }
 
+static void reset_codec_config()
+{
+    reset_a2dp_enc_config_params();
+    reset_a2dp_source_dec_config_params();
+    a2dp_reset_backend_cfg(SOURCE);
+    if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+        stop_abr();
+    a2dp.abr_config.is_abr_enabled = false;
+}
+
 int a2dp_stop_playback()
 {
     int ret =0;
@@ -2456,14 +2569,9 @@
             ALOGE("stop stream to BT IPC lib failed");
         else
             ALOGV("stop steam to BT IPC lib successful");
-        reset_a2dp_enc_config_params();
-        reset_a2dp_source_dec_config_params();
-        a2dp_reset_backend_cfg(SOURCE);
-        if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
-            stop_abr();
-        a2dp.abr_config.is_abr_enabled = false;
+        if (!a2dp.a2dp_source_suspended && !a2dp.swb_configured)
+            reset_codec_config();
         a2dp.a2dp_source_started = false;
-        a2dp_reset_backend_cfg(SOURCE);
     }
     if (!a2dp.a2dp_source_total_active_session_requests)
        a2dp.a2dp_source_started = false;
@@ -2578,8 +2686,8 @@
                         pthread_mutex_lock(&a2dp.adev->lock);
                     }
                 }
-                reset_a2dp_enc_config_params();
-                reset_a2dp_source_dec_config_params();
+                if (!a2dp.swb_configured)
+                    reset_codec_config();
                 if (a2dp.audio_source_suspend)
                    a2dp.audio_source_suspend();
             } else if (a2dp.a2dp_source_suspended == true) {
@@ -2699,8 +2807,11 @@
   a2dp.abr_config.abr_started = false;
   a2dp.abr_config.imc_instance = 0;
   a2dp.abr_config.abr_tx_handle = NULL;
+  a2dp.abr_config.abr_rx_handle = NULL;
   a2dp.is_tws_mono_mode_on = false;
   a2dp_source_init();
+  a2dp.swb_configured = false;
+
   // init function pointers
   fp_platform_get_pcm_device_id =
               init_config.fp_platform_get_pcm_device_id;
@@ -2802,3 +2913,112 @@
 
     return 0;
 }
+
+
+bool configure_aptx_ad_speech_enc_fmt() {
+    struct mixer_ctl *ctl_enc_data = NULL;
+    int mixer_size = 0;
+    int ret = 0;
+    struct aptx_ad_speech_enc_cfg_t aptx_dsp_cfg;
+
+    ctl_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_data) {
+        ALOGE(" ERROR a2dp encoder CONFIG data mixer control not identifed");
+        return false;
+    }
+
+    /* Initialize dsp configuration params */
+    memset(&aptx_dsp_cfg, 0x0, sizeof(struct aptx_ad_speech_enc_cfg_t));
+    aptx_dsp_cfg.custom_cfg.enc_format = MEDIA_FMT_APTX_AD_SPEECH;
+    aptx_dsp_cfg.custom_cfg.sample_rate = SAMPLING_RATE_32K;
+    aptx_dsp_cfg.custom_cfg.num_channels = CH_MONO;
+    aptx_dsp_cfg.custom_cfg.channel_mapping[0] = PCM_CHANNEL_L;
+    aptx_dsp_cfg.imc_info.direction = IMC_RECEIVE;
+    aptx_dsp_cfg.imc_info.enable = IMC_ENABLE;
+    aptx_dsp_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+    aptx_dsp_cfg.imc_info.comm_instance = APTX_AD_SPEECH_INSTANCE_ID;
+    aptx_dsp_cfg.speech_mode.mode = a2dp.adev->swb_speech_mode;
+    aptx_dsp_cfg.speech_mode.swapping = SWAP_ENABLE;
+
+    /* Configure AFE DSP configuration */
+    mixer_size = sizeof(struct aptx_ad_speech_enc_cfg_t);
+    ret = mixer_ctl_set_array(ctl_enc_data, (void *)&aptx_dsp_cfg,
+                  mixer_size);
+    if (ret != 0) {
+        ALOGE("%s: Failed to set SWB encoder config", __func__);
+        return false;
+    }
+
+    /* Configure AFE Input Bit Format as PCM_16 */
+    ret = a2dp_set_bit_format(DEFAULT_ENCODER_BIT_FORMAT);
+    if (ret != 0) {
+        ALOGE("%s: Failed to set SWB bit format", __func__);
+        return false;
+    }
+
+    return true;
+}
+
+bool configure_aptx_ad_speech_dec_fmt()
+{
+    struct mixer_ctl *ctl_dec_data = NULL;
+    struct aptx_ad_speech_dec_cfg_t dec_cfg;
+    int ret = 0;
+
+    ctl_dec_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_SOURCE_DEC_CONFIG_BLOCK);
+    if (!ctl_dec_data) {
+        ALOGE("%s: ERROR codec config data mixer control not identifed", __func__);
+        return false;
+    }
+    memset(&dec_cfg, 0x0, sizeof(dec_cfg));
+    dec_cfg.abr_cfg.dec_format = MEDIA_FMT_APTX_AD_SPEECH;
+    dec_cfg.abr_cfg.imc_info.direction = IMC_TRANSMIT;
+    dec_cfg.abr_cfg.imc_info.enable = IMC_ENABLE;
+    dec_cfg.abr_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+    dec_cfg.abr_cfg.imc_info.comm_instance = APTX_AD_SPEECH_INSTANCE_ID;
+    dec_cfg.speech_mode.mode = a2dp.adev->swb_speech_mode;
+    dec_cfg.speech_mode.swapping = SWAP_ENABLE;
+
+    ret = mixer_ctl_set_array(ctl_dec_data, &dec_cfg,
+                              sizeof(dec_cfg));
+    if (ret != 0) {
+        ALOGE("%s: Failed to set decoder config", __func__);
+        return false;
+    }
+      return true;
+}
+
+int sco_start_configuration()
+{
+    ALOGD("sco_start_configuration start");
+
+    if (!a2dp.swb_configured) {
+        a2dp.bt_encoder_format = CODEC_TYPE_APTX_AD_SPEECH;
+        /* Configure AFE codec*/
+        if (configure_aptx_ad_speech_enc_fmt() &&
+            configure_aptx_ad_speech_dec_fmt()) {
+            ALOGD("%s: SCO enc/dec configured successfully", __func__);
+        } else {
+            ALOGE("%s: failed to send SCO configuration", __func__);
+            return -ETIMEDOUT;
+        }
+        /* Configure backend*/
+        a2dp.enc_sampling_rate = SAMPLING_RATE_96K;
+        a2dp.enc_channels = CH_MONO;
+        a2dp.abr_config.is_abr_enabled = true;
+        a2dp_set_backend_cfg(SOURCE);
+        /* Start abr*/
+        start_abr();
+        a2dp.swb_configured = true;
+    }
+    return 0;
+}
+
+void sco_reset_configuration()
+{
+    ALOGD("sco_reset_configuration start");
+
+    reset_codec_config();
+    a2dp.bt_encoder_format = CODEC_TYPE_INVALID;
+    a2dp.swb_configured = false;
+}
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 431c248..9939e20 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -3791,6 +3791,13 @@
 typedef int (*a2dp_stop_capture_t)();
 static a2dp_stop_capture_t a2dp_stop_capture;
 
+typedef int (*sco_start_configuration_t)();
+static sco_start_configuration_t sco_start_configuration;
+
+typedef void (*sco_reset_configuration_t)();
+static sco_reset_configuration_t sco_reset_configuration;
+
+
 int a2dp_offload_feature_init(bool is_feature_enabled)
 {
     ALOGD("%s: Called with feature %s", __func__,
@@ -3842,6 +3849,15 @@
             ALOGE("%s: dlsym failed", __func__);
             goto feature_disabled;
         }
+        // initialize APIs for SWB extension
+        if (!(sco_start_configuration =
+                 (sco_start_configuration_t)dlsym(a2dp_lib_handle, "sco_start_configuration")) ||
+             !(sco_reset_configuration =
+                 (sco_reset_configuration_t)dlsym(a2dp_lib_handle, "sco_reset_configuration"))) {
+            ALOGE("%s: dlsym failed for swb APIs", __func__);
+            sco_start_configuration = NULL;
+            sco_reset_configuration = NULL;
+        }
         ALOGD("%s:: ---- Feature A2DP_OFFLOAD is Enabled ----", __func__);
         return 0;
     }
@@ -3965,6 +3981,16 @@
     return (a2dp_stop_capture ? a2dp_stop_capture() : 0);
 }
 
+int audio_extn_sco_start_configuration()
+{
+    return (sco_start_configuration? sco_start_configuration() : 0);
+}
+
+void audio_extn_sco_reset_configuration()
+{
+    return (sco_reset_configuration? sco_reset_configuration() : 0);
+}
+
 // END: A2DP_OFFLOAD =====================================================================
 
 // START: HFP ======================================================================
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 51e3ba2..c80666a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -313,6 +313,9 @@
 bool audio_extn_a2dp_source_is_suspended();
 int audio_extn_a2dp_start_capture();
 int audio_extn_a2dp_stop_capture();
+int audio_extn_sco_start_configuration();
+void audio_extn_sco_reset_configuration();
+
 
 // --- Function pointers from audio_extn needed by A2DP_OFFLOAD
 typedef int (*fp_check_a2dp_restore_t)(struct audio_device *,
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 7a9022e..1357435 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1183,6 +1183,14 @@
             goto err;
         }
 
+        if (((SND_DEVICE_OUT_BT_SCO_SWB == snd_device) ||
+             (SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC == snd_device) ||
+             (SND_DEVICE_IN_BT_SCO_MIC_SWB == snd_device)) &&
+            (audio_extn_sco_start_configuration() < 0)) {
+            ALOGE(" fail to configure sco control path ");
+            goto err;
+        }
+
         /* due to the possibility of calibration overwrite between listen
             and audio, notify listen hal before audio calibration is sent */
         audio_extn_sound_trigger_update_device_status(snd_device,
@@ -1275,7 +1283,14 @@
             audio_extn_a2dp_stop_playback();
         else if (snd_device == SND_DEVICE_IN_BT_A2DP)
             audio_extn_a2dp_stop_capture();
-        else if ((snd_device == SND_DEVICE_OUT_HDMI) ||
+        else if ((snd_device == SND_DEVICE_OUT_BT_SCO_SWB) ||
+                 (snd_device == SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC) ||
+                 (snd_device == SND_DEVICE_IN_BT_SCO_MIC_SWB)) {
+            if ((adev->snd_dev_ref_cnt[SND_DEVICE_OUT_BT_SCO_SWB] == 0) &&
+                (adev->snd_dev_ref_cnt[SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC] == 0) &&
+                (adev->snd_dev_ref_cnt[SND_DEVICE_IN_BT_SCO_MIC_SWB] == 0))
+                audio_extn_sco_reset_configuration();
+       } else if ((snd_device == SND_DEVICE_OUT_HDMI) ||
                 (snd_device == SND_DEVICE_OUT_DISPLAY_PORT))
             adev->is_channel_status_set = false;
         else if ((snd_device == SND_DEVICE_OUT_HEADPHONES) &&
@@ -7573,6 +7588,12 @@
             adev->bt_wb_speech_enabled = false;
     }
 
+    ret = str_parms_get_str(parms, "bt_swb", value, sizeof(value));
+    if (ret >= 0) {
+        val = atoi(value);
+        adev->swb_speech_mode = val;
+    }
+
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
     if (ret >= 0) {
         val = atoi(value);
@@ -9013,6 +9034,7 @@
 
     adev->enable_voicerx = false;
     adev->bt_wb_speech_enabled = false;
+    adev->swb_speech_mode = SPEECH_MODE_INVALID;
     //initialize this to false for now,
     //this will be set to true through set param
     adev->vr_audio_mode_enabled = false;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 69c13e9..773aa5a 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -85,6 +85,9 @@
 #define ACDB_DEV_TYPE_OUT 1
 #define ACDB_DEV_TYPE_IN 2
 
+/* SCO SWB codec mode */
+#define SPEECH_MODE_INVALID  0xFFFF
+
 /* support positional and index masks to 8ch */
 #define MAX_SUPPORTED_CHANNEL_MASKS (2 * FCC_8)
 #define MAX_SUPPORTED_FORMATS 15
@@ -525,6 +528,7 @@
     unsigned int cur_hdmi_bit_width;
     unsigned int cur_wfd_channels;
     bool bt_wb_speech_enabled;
+    unsigned int swb_speech_mode;
     bool allow_afe_proxy_usage;
     bool is_charging; // from battery listener
     bool mic_break_enabled;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index a428f9a..35f2a2c 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -498,6 +498,7 @@
     [SND_DEVICE_OUT_SPEAKER_AND_DISPLAY_PORT] = "speaker-and-display-port",
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
     [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
+    [SND_DEVICE_OUT_BT_SCO_SWB] = "bt-sco-headset-swb",
     [SND_DEVICE_OUT_BT_A2DP] = "bt-a2dp",
     [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = "speaker-and-bt-a2dp",
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = "speaker-safe-and-bt-a2dp",
@@ -544,9 +545,12 @@
     [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO] = "speaker-and-bt-sco",
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = "speaker-safe-and-bt-sco",
     [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB] = "speaker-and-bt-sco-wb",
+    [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB] = "speaker-and-bt-sco-swb",
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = "speaker-safe-and-bt-sco-wb",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB] = "speaker-safe-and-bt-sco-swb",
     [SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO] = "wsa-speaker-and-bt-sco",
     [SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB] = "wsa-speaker-and-bt-sco-wb",
+    [SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_SWB] = "wsa-speaker-and-bt-sco-wb",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -593,6 +597,8 @@
     [SND_DEVICE_IN_BT_SCO_MIC_NREC] = "bt-sco-mic",
     [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb",
     [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = "bt-sco-mic-wb",
+    [SND_DEVICE_IN_BT_SCO_MIC_SWB] = "bt-sco-mic-swb",
+    [SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC] = "bt-sco-mic-swb",
     [SND_DEVICE_IN_BT_A2DP] = "bt-a2dp-cap",
     [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
     [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef",
@@ -751,7 +757,9 @@
     [SND_DEVICE_OUT_BT_SCO] = 22,
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = 14,
     [SND_DEVICE_OUT_BT_SCO_WB] = 39,
+    [SND_DEVICE_OUT_BT_SCO_SWB] = 39,
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = 14,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB] = 14,
     [SND_DEVICE_OUT_BT_A2DP] = 20,
     [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = 14,
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = 14,
@@ -834,6 +842,8 @@
     [SND_DEVICE_IN_BT_SCO_MIC_NREC] = 122,
     [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38,
     [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = 123,
+    [SND_DEVICE_IN_BT_SCO_MIC_SWB] = 38,
+    [SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC] = 123,
     [SND_DEVICE_IN_BT_A2DP] = 21,
     [SND_DEVICE_IN_CAMCORDER_MIC] = 4,
     [SND_DEVICE_IN_VOICE_DMIC] = 41,
@@ -954,7 +964,9 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_SWB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_A2DP)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)},
@@ -965,8 +977,10 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_SWB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_USB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_USB)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TX)},
@@ -1044,6 +1058,8 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_NREC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)},
     {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_SWB)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_BT_A2DP)},
     {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)},
@@ -1894,11 +1910,14 @@
     backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
     backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
     backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_SWB] = strdup("bt-sco-swb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC] = strdup("bt-sco-swb");
     backend_tag_table[SND_DEVICE_IN_SPDIF] = strdup("spdif-in");
     backend_tag_table[SND_DEVICE_IN_HDMI_MIC] = strdup("hdmi-in");
     backend_tag_table[SND_DEVICE_IN_HDMI_ARC] = strdup("hdmi-arc-in");
     backend_tag_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
     backend_tag_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO_SWB] = strdup("bt-sco-swb");
     backend_tag_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
     backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
     backend_tag_table[SND_DEVICE_OUT_DISPLAY_PORT] = strdup("display-port");
@@ -1920,6 +1939,8 @@
         strdup("speaker-safe-and-bt-sco");
     backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] =
         strdup("speaker-safe-and-bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB] =
+        strdup("speaker-safe-and-bt-sco-swb");
     backend_tag_table[SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
@@ -1987,6 +2008,7 @@
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_DISPLAY_PORT] = strdup("SLIMBUS_0_RX-and-DISPLAY_PORT");
     hw_interface_table[SND_DEVICE_OUT_BT_SCO] = strdup("SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("SLIMBUS_7_RX");
+    hw_interface_table[SND_DEVICE_OUT_BT_SCO_SWB] = strdup("SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_BT_A2DP] = strdup("SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = strdup("SLIMBUS_0_RX-and-SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] =
@@ -2020,8 +2042,10 @@
     hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_SCO] = strdup("SLIMBUS_0_RX-and-SEC_AUX_PCM_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB] = strdup("SLIMBUS_0_RX-and-SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB] = strdup("SLIMBUS_0_RX-and-SEC_AUX_PCM_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = strdup("QUAT_TDM_RX_0-and-SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = strdup("QUAT_TDM_RX_0-and-SLIMBUS_7_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB] = strdup("QUAT_TDM_RX_0-and-SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_2_PROTECTED] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_STEREO_PROTECTED] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED_VBAT] = strdup("SLIMBUS_0_RX");
@@ -2076,6 +2100,8 @@
     hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("SLIMBUS_7_TX");
     hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("SLIMBUS_7_TX");
     hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("SLIMBUS_7_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_SWB] = strdup("SLIMBUS_7_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC] = strdup("SLIMBUS_7_TX");
     hw_interface_table[SND_DEVICE_IN_BT_A2DP] = strdup("SLIMBUS_7_TX");
     hw_interface_table[SND_DEVICE_IN_CAMCORDER_MIC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_DMIC] = strdup("SLIMBUS_0_TX");
@@ -4940,6 +4966,19 @@
         new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
         new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_WB;
         ret = 0;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_BT_SCO_SWB)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_SWB;
+        ret = 0;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE,
+                                              SND_DEVICE_OUT_BT_SCO_SWB)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_SWB;
+        ret = 0;
     } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET &&
                !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_USB_HEADSET)) {
         *num_devices = 2;
@@ -5131,19 +5170,29 @@
             snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP;
         } else if ((devices & AUDIO_DEVICE_OUT_ALL_SCO) &&
                    ((devices & ~AUDIO_DEVICE_OUT_ALL_SCO) == AUDIO_DEVICE_OUT_SPEAKER)) {
-            if (my_data->is_wsa_speaker)
-                snd_device = adev->bt_wb_speech_enabled ?
-                        SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB :
-                        SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO;
-            else
-                snd_device = adev->bt_wb_speech_enabled ?
-                        SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB :
-                        SND_DEVICE_OUT_SPEAKER_AND_BT_SCO;
+            if (my_data->is_wsa_speaker) {
+                if (adev->swb_speech_mode != SPEECH_MODE_INVALID)
+                    snd_device = SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_SWB;
+                else
+                    snd_device = adev->bt_wb_speech_enabled ?
+                            SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB :
+                            SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO;
+            } else {
+                if (adev->swb_speech_mode != SPEECH_MODE_INVALID)
+                    snd_device = SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB;
+                else
+                    snd_device = adev->bt_wb_speech_enabled ?
+                            SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB :
+                            SND_DEVICE_OUT_SPEAKER_AND_BT_SCO;
+            }
         } else if ((devices & AUDIO_DEVICE_OUT_ALL_SCO) &&
                          ((devices & ~AUDIO_DEVICE_OUT_ALL_SCO) == AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-            snd_device = adev->bt_wb_speech_enabled ?
-                    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB :
-                    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO;
+            if (adev->swb_speech_mode != SPEECH_MODE_INVALID)
+                snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB;
+            else
+                snd_device = adev->bt_wb_speech_enabled ?
+                        SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB :
+                        SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO;
         } else if ((devices == (AUDIO_DEVICE_OUT_USB_DEVICE |
                                AUDIO_DEVICE_OUT_SPEAKER_SAFE)) ||
                 (devices == (AUDIO_DEVICE_OUT_USB_HEADSET |
@@ -5225,7 +5274,9 @@
                                  SND_DEVICE_OUT_VOICE_USB_HEADPHONES;
             }
         } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-            if (adev->bt_wb_speech_enabled)
+            if (adev->swb_speech_mode != SPEECH_MODE_INVALID)
+                snd_device = SND_DEVICE_OUT_BT_SCO_SWB;
+            else if (adev->bt_wb_speech_enabled)
                 snd_device = SND_DEVICE_OUT_BT_SCO_WB;
             else
                 snd_device = SND_DEVICE_OUT_BT_SCO;
@@ -5351,7 +5402,9 @@
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
     } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-        if (adev->bt_wb_speech_enabled)
+        if (adev->swb_speech_mode != SPEECH_MODE_INVALID)
+                snd_device = SND_DEVICE_OUT_BT_SCO_SWB;
+        else if (adev->bt_wb_speech_enabled)
             snd_device = SND_DEVICE_OUT_BT_SCO_WB;
         else
             snd_device = SND_DEVICE_OUT_BT_SCO;
@@ -5740,7 +5793,12 @@
             if (audio_extn_hfp_is_active(adev))
                 platform_set_echo_reference(adev, true, out_device);
         } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
-            if (adev->bt_wb_speech_enabled) {
+            if (adev->swb_speech_mode != SPEECH_MODE_INVALID) {
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB;
+            } else if (adev->bt_wb_speech_enabled) {
                 if (adev->bluetooth_nrec)
                     snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
                 else
@@ -6050,7 +6108,12 @@
         } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-            if (adev->bt_wb_speech_enabled) {
+            if (adev->swb_speech_mode != SPEECH_MODE_INVALID) {
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB;
+            } else if (adev->bt_wb_speech_enabled) {
                 if (adev->bluetooth_nrec)
                     snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
                 else
@@ -6113,7 +6176,12 @@
             snd_device = my_data->fluence_sb_enabled ? SND_DEVICE_IN_HANDSET_MIC_SB
                              : SND_DEVICE_IN_HANDSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
-            if (adev->bt_wb_speech_enabled) {
+            if (adev->swb_speech_mode != SPEECH_MODE_INVALID) {
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_SWB;
+            } else if (adev->bt_wb_speech_enabled) {
                 if (adev->bluetooth_nrec)
                     snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
                 else
@@ -7790,6 +7858,7 @@
     if (snd_device == SND_DEVICE_OUT_BT_A2DP ||
         snd_device == SND_DEVICE_OUT_BT_SCO ||
         snd_device == SND_DEVICE_OUT_BT_SCO_WB ||
+        snd_device == SND_DEVICE_OUT_BT_SCO_SWB ||
         snd_device == SND_DEVICE_IN_BT_A2DP ||
         snd_device == SND_DEVICE_OUT_AFE_PROXY) {
         backend_change = false;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 2cb1741..ec6843b 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -110,6 +110,7 @@
     SND_DEVICE_OUT_SPEAKER_AND_DISPLAY_PORT,
     SND_DEVICE_OUT_BT_SCO,
     SND_DEVICE_OUT_BT_SCO_WB,
+    SND_DEVICE_OUT_BT_SCO_SWB,
     SND_DEVICE_OUT_BT_A2DP,
     SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP,
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP,
@@ -118,8 +119,11 @@
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO,
     SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB,
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB,
+    SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_SWB,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_SWB,
     SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO,
     SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB,
+    SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_SWB,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
@@ -219,6 +223,8 @@
     SND_DEVICE_IN_BT_SCO_MIC_NREC,
     SND_DEVICE_IN_BT_SCO_MIC_WB,
     SND_DEVICE_IN_BT_SCO_MIC_WB_NREC,
+    SND_DEVICE_IN_BT_SCO_MIC_SWB,
+    SND_DEVICE_IN_BT_SCO_MIC_SWB_NREC,
     SND_DEVICE_IN_BT_A2DP,
     SND_DEVICE_IN_CAMCORDER_MIC,
     SND_DEVICE_IN_VOICE_DMIC,