hal: Add support for adaptive bitrate with A2DP Offload
am: 9954631a45

Change-Id: I1663e4e3dd7278f792d3f87992d6e26beb7d7989
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 1a9421b..6f278c2 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -33,7 +33,6 @@
 #include "platform_api.h"
 
 #ifdef A2DP_OFFLOAD_ENABLED
-#define AUDIO_PARAMETER_A2DP_STARTED "A2dpStarted"
 #define BT_IPC_LIB_NAME  "libbthost_if.so"
 
 // Media format definitions
@@ -60,10 +59,14 @@
 
 // Mixer controls sent to ALSA
 #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"
 
 // Encoder format strings
 #define ENC_FMT_AAC                "aac"
@@ -96,6 +99,18 @@
 #define DEFAULT_SINK_LATENCY_LDAC      180
 #define DEFAULT_SINK_LATENCY_SBC       140
 
+// 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
  * keep encoder codec type local to bthost_ipc
@@ -130,6 +145,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;
+
 /* Data structure used to:
  * - Update the A2DP state machine
  * - Communicate with the libbthost_if.so IPC library
@@ -181,10 +232,76 @@
     bool is_handoff_in_progress;
     /* Flag to denote whether APTX Dual Mono encoder is supported */
     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
  */
@@ -319,6 +436,7 @@
 struct ldac_enc_cfg_t {
     struct custom_enc_cfg_t  custom_cfg;
     struct ldac_specific_enc_cfg_t ldac_cfg;
+    struct abr_enc_cfg_t abr_cfg;
 } __attribute__ ((packed));
 
 /* Information about Bluetooth SBC encoder configuration
@@ -383,6 +501,8 @@
     uint16_t channel_mode; /* 0, 4, 2, 1 */
     uint16_t mtu;
     uint32_t bits_per_sample; /* 16, 24, 32 (bits) */
+    bool is_abr_enabled;
+    struct quality_level_to_bitrate_info level_to_bitrate_map;
 } audio_ldac_encoder_config;
 
 /*********** END of DSP configurable structures ********************/
@@ -394,15 +514,129 @@
     a2dp.a2dp_suspended = false;
     a2dp.bt_encoder_format = ENC_CODEC_TYPE_INVALID;
     a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+    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;
 }
 
 static void update_offload_codec_support()
 {
-    char value[PROPERTY_VALUE_MAX] = {'\0'};
-
     a2dp.is_a2dp_offload_supported =
             property_get_bool(SYSPROP_A2DP_OFFLOAD_ENABLED, false);
-    ALOGD("%s: A2DP offload supported = %s", __func__, value);
+    ALOGD("%s: A2DP offload supported = %d",
+                   __func__, a2dp.is_a2dp_offload_supported);
+}
+
+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 Bluetooth IPC library to start IPC communication */
@@ -484,6 +718,8 @@
         if (a2dp.audio_stream_close() == false)
             ALOGE("%s: failed close A2DP control path from Bluetooth IPC library", __func__);
     }
+    if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+        stop_abr();
     a2dp_common_init();
     a2dp.enc_sampling_rate = 0;
     a2dp.enc_channels = 0;
@@ -519,48 +755,63 @@
     return 0;
 }
 
-static void a2dp_set_backend_cfg()
+static int a2dp_set_backend_cfg()
 {
     const 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);
+    ALOGV("%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("%s: ERROR backend sample rate mixer control not identifed", __func__);
-        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;
-        }
+        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;
+
+    ALOGV("%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
@@ -574,18 +825,19 @@
         break;
     }
 
-    ALOGD("%s: set AFE input channels =%d", __func__, a2dp.enc_channels);
+    ALOGV("%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("%s: ERROR AFE input channels mixer control not identifed", __func__);
-        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;
-        }
+        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 int a2dp_set_bit_format(uint32_t enc_bit_format)
@@ -621,38 +873,81 @@
     return 0;
 }
 
-static void a2dp_reset_backend_cfg()
+static int a2dp_reset_backend_cfg()
 {
     const char *rate_str = "KHZ_8", *in_channels = "Zero";
-    struct mixer_ctl *ctl_sample_rate = NULL, *ctrl_in_channels = NULL;
+    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("%s: ERROR backend sample rate mixer control not identifed", __func__);
-        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;
-        }
+    ALOGV("%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;
+    }
+
+    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);
+    ALOGV("%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("%s: ERROR AFE input channels mixer control not identifed", __func__);
-        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;
+        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, (void *)&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 */
@@ -912,7 +1207,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("%s: Successfully updated AAC enc format with sampling rate: %d channels:%d",
            __func__, aac_dsp_cfg.sample_rate, aac_dsp_cfg.channel_cfg);
 exit:
@@ -959,6 +1254,14 @@
     ldac_dsp_cfg.custom_cfg.custom_size = sizeof(ldac_dsp_cfg);
     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(ldac_dsp_cfg));
     if (ret != 0) {
@@ -975,6 +1278,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("%s: Successfully updated LDAC encformat with sampling rate: %d channels:%d",
            __func__, ldac_dsp_cfg.custom_cfg.sample_rate,
            ldac_dsp_cfg.custom_cfg.num_channels);
@@ -998,6 +1302,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("%s: Received SBC encoder supported Bluetooth device", __func__);
@@ -1023,8 +1330,12 @@
             break;
         case ENC_CODEC_TYPE_LDAC:
             ALOGD("%s: Received LDAC encoder supported Bluetooth device", __func__);
+            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("%s: Received unsupported encoder format", __func__);
@@ -1077,6 +1388,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("%s: start A2DP playback total active sessions :%d", __func__,
@@ -1131,6 +1444,9 @@
             ALOGV("%s: stop steam to Bluetooth IPC lib successful", __func__);
         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;
     }
     ALOGD("%s: Stop A2DP playback total active sessions :%d", __func__,
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 325131e..99e638b 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -291,6 +291,8 @@
     [USECASE_AUDIO_RECORD_VOIP] = "audio-record-voip",
 
     [USECASE_INCALL_MUSIC_UPLINK] = "incall-music-uplink",
+
+    [USECASE_AUDIO_A2DP_ABR_FEEDBACK] = "a2dp-abr-feedback",
 };
 
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index bc15915..a42a458 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -133,6 +133,8 @@
 
     USECASE_INCALL_MUSIC_UPLINK,
 
+    USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+
     AUDIO_USECASE_MAX
 };
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index e405495..4b91bcd 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -496,6 +496,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO_WB)},
     {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_TX)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 #define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index cb07986..66c72fb 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -600,6 +600,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_VOIP)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_VOIP)},
     {TO_NAME_INDEX(USECASE_INCALL_MUSIC_UPLINK)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 static const struct name_to_index usecase_type_index[USECASE_TYPE_MAX] = {