hal: Add support for adaptive bitrate

Add support for adaptive bitrate with A2DP Offload.
Changes include separate sample rate for Slimbus Rx and
Tx backend, Tx feedback path setup on SLIMBUS_7_TX from
BTSoC to LPASS, AFE port configuration for A2DP encoder
and decoder.

Change-Id: I3b339e5525c9b6dab2e83f50bb4d63cd5d73850e
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 3b4acd8..21495df 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -71,10 +71,14 @@
 #define MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS           0
 #define MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR                1
 #define MIXER_ENC_CONFIG_BLOCK     "SLIM_7_RX Encoder Config"
+#define MIXER_DEC_CONFIG_BLOCK     "SLIM_7_TX Decoder Config"
 #define MIXER_ENC_BIT_FORMAT       "AFE Input Bit Format"
 #define MIXER_SCRAMBLER_MODE       "AFE Scrambler Mode"
-#define MIXER_SAMPLE_RATE          "BT SampleRate"
+#define MIXER_SAMPLE_RATE_RX       "BT SampleRate RX"
+#define MIXER_SAMPLE_RATE_TX       "BT SampleRate TX"
 #define MIXER_AFE_IN_CHANNELS      "AFE Input Channels"
+#define MIXER_ABR_TX_FEEDBACK_PATH "A2DP_SLIM7_UL_HL Switch"
+#define MIXER_SET_FEEDBACK_CHANNEL "BT set feedback channel"
 #define MIXER_ENC_FMT_SBC          "SBC"
 #define MIXER_ENC_FMT_AAC          "AAC"
 #define MIXER_ENC_FMT_APTX         "APTX"
@@ -95,6 +99,18 @@
 #define DEFAULT_SINK_LATENCY_CELT      180
 #define DEFAULT_SINK_LATENCY_LDAC      180
 
+// Slimbus Tx sample rate for ABR feedback channel
+#define ABR_TX_SAMPLE_RATE             "KHZ_8"
+
+// Purpose ID for Inter Module Communication (IMC) in AFE
+#define IMC_PURPOSE_ID_BT_INFO         0x000132E2
+
+// Maximum quality levels for ABR
+#define MAX_ABR_QUALITY_LEVELS             5
+
+// Instance identifier for A2DP
+#define MAX_INSTANCE_ID                (UINT32_MAX / 2)
+
 /*
  * Below enum values are extended from audio_base.h to
  * to keep encoder codec type local to bthost_ipc
@@ -134,6 +150,42 @@
     A2DP_STATE_DISCONNECTED,
 };
 
+typedef enum {
+    IMC_TRANSMIT,
+    IMC_RECEIVE,
+} imc_direction_t;
+
+typedef enum {
+    IMC_DISABLE,
+    IMC_ENABLE,
+} imc_status_t;
+
+/* PCM config for ABR Feedback hostless front end */
+static struct pcm_config pcm_config_abr = {
+    .channels = 1,
+    .rate = 8000,
+    .period_size = 240,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+/* Adaptive bitrate config for A2DP codecs */
+struct a2dp_abr_config {
+    /* Flag to denote whether Adaptive bitrate is enabled for codec */
+    bool is_abr_enabled;
+    /* Flag to denote whether front end has been opened for ABR */
+    bool abr_started;
+    /* ABR Tx path pcm handle */
+    struct pcm *abr_tx_handle;
+    /* ABR Inter Module Communication (IMC) instance ID */
+    uint32_t imc_instance;
+};
+
+static uint32_t instance_id = MAX_INSTANCE_ID;
+
 /* structure used to  update a2dp state machine
  * to communicate IPC library
  * to store DSP encoder configuration information
@@ -162,10 +214,76 @@
     bool is_a2dp_offload_supported;
     bool is_handoff_in_progress;
     bool is_aptx_dual_mono_supported;
+    /* Adaptive bitrate config for A2DP codecs */
+    struct a2dp_abr_config abr_config;
 };
 
 struct a2dp_data a2dp;
 
+/* Adaptive bitrate (ABR) is supported by certain Bluetooth codecs.
+ * Structures sent to configure DSP for ABR are defined below.
+ * This data helps DSP configure feedback path (BTSoC to LPASS)
+ * for link quality levels and mapping quality levels to codec
+ * specific bitrate.
+ */
+
+/* Key value pair for link quality level to bitrate mapping. */
+struct bit_rate_level_map_t {
+    uint32_t link_quality_level;
+    uint32_t bitrate;
+};
+
+/* Link quality level to bitrate mapping info sent to DSP. */
+struct quality_level_to_bitrate_info {
+    /* Number of quality levels being mapped.
+     * This will be equal to the size of mapping table.
+     */
+    uint32_t num_levels;
+    /* Quality level to bitrate mapping table */
+    struct bit_rate_level_map_t bit_rate_level_map[MAX_ABR_QUALITY_LEVELS];
+};
+
+/* Structure to set up Inter Module Communication (IMC) between
+ * AFE Decoder and Encoder.
+ */
+struct imc_dec_enc_info {
+    /* Decoder to encoder communication direction.
+     * Transmit = 0 / Receive = 1
+     */
+    uint32_t direction;
+    /* Enable / disable IMC between decoder and encoder */
+    uint32_t enable;
+    /* Purpose of IMC being set up between decoder and encoder.
+     * IMC_PURPOSE_ID_BT_INFO defined for link quality feedback
+     * is the default value to be sent as purpose.
+     */
+    uint32_t purpose;
+    /* Unique communication instance ID.
+     * purpose and comm_instance together form the actual key
+     * used in IMC registration, which must be the same for
+     * encoder and decoder for which IMC is being set up.
+     */
+    uint32_t comm_instance;
+};
+
+/* Structure used for ABR config of AFE encoder and decoder. */
+struct abr_enc_cfg_t {
+    /* Link quality level to bitrate mapping info sent to DSP. */
+    struct quality_level_to_bitrate_info mapping_info;
+    /* Information to set up IMC between decoder and encoder */
+    struct imc_dec_enc_info imc_info;
+}  __attribute__ ((packed));
+
+/* Structure to send configuration for decoder introduced
+ * on AFE Tx path for ABR link quality feedback to BT encoder.
+ */
+struct abr_dec_cfg_t {
+    /* Decoder media format */
+    uint32_t dec_format;
+    /* Information to set up IMC between decoder and encoder */
+    struct imc_dec_enc_info imc_info;
+} __attribute__ ((packed));
+
 /* START of DSP configurable structures
  * These values should match with DSP interface defintion
  */
@@ -265,6 +383,7 @@
 {
     struct custom_enc_cfg_t  custom_cfg;
     struct ldac_specific_enc_cfg_t ldac_cfg;
+    struct abr_enc_cfg_t abr_cfg;
 } __attribute__ ((packed));
 
 /* In LE BT source code uses system/audio.h for below
@@ -348,6 +467,8 @@
     uint32_t bit_rate; /*303000,606000,909000(in bits per second)*/
     uint16_t channel_mode; /* 0, 4, 2, 1*/
     uint16_t mtu; /*679*/
+    bool is_abr_enabled;
+    struct quality_level_to_bitrate_info level_to_bitrate_map;
 } audio_ldac_encoder_config;
 
 /*********** END of DSP configurable structures ********************/
@@ -405,6 +526,117 @@
     ALOGD("%s: codec cap = %s",__func__,value);
 }
 
+static int stop_abr()
+{
+    struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
+
+    /* This function can be used if !abr_started for clean up */
+    ALOGV("%s: enter", __func__);
+
+    // Close hostless front end
+    if (a2dp.abr_config.abr_tx_handle != NULL) {
+        pcm_close(a2dp.abr_config.abr_tx_handle);
+        a2dp.abr_config.abr_tx_handle = NULL;
+    }
+    a2dp.abr_config.abr_started = false;
+    a2dp.abr_config.imc_instance = 0;
+
+    // Reset BT driver mixer control for ABR usecase
+    ctl_set_bt_feedback_channel = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        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) {
+        ALOGE("%s: Failed to set BT usecase", __func__);
+        return -ENOSYS;
+    }
+
+    // Reset ABR Tx feedback path
+    ALOGV("%s: Disable ABR Tx feedback path", __func__);
+    ctl_abr_tx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        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) {
+        ALOGE("%s: Failed to set ABR Tx feedback path", __func__);
+        return -ENOSYS;
+    }
+
+   return 0;
+}
+
+static int start_abr()
+{
+    struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
+    int abr_device_id;
+    int ret = 0;
+
+    if (!a2dp.abr_config.is_abr_enabled) {
+        ALOGE("%s: Cannot start if ABR is not enabled", __func__);
+        return -ENOSYS;
+    }
+
+    if (a2dp.abr_config.abr_started) {
+        ALOGI("%s: ABR has already started", __func__);
+        return ret;
+    }
+
+    // Enable Slimbus 7 Tx feedback path
+    ALOGV("%s: Enable ABR Tx feedback path", __func__);
+    ctl_abr_tx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        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, 1) != 0) {
+        ALOGE("%s: Failed to set ABR Tx feedback path", __func__);
+        return -ENOSYS;
+    }
+
+    // Notify ABR usecase information to BT driver to distinguish
+    // between SCO and feedback usecase
+    ctl_set_bt_feedback_channel = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        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, 1) != 0) {
+        ALOGE("%s: Failed to set BT usecase", __func__);
+        return -ENOSYS;
+    }
+
+    // Open hostless front end and prepare ABR Tx path
+    abr_device_id = platform_get_pcm_device_id(USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+                                               PCM_CAPTURE);
+    if (!a2dp.abr_config.abr_tx_handle) {
+        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))
+            goto fail;
+    }
+    ret = pcm_start(a2dp.abr_config.abr_tx_handle);
+    if (ret < 0)
+        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;
+}
+
 /* API to open BT IPC library to start IPC communication */
 static void open_a2dp_output()
 {
@@ -489,6 +721,12 @@
     a2dp.enc_sampling_rate = 48000;
     a2dp.enc_channels = 2;
     a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+    if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+        stop_abr();
+    a2dp.abr_config.is_abr_enabled = false;
+    a2dp.abr_config.abr_started = false;
+    a2dp.abr_config.imc_instance = 0;
+    a2dp.abr_config.abr_tx_handle = NULL;
 
     return 0;
 }
@@ -516,48 +754,63 @@
     }
 }
 
-static void a2dp_set_backend_cfg()
+static int a2dp_set_backend_cfg()
 {
     char *rate_str = NULL, *in_channels = NULL;
-    uint32_t sampling_rate = a2dp.enc_sampling_rate;
+    uint32_t sampling_rate_rx = a2dp.enc_sampling_rate;
     struct mixer_ctl *ctl_sample_rate = NULL, *ctrl_in_channels = NULL;
 
     //For LDAC encoder open slimbus port at 96Khz for 48Khz input
     //and 88.2Khz for 44.1Khz input.
     if ((a2dp.bt_encoder_format == ENC_CODEC_TYPE_LDAC) &&
-        (sampling_rate == 48000 || sampling_rate == 44100 )) {
-        sampling_rate = sampling_rate *2;
+        (sampling_rate_rx == 48000 || sampling_rate_rx == 44100 )) {
+        sampling_rate_rx *= 2;
     }
-    //Configure backend sampling rate
-    switch (sampling_rate) {
+
+    // Set Rx backend sample rate
+    switch (sampling_rate_rx) {
     case 44100:
         rate_str = "KHZ_44P1";
         break;
-    case 48000:
-        rate_str = "KHZ_48";
-        break;
     case 88200:
         rate_str = "KHZ_88P2";
         break;
     case 96000:
         rate_str = "KHZ_96";
         break;
+    case 48000:
     default:
         rate_str = "KHZ_48";
         break;
     }
 
-    ALOGD("%s: set backend sample rate =%s", __func__, rate_str);
+    ALOGD("%s: set backend rx sample rate = %s", __func__, rate_str);
     ctl_sample_rate = mixer_get_ctl_by_name(a2dp.adev->mixer,
-                                        MIXER_SAMPLE_RATE);
+                                        MIXER_SAMPLE_RATE_RX);
     if (!ctl_sample_rate) {
-        ALOGE(" ERROR backend sample rate mixer control not identifed");
-        return;
-    } else {
-        if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
-            ALOGE("%s: Failed to set backend sample rate =%s", __func__, rate_str);
-            return;
-        }
+        ALOGE("%s: ERROR backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
+        ALOGE("%s: Failed to set backend sample rate = %s", __func__, rate_str);
+        return -ENOSYS;
+    }
+
+    // Set Tx backend sample rate
+    if (a2dp.abr_config.is_abr_enabled)
+        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_SAMPLE_RATE_TX);
+    if (!ctl_sample_rate) {
+        ALOGE("%s: ERROR backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
+        ALOGE("%s: Failed to set backend sample rate = %s",
+                                    __func__, rate_str);
+        return -ENOSYS;
     }
 
     //Configure AFE input channels
@@ -571,52 +824,96 @@
         break;
     }
 
-    ALOGD("%s: set afe input channels =%d", __func__, a2dp.enc_channels);
+    ALOGD("%s: set AFE input channels = %d", __func__, a2dp.enc_channels);
     ctrl_in_channels = mixer_get_ctl_by_name(a2dp.adev->mixer,
                                         MIXER_AFE_IN_CHANNELS);
     if (!ctrl_in_channels) {
-        ALOGE(" ERROR AFE input channels mixer control not identifed");
-        return;
-    } else {
-        if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
-            ALOGE("%s: Failed to set AFE in channels =%d", __func__, a2dp.enc_channels);
-            return;
-        }
+        ALOGE("%s: ERROR AFE input channels mixer control not identifed", __func__);
+        return -ENOSYS;
     }
+    if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
+        ALOGE("%s: Failed to set AFE in channels = %d", __func__, a2dp.enc_channels);
+        return -ENOSYS;
+    }
+
+    return 0;
 }
 
-static void a2dp_reset_backend_cfg()
+static int a2dp_reset_backend_cfg()
 {
-    char *rate_str = "KHZ_8", *in_channels = "Zero";
-    struct mixer_ctl *ctl_sample_rate = NULL, *ctrl_in_channels = NULL;
+    const char *rate_str = "KHZ_8", *in_channels = "Zero";
+    struct mixer_ctl *ctl_sample_rate_rx = NULL, *ctl_sample_rate_tx = NULL;
+    struct mixer_ctl *ctrl_in_channels = NULL;
 
-    //reset backend sampling rate
-    ALOGD("%s: reset backend sample rate =%s", __func__, rate_str);
-    ctl_sample_rate = mixer_get_ctl_by_name(a2dp.adev->mixer,
-                                        MIXER_SAMPLE_RATE);
-    if (!ctl_sample_rate) {
-        ALOGE(" ERROR backend sample rate mixer control not identifed");
-        return;
-    } else {
-        if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
-            ALOGE("%s: Failed to reset backend sample rate =%s", __func__, rate_str);
-            return;
-        }
+    // Reset backend sampling rate
+    ALOGD("%s: reset backend sample rate = %s", __func__, rate_str);
+    ctl_sample_rate_rx = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SAMPLE_RATE_RX);
+    if (!ctl_sample_rate_rx) {
+        ALOGE("%s: ERROR Rx backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate_rx, rate_str) != 0) {
+        ALOGE("%s: Failed to reset Rx backend sample rate = %s", __func__, rate_str);
+        return -ENOSYS;
     }
 
-    //reset AFE input channels
-    ALOGD("%s: reset afe input channels =%s", __func__, in_channels);
+    ctl_sample_rate_tx = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_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;
+    }
+
+    // Reset AFE input channels
+    ALOGD("%s: reset AFE input channels = %s", __func__, in_channels);
     ctrl_in_channels = mixer_get_ctl_by_name(a2dp.adev->mixer,
                                         MIXER_AFE_IN_CHANNELS);
     if (!ctrl_in_channels) {
-        ALOGE(" ERROR AFE input channels mixer control not identifed");
-        return;
-    } else {
-        if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
-            ALOGE("%s: Failed to reset AFE in channels =%d", __func__, a2dp.enc_channels);
-            return;
+        ALOGE("%s: ERROR AFE input channels mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
+        ALOGE("%s: Failed to reset AFE in channels = %d", __func__, a2dp.enc_channels);
+        return -ENOSYS;
+    }
+
+    return 0;
+}
+
+/* API to configure AFE decoder in DSP */
+static bool configure_a2dp_decoder_format(int dec_format)
+{
+    struct mixer_ctl *ctl_dec_data = NULL;
+    struct abr_dec_cfg_t dec_cfg;
+    int ret = 0;
+
+    if (a2dp.abr_config.is_abr_enabled) {
+        ctl_dec_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_DEC_CONFIG_BLOCK);
+        if (!ctl_dec_data) {
+            ALOGE("%s: ERROR A2DP codec config data mixer control not identifed", __func__);
+            return false;
+        }
+        memset(&dec_cfg, 0x0, sizeof(dec_cfg));
+        dec_cfg.dec_format = dec_format;
+        dec_cfg.imc_info.direction = IMC_TRANSMIT;
+        dec_cfg.imc_info.enable = IMC_ENABLE;
+        dec_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+        dec_cfg.imc_info.comm_instance = a2dp.abr_config.imc_instance;
+
+        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;
 }
 
 /* API to configure SBC DSP encoder */
@@ -973,7 +1270,7 @@
     is_configured = true;
     a2dp.bt_encoder_format = ENC_CODEC_TYPE_AAC;
     a2dp.enc_sampling_rate = aac_bt_cfg->sampling_rate;
-    a2dp.enc_channels = aac_bt_cfg->channels;;
+    a2dp.enc_channels = aac_bt_cfg->channels;
     ALOGV("Successfully updated AAC enc format with samplingrate: %d channels:%d",
            aac_dsp_cfg.sample_rate, aac_dsp_cfg.channel_cfg);
 fail:
@@ -1085,6 +1382,14 @@
     ldac_dsp_cfg.custom_cfg.custom_size = sizeof(struct ldac_enc_cfg_t);
     ldac_dsp_cfg.ldac_cfg.mtu = ldac_bt_cfg->mtu;
     ldac_dsp_cfg.ldac_cfg.bit_rate = ldac_bt_cfg->bit_rate;
+    if (ldac_bt_cfg->is_abr_enabled) {
+        ldac_dsp_cfg.abr_cfg.mapping_info = ldac_bt_cfg->level_to_bitrate_map;
+        ldac_dsp_cfg.abr_cfg.imc_info.direction = IMC_RECEIVE;
+        ldac_dsp_cfg.abr_cfg.imc_info.enable = IMC_ENABLE;
+        ldac_dsp_cfg.abr_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+        ldac_dsp_cfg.abr_cfg.imc_info.comm_instance = a2dp.abr_config.imc_instance;
+    }
+
     ret = mixer_ctl_set_array(ldac_enc_data, (void *)&ldac_dsp_cfg,
                               sizeof(struct ldac_enc_cfg_t));
     if (ret != 0) {
@@ -1108,6 +1413,7 @@
     a2dp.bt_encoder_format = ENC_CODEC_TYPE_LDAC;
     a2dp.enc_sampling_rate = ldac_bt_cfg->sampling_rate;
     a2dp.enc_channels = ldac_dsp_cfg.custom_cfg.num_channels;
+    a2dp.abr_config.is_abr_enabled = ldac_bt_cfg->is_abr_enabled;
     ALOGV("Successfully updated LDAC encformat with samplingrate: %d channels:%d",
            ldac_dsp_cfg.custom_cfg.sample_rate, ldac_dsp_cfg.custom_cfg.num_channels);
 fail:
@@ -1130,6 +1436,9 @@
     codec_info = a2dp.audio_get_codec_config(&multi_cast, &num_dev,
                                &codec_type);
 
+    // ABR disabled by default for all codecs
+    a2dp.abr_config.is_abr_enabled = false;
+
     switch(codec_type) {
         case ENC_CODEC_TYPE_SBC:
             ALOGD(" Received SBC encoder supported BT device");
@@ -1176,8 +1485,12 @@
             break;
         case ENC_CODEC_TYPE_LDAC:
             ALOGD(" Received LDAC encoder supported BT device");
+            if (!instance_id || instance_id > MAX_INSTANCE_ID)
+                instance_id = MAX_INSTANCE_ID;
+            a2dp.abr_config.imc_instance = instance_id--;
             is_configured =
-              configure_ldac_enc_format((audio_ldac_encoder_config *)codec_info);
+                (configure_ldac_enc_format((audio_ldac_encoder_config *)codec_info) &&
+                 configure_a2dp_decoder_format(ENC_CODEC_TYPE_LDAC));
             break;
         default:
             ALOGD(" Received Unsupported encoder formar");
@@ -1230,6 +1543,8 @@
         a2dp.a2dp_total_active_session_request++;
         a2dp_check_and_set_scrambler();
         a2dp_set_backend_cfg();
+        if (a2dp.abr_config.is_abr_enabled)
+            start_abr();
     }
 
     ALOGD("start A2DP playback total active sessions :%d",
@@ -1288,6 +1603,10 @@
             ALOGV("stop steam to BT IPC lib successful");
         reset_a2dp_enc_config_params();
         a2dp_reset_backend_cfg();
+        if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+            stop_abr();
+        a2dp.abr_config.is_abr_enabled = false;
+        a2dp.a2dp_started = false;
     }
     if(!a2dp.a2dp_total_active_session_request)
        a2dp.a2dp_started = false;
@@ -1446,6 +1765,10 @@
   a2dp.is_a2dp_offload_supported = false;
   a2dp.is_handoff_in_progress = false;
   a2dp.is_aptx_dual_mono_supported = false;
+  a2dp.abr_config.is_abr_enabled = false;
+  a2dp.abr_config.abr_started = false;
+  a2dp.abr_config.imc_instance = 0;
+  a2dp.abr_config.abr_tx_handle = NULL;
   reset_a2dp_enc_config_params();
   update_offload_codec_capabilities();
 }
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index ccac3a1..fa83205 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -351,7 +351,9 @@
     [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7] = "audio-interactive-stream7",
     [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8] = "audio-interactive-stream8",
 
-    [USECASE_AUDIO_EC_REF_LOOPBACK] = "ec-ref-audio-capture"
+    [USECASE_AUDIO_EC_REF_LOOPBACK] = "ec-ref-audio-capture",
+
+    [USECASE_AUDIO_A2DP_ABR_FEEDBACK] = "a2dp-abr-feedback"
 };
 
 static const audio_usecase_t offload_usecases[] = {
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 0dc9115..497b5c8 100755
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -201,6 +201,9 @@
     USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8,
 
     USECASE_AUDIO_EC_REF_LOOPBACK,
+
+    USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+
     AUDIO_USECASE_MAX
 };
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c4708a1..6e76bdc 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -906,6 +906,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_AFE_PROXY)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_AFE_PROXY)},
     {TO_NAME_INDEX(USECASE_AUDIO_EC_REF_LOOPBACK)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 #define NO_COLS 2
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 80f664b..237fddb 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -884,6 +884,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_AFE_PROXY)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_AFE_PROXY)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_SILENCE)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 #define NO_COLS 2