Merge "hal: Deinit EC reference loopback"
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
index 59fb5a3..54c1e5a 100644
--- a/hal/audio_extn/spkr_protection.c
+++ b/hal/audio_extn/spkr_protection.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.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -125,6 +125,12 @@
 /* In wsa analog mode vi feedback DAI supports at max 2 channels*/
 #define WSA_ANALOG_MODE_CHANNELS 2
 
+/* v-validation parameters */
+#define SPKR_V_VALI_TEMP_MASK 0xFFFE
+#define SPKR_V_VALI_DEFAULT_WAIT_TIME 500
+#define SPKR_V_VALI_DEFAULT_VALI_TIME 2000
+#define SPKR_V_VALI_SUCCESS 1
+
 #define MAX_PATH             (256)
 #define MAX_STR_SIZE         (1024)
 #define THERMAL_SYSFS "/sys/devices/virtual/thermal"
@@ -135,11 +141,16 @@
 #define AUDIO_PARAMETER_KEY_SPKR_TZ_2     "spkr_2_tz_name"
 
 #define AUDIO_PARAMETER_KEY_FBSP_TRIGGER_SPKR_CAL   "trigger_spkr_cal"
+#define AUDIO_PARAMETER_KEY_FBSP_APPLY_SPKR_CAL   "apply_spkr_cal"
 #define AUDIO_PARAMETER_KEY_FBSP_GET_SPKR_CAL       "get_spkr_cal"
 #define AUDIO_PARAMETER_KEY_FBSP_CFG_WAIT_TIME      "fbsp_cfg_wait_time"
 #define AUDIO_PARAMETER_KEY_FBSP_CFG_FTM_TIME       "fbsp_cfg_ftm_time"
 #define AUDIO_PARAMETER_KEY_FBSP_GET_FTM_PARAM      "get_ftm_param"
+#define AUDIO_PARAMETER_KEY_FBSP_TRIGGER_V_VALI     "trigger_v_vali"
+#define AUDIO_PARAMETER_KEY_FBSP_V_VALI_WAIT_TIME   "fbsp_v_vali_wait_time"
+#define AUDIO_PARAMETER_KEY_FBSP_V_VALI_VALI_TIME   "fbsp_v_vali_vali_time"
 
+static int get_spkr_prot_v_vali_param(int cal_fd, int *status, int *vrms);
 /*Modes of Speaker Protection*/
 enum speaker_protection_mode {
     SPKR_PROTECTION_DISABLED = -1,
@@ -158,6 +169,7 @@
     int thermal_client_handle;
     pthread_mutex_t mutex_spkr_prot;
     pthread_t spkr_calibration_thread;
+    pthread_t spkr_v_vali_thread;
     pthread_mutex_t spkr_prot_thermalsync_mutex;
     pthread_cond_t spkr_prot_thermalsync;
     int cancel_spkr_calib;
@@ -165,6 +177,7 @@
     pthread_mutex_t spkr_calib_cancelack_mutex;
     pthread_cond_t spkr_calibcancel_ack;
     pthread_t speaker_prot_threadid;
+    pthread_t v_vali_threadid;
     void *thermal_handle;
     void *adev_handle;
     int spkr_prot_t0;
@@ -182,12 +195,18 @@
     int spkr_1_tzn;
     int spkr_2_tzn;
     bool trigger_cal;
+    bool trigger_v_vali;
+    bool apply_cal;
     pthread_mutex_t cal_wait_cond_mutex;
     pthread_cond_t cal_wait_condition;
-    bool init_check;
+    bool spkr_cal_dynamic;
     volatile bool thread_exit;
     unsigned int sp_version;
     int limiter_th[SP_V2_NUM_MAX_SPKRS];
+    int v_vali_wait_time;
+    int v_vali_vali_time;
+    bool cal_thrd_created;
+    bool v_vali_thrd_created;
 };
 
 static struct pcm_config pcm_config_skr_prot = {
@@ -377,7 +396,8 @@
     struct audio_usecase *uc_info;
     threadid = pthread_self();
     ALOGV("%s: Entry", __func__);
-    if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
+    if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev ||
+        pthread_equal(handle.v_vali_threadid, threadid)) {
         ALOGE("%s: Invalid params", __func__);
         return;
     }
@@ -740,12 +760,15 @@
     struct audio_device *adev = handle.adev_handle;
     struct audio_cal_info_spk_prot_cfg protCfg;
     struct audio_cal_info_msm_spk_prot_status status;
+    int status_v_vali[SP_V2_NUM_MAX_SPKRS], vrms[SP_V2_NUM_MAX_SPKRS];
     bool cleanup = false, disable_rx = false, disable_tx = false;
     int acdb_fd = -1;
     struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
     int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
     struct timespec ts;
+    unsigned long total_time;
     bool acquire_device = false;
+    bool v_validation = false;
 
     memset(&status, 0, sizeof(status));
     memset(&protCfg, 0, sizeof(protCfg));
@@ -757,12 +780,26 @@
         ALOGD("%s: Usecase present retry speaker protection", __func__);
         return -EAGAIN;
     }
+    if (t0_spk_1 == SPKR_V_VALI_TEMP_MASK &&
+        t0_spk_2 == SPKR_V_VALI_TEMP_MASK) {
+        ALOGD("%s: v-validation start", __func__);
+        v_validation = true;
+    }
     acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
     if (acdb_fd < 0) {
         ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
         return -ENODEV;
     } else {
         protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
+        if (v_validation) {
+            if (handle.spkr_prot_mode == MSM_SPKR_PROT_CALIBRATED) {
+                t0_spk_1 = handle.sp_r0t0_cal.t0[SP_V2_SPKR_1];
+                t0_spk_2 = handle.sp_r0t0_cal.t0[SP_V2_SPKR_2];
+            } else {
+                t0_spk_1 = SAFE_SPKR_TEMP_Q6;
+                t0_spk_2 = SAFE_SPKR_TEMP_Q6;
+            }
+        }
         protCfg.t0[SP_V2_SPKR_1] = t0_spk_1;
         protCfg.t0[SP_V2_SPKR_2] = t0_spk_2;
         if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
@@ -855,7 +892,13 @@
     }
     cleanup = true;
     clock_gettime(CLOCK_MONOTONIC, &ts);
-    ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
+    if (!v_validation) {
+        ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
+    } else {
+        total_time = (handle.v_vali_wait_time + handle.v_vali_vali_time);
+        ts.tv_sec += (total_time/1000);
+        ts.tv_nsec += ((total_time%1000) * 1000000);
+    }
     pthread_mutex_lock(&handle.mutex_spkr_prot);
     pthread_mutex_unlock(&adev->lock);
     acquire_device = true;
@@ -870,11 +913,38 @@
     }
     if (acdb_fd > 0) {
         status.status = -EINVAL;
+        if (v_validation) {
+              if (!get_spkr_prot_v_vali_param(acdb_fd, status_v_vali, vrms)) {
+                  int i;
+
+                  for (i = 0; i < vi_feed_no_channels; i++) {
+                       if ((status_v_vali[i] != SPKR_V_VALI_SUCCESS)) {
+                           ALOGE("%s: failed in v-validation, retry\n", __func__);
+                           goto exit;
+                       } else {
+                           ALOGD("%s: spkr_v_validation success vrms %d",
+                           __func__, vrms[i]);
+                       }
+                  }
+                  status.status = 0;
+             }
+             goto exit;
+        }
         while (!get_spkr_prot_cal(acdb_fd, &status)) {
             /*sleep for 200 ms to check for status check*/
             if (!status.status) {
+                int i;
+
                 ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
                  __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
+                for (i = 0; i < vi_feed_no_channels; i++) {
+                    if (!((status.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
+                         && (status.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
+                         ALOGE("%s R0 not in range, retry R0:%d\n", __func__, status.r0[i]);
+                         status.status = -EINVAL;
+                         break;
+                   }
+                }
                 FILE *fp;
                 fp = fopen(CALIB_FILE,"wb");
                 if (!fp) {
@@ -907,24 +977,26 @@
         if (handle.pcm_tx)
             pcm_close(handle.pcm_tx);
         handle.pcm_tx = NULL;
-        if (!status.status) {
-            protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
-            protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
-            protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
-            if (set_spkr_prot_cal(acdb_fd, &protCfg))
-                ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
-            else
-                handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
-        } else {
-            protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
-            handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
-            if (set_spkr_prot_cal(acdb_fd, &protCfg))
-                ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
+        if (!v_validation) {
+            if (!status.status) {
+                protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+                protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
+                protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
+                if (set_spkr_prot_cal(acdb_fd, &protCfg))
+                    ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
+                else
+                    handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+            } else {
+                protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+                handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+                if (set_spkr_prot_cal(acdb_fd, &protCfg))
+                    ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
+            }
         }
         if (acdb_fd > 0)
             close(acdb_fd);
 
-        if (!handle.cancel_spkr_calib && cleanup) {
+        if (!handle.cancel_spkr_calib && cleanup && !handle.spkr_cal_dynamic) {
             pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
             pthread_cond_wait(&handle.spkr_calib_cancel,
             &handle.mutex_spkr_prot);
@@ -1025,41 +1097,46 @@
     spv3_enable = property_get_bool("persist.vendor.audio.spv3.enable", false);
     afe_api_version = property_get_int32("persist.vendor.audio.avs.afe_api_version", 0);
 
-    fp = fopen(CALIB_FILE,"rb");
-    if (fp) {
-        int i;
-        bool spkr_calibrated = true;
-        for (i = 0; i < vi_feed_no_channels; i++) {
-            fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
-            fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
-        }
-        ALOGD("%s: spkr_prot_thread r0 value %d %d",
-               __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
-        ALOGD("%s: spkr_prot_thread t0 value %d %d",
-               __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
-        fclose(fp);
-        /*Valid tempature range: -30C to 80C(in q6 format)
-          Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
-        for (i = 0; i < vi_feed_no_channels; i++) {
-            if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
-                && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
-                && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
-                spkr_calibrated = false;
-                break;
+    if (!handle.spkr_cal_dynamic || handle.apply_cal) {
+        bool spkr_calibrated = false;
+        fp = fopen(CALIB_FILE,"rb");
+        if (fp) {
+            int i;
+            spkr_calibrated = true;
+            for (i = 0; i < vi_feed_no_channels; i++) {
+                 fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
+                 fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
+            }
+            ALOGD("%s: spkr_prot_thread r0 value %d %d",
+                  __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
+            ALOGD("%s: spkr_prot_thread t0 value %d %d",
+                   __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
+            fclose(fp);
+            /*Valid tempature range: -30C to 80C(in q6 format)
+              Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
+            for (i = 0; i < vi_feed_no_channels; i++) {
+                 if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
+                     && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
+                     && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
+                     spkr_calibrated = false;
+                     break;
+                 }
+            }
+            if (spkr_calibrated) {
+                ALOGD("%s: Spkr calibrated", __func__);
+                protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+                if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+                    ALOGE("%s: enable prot failed", __func__);
+                    handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+                } else
+                    handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+
+                audio_extn_set_boost_and_limiter(adev, spv3_enable, afe_api_version);
             }
         }
-        if (spkr_calibrated) {
-            ALOGD("%s: Spkr calibrated", __func__);
-            protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
-            if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
-                ALOGE("%s: enable prot failed", __func__);
-                handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
-            } else
-                handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+        if (handle.spkr_cal_dynamic || spkr_calibrated) {
             close(acdb_fd);
-
-            audio_extn_set_boost_and_limiter(adev, spv3_enable, afe_api_version);
-
+            handle.apply_cal = false;
             pthread_exit(0);
             return NULL;
         }
@@ -1232,6 +1309,7 @@
                     ALOGE("%s: calibrate status %s", __func__, strerror(status));
                 }
                 ALOGD("%s: spkr_prot_thread end calibration", __func__);
+                handle.trigger_cal = false;
                 break;
         }
     }
@@ -1424,6 +1502,11 @@
     th_vi_cal_data.cal_type.cal_hdr.version = VERSION_0_0;
     th_vi_cal_data.cal_type.cal_hdr.buffer_number = 0;
     th_vi_cal_data.cal_type.cal_data.mem_handle = -1;
+#ifdef MSM_SPKR_PROT_IN_V_VALI_MODE
+    /* for v-validation, same cal type is used.
+     * need this mode info to differentiate feature under test */
+    th_vi_cal_data.cal_type.cal_info.mode = MSM_SPKR_PROT_IN_FTM_MODE; // FTM mode
+#endif
 
     if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &th_vi_cal_data))
         ALOGE("%s: Error %d in getting th_vi_cal_data", __func__, errno);
@@ -1491,6 +1574,129 @@
 }
 #endif
 
+#ifdef MSM_SPKR_PROT_IN_V_VALI_MODE
+
+static int set_spkr_prot_v_vali_cfg(int wait_time, int vali_time)
+{
+    int ret = 0;
+    struct audio_cal_sp_th_vi_v_vali_cfg cal_data;
+
+    int cal_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
+    if (cal_fd < 0) {
+        ALOGE("%s: open msm_acdb failed", __func__);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    memset(&cal_data, 0, sizeof(cal_data));
+    cal_data.hdr.data_size = sizeof(cal_data);
+    cal_data.hdr.version = VERSION_0_0;
+    cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE;
+    cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+    cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+    cal_data.cal_type.cal_hdr.buffer_number = 0;
+    cal_data.cal_type.cal_info.wait_time[SP_V2_SPKR_1] = wait_time;
+    cal_data.cal_type.cal_info.wait_time[SP_V2_SPKR_2] = wait_time;
+    cal_data.cal_type.cal_info.vali_time[SP_V2_SPKR_1] = vali_time;
+    cal_data.cal_type.cal_info.vali_time[SP_V2_SPKR_2] = vali_time;
+    cal_data.cal_type.cal_info.mode = MSM_SPKR_PROT_IN_V_VALI_MODE; // V-VALI mode
+    cal_data.cal_type.cal_data.mem_handle = -1;
+    handle.v_vali_wait_time = wait_time;
+    handle.v_vali_vali_time = vali_time;
+
+    if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data))
+        ALOGE("%s: failed to set TH VI V_VALI_CFG, errno = %d", __func__, errno);
+
+    if (cal_fd > 0)
+        close(cal_fd);
+done:
+    return ret;
+}
+
+static int get_spkr_prot_v_vali_param(int cal_fd, int *status, int *vrms)
+{
+    struct audio_cal_sp_th_vi_v_vali_param cal_data;
+    int ret = 0;
+
+    if (cal_fd < 0) {
+        ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (status == NULL || vrms == NULL) {
+        ALOGE("%s: Error: status or vrms NULL", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    memset(&cal_data, 0, sizeof(cal_data));
+    cal_data.cal_type.cal_info.status[SP_V2_SPKR_1] = -EINVAL;
+    cal_data.cal_type.cal_info.status[SP_V2_SPKR_2] = -EINVAL;
+    cal_data.hdr.data_size = sizeof(cal_data);
+    cal_data.hdr.version = VERSION_0_0;
+    cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE;
+    cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+    cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+    cal_data.cal_type.cal_hdr.buffer_number = 0;
+    cal_data.cal_type.cal_data.mem_handle = -1;
+    cal_data.cal_type.cal_info.mode = MSM_SPKR_PROT_IN_V_VALI_MODE; // V-VALI mode
+
+    if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
+        ALOGE("%s: Error %d in getting V-VALI cal_data", __func__, errno);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    ALOGD("%s:: vrms = %d %d, status = %d %d\n", __func__,
+          cal_data.cal_type.cal_info.vrms_q24[SP_V2_SPKR_1],
+          cal_data.cal_type.cal_info.vrms_q24[SP_V2_SPKR_2],
+          cal_data.cal_type.cal_info.status[SP_V2_SPKR_1],
+          cal_data.cal_type.cal_info.status[SP_V2_SPKR_2]);
+
+    vrms[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.vrms_q24[SP_V2_SPKR_1];
+    vrms[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.vrms_q24[SP_V2_SPKR_2];
+    status[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.status[SP_V2_SPKR_1];
+    status[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.status[SP_V2_SPKR_2];
+
+done:
+    return ret;
+}
+#else
+
+static int set_spkr_prot_v_vali_cfg(int wait_time __unused, int vali_time __unused)
+{
+    ALOGD("%s: not supported", __func__);
+    return -ENOSYS;
+}
+
+static int get_spkr_prot_v_vali_param(int cal_fd __unused, int *status __unused,
+                                      int *vrms __unused)
+{
+    ALOGD("%s: not supported", __func__);
+    return -ENOSYS;
+}
+#endif
+
+static void* spkr_v_vali_thread()
+{
+    int ret = 0;
+    handle.v_vali_threadid = pthread_self();
+
+    if (!handle.v_vali_wait_time)
+        handle.v_vali_wait_time = SPKR_V_VALI_DEFAULT_WAIT_TIME;/*set default if not setparam */
+    if (!handle.v_vali_vali_time)
+        handle.v_vali_vali_time = SPKR_V_VALI_DEFAULT_VALI_TIME;/*set default if not setparam */
+    set_spkr_prot_v_vali_cfg(handle.v_vali_wait_time, handle.v_vali_vali_time);
+    ret = spkr_calibrate(SPKR_V_VALI_TEMP_MASK,
+                         SPKR_V_VALI_TEMP_MASK);/*use 0xfffe as temp to initiate v_vali*/
+    if (ret)
+        ALOGE("%s: failed, retry again\n", __func__);
+    pthread_exit(0);
+    handle.trigger_v_vali = false;
+    return NULL;
+}
+
 static void spkr_calibrate_signal()
 {
     pthread_mutex_lock(&handle.cal_wait_cond_mutex);
@@ -1498,6 +1704,59 @@
     pthread_mutex_unlock(&handle.cal_wait_cond_mutex);
 }
 
+static void spkr_calib_thread_create()
+{
+    int result = 0;
+
+    if (!handle.spkr_prot_enable) {
+        ALOGD("%s: Speaker protection disabled", __func__);
+        return;
+    }
+    if (handle.cal_thrd_created) {
+           result = pthread_join(handle.spkr_calibration_thread, (void **) NULL);
+           if (result < 0) {
+               ALOGE("%s:Unable to join the calibration thread", __func__);
+               return;
+           }
+           handle.cal_thrd_created = false;
+    }
+
+    result = pthread_create(&handle.spkr_calibration_thread,
+               (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
+    if (result == 0) {
+        handle.cal_thrd_created = true;
+    } else {
+        ALOGE("%s: speaker calibration thread creation failed", __func__);
+        handle.trigger_cal = false;
+    }
+}
+
+static void spkr_v_vali_thread_create()
+{
+    int result = 0;
+
+    if (!handle.spkr_prot_enable) {
+        ALOGD("%s: Speaker protection disabled", __func__);
+        return;
+    }
+    if (handle.v_vali_thrd_created) {
+        result = pthread_join(handle.spkr_v_vali_thread, (void **) NULL);
+        if (result < 0) {
+            ALOGE("%s:Unable to join the v-vali thread", __func__);
+            return;
+        }
+        handle.v_vali_thrd_created = false;
+    }
+    result = pthread_create(&handle.spkr_v_vali_thread,
+               (const pthread_attr_t *) NULL, spkr_v_vali_thread, &handle);
+    if (result == 0) {
+        handle.v_vali_thrd_created = true;
+    } else {
+        ALOGE("%s: failed to create v_vali thread\n", __func__);
+        handle.trigger_v_vali = false;
+    }
+}
+
 int audio_extn_fbsp_set_parameters(struct str_parms *parms)
 {
     int ret= 0 , err;
@@ -1505,7 +1764,7 @@
     int len;
     char *test_r = NULL;
     char *cfg_str;
-    int wait_time, ftm_time;
+    int wait_time, ftm_time, vali_time;
     char *kv_pairs = str_parms_to_str(parms);
 
     if(kv_pairs == NULL) {
@@ -1532,12 +1791,41 @@
     if (err >= 0) {
         str_parms_del(parms, AUDIO_PARAMETER_KEY_FBSP_TRIGGER_SPKR_CAL);
         if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0)) {
+            if (handle.trigger_cal)
+                goto done;
             handle.trigger_cal = true;
             spkr_calibrate_signal();
+            if (handle.spkr_cal_dynamic)
+                spkr_calib_thread_create();
+        }
+        goto done;
+    }
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FBSP_APPLY_SPKR_CAL, value,
+                            len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_FBSP_APPLY_SPKR_CAL);
+        if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0)) {
+            if (handle.apply_cal)
+                goto done;
+            handle.apply_cal = true;
+            if (handle.spkr_cal_dynamic)
+                spkr_calib_thread_create();
         }
         goto done;
     }
 
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FBSP_TRIGGER_V_VALI, value,
+                            len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_FBSP_TRIGGER_V_VALI);
+        if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0)) {
+            if (handle.trigger_v_vali)
+                goto done;
+            handle.trigger_v_vali = true;
+            spkr_v_vali_thread_create();
+        }
+        goto done;
+    }
     /* Expected key value pair is in below format:
      * AUDIO_PARAM_FBSP_CFG_WAIT_TIME=waittime;AUDIO_PARAM_FBSP_CFG_FTM_TIME=ftmtime;
      * Parse waittime and ftmtime from it.
@@ -1575,6 +1863,43 @@
             }
         }
     }
+    /* Expected key value pair is in below format:
+     * AUDIO_PARAM_FBSP_V_VALI_WAIT_TIME=waittime;AUDIO_PARAM_FBSP_V_VALI_VALI_TIME=valitime;
+     * Parse waittime and validationtime from it.
+     */
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FBSP_V_VALI_WAIT_TIME,
+                            value, len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_FBSP_V_VALI_WAIT_TIME);
+        cfg_str = strtok_r(value, ";", &test_r);
+        if (cfg_str == NULL) {
+            ALOGE("%s: incorrect wait time cfg_str", __func__);
+            ret = -EINVAL;
+            goto done;
+        }
+        wait_time = atoi(cfg_str);
+        ALOGV(" %s: cfg_str = %s, wait_time = %d", __func__, cfg_str, wait_time);
+
+        err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FBSP_V_VALI_VALI_TIME,
+                                value, len);
+        if (err >= 0) {
+            str_parms_del(parms, AUDIO_PARAMETER_KEY_FBSP_V_VALI_VALI_TIME);
+            cfg_str = strtok_r(value, ";", &test_r);
+            if (cfg_str == NULL) {
+                ALOGE("%s: incorrect validation time cfg_str", __func__);
+                ret = -EINVAL;
+                goto done;
+            }
+            vali_time = atoi(cfg_str);
+            ALOGV(" %s: cfg_str = %s, vali_time = %d", __func__, cfg_str, vali_time);
+
+            ret = set_spkr_prot_v_vali_cfg(wait_time, vali_time);
+            if (ret < 0) {
+                ALOGE("%s: set_spkr_prot_v_vali_cfg failed", __func__);
+                goto done;
+            }
+        }
+    }
 
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
@@ -1625,9 +1950,10 @@
         return;
     }
     property_get("persist.vendor.audio.speaker.prot.enable", value, "");
+    handle.spkr_cal_dynamic = property_get_bool("persist.vendor.audio.spkr.cal.dynamic", false);
     handle.spkr_prot_enable = false;
-    handle.init_check = false;
     handle.thread_exit = false;
+    handle.cal_thrd_created = false;
     if (!strncmp("true", value, 4))
        handle.spkr_prot_enable = true;
     if (!handle.spkr_prot_enable) {
@@ -1656,14 +1982,9 @@
         pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
         pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
         pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
-        ALOGD("%s:WSA Create calibration thread", __func__);
-        result = pthread_create(&handle.spkr_calibration_thread,
-        (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
-        if (result == 0) {
-            handle.init_check = true;
-        } else {
-            ALOGE("%s: speaker calibration thread creation failed", __func__);
-            destroy_thread_params();
+        if (!handle.spkr_cal_dynamic) {
+            ALOGD("%s:WSA Create calibration thread", __func__);
+            spkr_calib_thread_create();
         }
     return;
     } else {
@@ -1708,7 +2029,7 @@
         result = pthread_create(&handle.spkr_calibration_thread,
         (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
         if (result == 0) {
-            handle.init_check = true;
+            handle.cal_thrd_created = true;
         } else {
             ALOGE("%s: speaker calibration thread creation failed", __func__);
             destroy_thread_params();
@@ -1739,17 +2060,28 @@
 {
     int result = 0;
 
-    ALOGD("%s: Entering deinit init_check :%d",
-          __func__, handle.init_check);
-    if(!handle.init_check)
-        return -1;
+    ALOGD("%s: Entering deinit cal_thrd_created :%d",
+          __func__, handle.cal_thrd_created);
 
     handle.thread_exit = true;
     spkr_calibrate_signal();
-    result = pthread_join(handle.spkr_calibration_thread, (void **) NULL);
-    if (result < 0) {
-        ALOGE("%s:Unable to join the calibration thread", __func__);
-        return -1;
+    if (handle.cal_thrd_created) {
+        result = pthread_join(handle.spkr_calibration_thread,
+                              (void **) NULL);
+        if (result < 0) {
+            ALOGE("%s:Unable to join the calibration thread", __func__);
+            return -1;
+        }
+        handle.cal_thrd_created = false;
+    }
+    if (handle.v_vali_thrd_created) {
+        result = pthread_join(handle.spkr_v_vali_thread,
+                              (void **) NULL);
+        if (result < 0) {
+            ALOGE("%s:Unable to join the v_vali thread", __func__);
+            return -1;
+        }
+        handle.v_vali_thrd_created = false;
     }
     destroy_thread_params();
     memset(&handle, 0, sizeof(handle));
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 8b4a7e0..b0d10e0 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -3725,6 +3725,12 @@
     if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
         send_offload_cmd_l(out, OFFLOAD_CMD_ERROR);
     }
+
+    if (is_offload_usecase(out->usecase) && out->card_status == CARD_STATUS_OFFLINE) {
+        ALOGD("Setting previous card status if offline");
+        out->prev_card_status_offline = true;
+    }
+
     pthread_mutex_unlock(&out->lock);
 
     return status;
@@ -4948,6 +4954,9 @@
              */
             ALOGE(" ERROR: sound card not active, return error");
             ret = -EINVAL;
+        } else if (out->prev_card_status_offline) {
+            ALOGE("ERROR: previously sound card was offline,return error");
+            ret = -EINVAL;
         } else {
             ret = 0;
             adjust_frames_for_device_delay(out, dsp_frames);
@@ -5050,7 +5059,10 @@
         } else if (out->card_status == CARD_STATUS_OFFLINE) {
             *frames = out->written;
             clock_gettime(CLOCK_MONOTONIC, timestamp);
-            ret = 0;
+            if (is_offload_usecase(out->usecase))
+                ret = -EINVAL;
+            else
+                ret = 0;
         }
     }
     pthread_mutex_unlock(&out->lock);
@@ -6093,6 +6105,7 @@
     out->hal_output_suspend_supported = 0;
     out->dynamic_pm_qos_config_supported = 0;
     out->set_dual_mono = false;
+    out->prev_card_status_offline = false;
 
     if ((flags & AUDIO_OUTPUT_FLAG_BD) &&
         (property_get_bool("vendor.audio.matrix.limiter.enable", false)))
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 1675893..003690a 100755
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -355,6 +355,7 @@
     mix_matrix_params_t pan_scale_params;
     mix_matrix_params_t downmix_params;
     bool set_dual_mono;
+    bool prev_card_status_offline;
 };
 
 struct stream_in {