audio: hal: Add AVD Drift Query support

- Add out set_param_data() and get_param_data() API.
- This API is needs to be used for client to set/get out stream
  specific params.
- Currently it only supports get/set of afew out params. Going forward all
  the new param needs to be supported by these APIs.
  In future key,value pair based set/get param API will be deprecated.
- Use get_param_data() API to query avtimer vs device drift support.
  Currently this API only supports drift query for HDMI device as for
  other device DSP can directly do the drift correction.

Change-Id: Iaec94b7220cdaa243d4b616fb727aae7179539d2
diff --git a/hal/audio_extn/audio_defs.h b/hal/audio_extn/audio_defs.h
index ae90cb3..dc9e5bd 100644
--- a/hal/audio_extn/audio_defs.h
+++ b/hal/audio_extn/audio_defs.h
@@ -138,16 +138,42 @@
    struct aptx_dec_bt_addr bt_addr;
 };
 
+struct audio_avt_device_drift_param {
+   /* Flag to indicate if resync is required on the client side for
+    * drift correction. Flag is set to TRUE for the first get_param response
+    * after device interface starts. This flag value can be used by client
+    * to identify if device interface restart has happened and if any
+    * re-sync is required at their end for drift correction.
+    */
+    uint32_t        resync_flag;
+    /* Accumulated drift value in microseconds. This value is updated
+     * every 100th ms.
+     * Positive drift value indicates AV timer is running faster than device.
+     * Negative drift value indicates AV timer is running slower than device.
+     */
+    int32_t         avt_device_drift_value;
+    /* Lower 32 bits of the 64-bit absolute timestamp of reference
+     * timer in microseconds.
+     */
+    uint32_t        ref_timer_abs_ts_lsw;
+    /* Upper 32 bits of the 64-bit absolute timestamp of reference
+     * timer in microseconds.
+     */
+    uint32_t        ref_timer_abs_ts_msw;
+};
+
 typedef union {
     struct source_tracking_param st_params;
     struct sound_focus_param sf_params;
     struct aptx_dec_param aptx_params;
+    struct audio_avt_device_drift_param drift_params;
 } audio_extn_param_payload;
 
 typedef enum {
     AUDIO_EXTN_PARAM_SOURCE_TRACK,
     AUDIO_EXTN_PARAM_SOUND_FOCUS,
-    AUDIO_EXTN_PARAM_APTX_DEC
+    AUDIO_EXTN_PARAM_APTX_DEC,
+    AUDIO_EXTN_PARAM_AVT_DEVICE_DRIFT
 } audio_extn_param_id;
 
 #endif /* AUDIO_DEFS_H */
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c114d2d..bbdd129 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -820,4 +820,7 @@
 int audio_extn_set_aptx_dec_params(struct aptx_dec_param *payload);
 #endif
 
+int audio_extn_utils_get_avt_device_drift(
+                struct audio_usecase *usecase,
+                struct audio_avt_device_drift_param *drift_param);
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index b12ab8e..91e83af 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -155,6 +155,17 @@
 #endif
 };
 
+/* payload structure avt_device drift query */
+struct audio_avt_device_drift_stats {
+    uint32_t       minor_version;
+    /* Indicates the device interface direction as either
+     * source (Tx) or sink (Rx).
+    */
+    uint16_t        device_direction;
+    /*params exposed to client */
+    struct audio_avt_device_drift_param drift_param;
+};
+
 static char bTable[BASE_TABLE_SIZE] = {
             'A','B','C','D','E','F','G','H','I','J','K','L',
             'M','N','O','P','Q','R','S','T','U','V','W','X',
@@ -1548,3 +1559,75 @@
 
 }
 #endif
+
+int audio_extn_utils_get_avt_device_drift(
+                struct audio_usecase *usecase,
+                struct audio_avt_device_drift_param *drift_param)
+{
+    int ret = 0, count = 0;
+    char avt_device_drift_mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
+    struct mixer_ctl *ctl = NULL;
+    struct audio_avt_device_drift_stats drift_stats;
+    struct audio_device *adev = NULL;
+
+    if (usecase != NULL && usecase->type == PCM_PLAYBACK) {
+        adev = usecase->stream.out->dev;
+        switch(usecase->out_snd_device) {
+            case SND_DEVICE_OUT_HDMI:
+                strlcpy(avt_device_drift_mixer_ctl_name,
+                        "HDMI RX Drift",
+                        MIXER_PATH_MAX_LENGTH);
+                break;
+            case SND_DEVICE_OUT_DISPLAY_PORT:
+                strlcpy(avt_device_drift_mixer_ctl_name,
+                        "DISPLAY Port RX Drift",
+                        MIXER_PATH_MAX_LENGTH);
+                break;
+            default :
+                ALOGE("%s: Unsupported device %d",__func__,
+                        usecase->stream.out->devices);
+                ret = -EINVAL;
+        }
+    } else {
+        ALOGE("%s: Invalid usecase %d ",__func__, usecase->type);
+        ret = -EINVAL;
+    }
+
+    if(ret)
+        goto done;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, avt_device_drift_mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+                __func__, avt_device_drift_mixer_ctl_name);
+
+        ret = -EINVAL;
+        goto done;
+    }
+
+    ALOGV("%s: Getting AV Timer vs Device Drift mixer ctrl name %s", __func__,
+            avt_device_drift_mixer_ctl_name);
+
+    mixer_ctl_update(ctl);
+    count = mixer_ctl_get_num_values(ctl);
+    if (count != sizeof(struct audio_avt_device_drift_stats)) {
+        ALOGE("%s: mixer_ctl_get_num_values() invalid drift_stats data size",
+                __func__);
+
+        ret = -EINVAL;
+        goto done;
+    }
+
+    ret = mixer_ctl_get_array(ctl, (void *)&drift_stats, count);
+    if (ret != 0) {
+        ALOGE("%s: mixer_ctl_get_array() failed to get drift_stats Params",
+                __func__);
+
+        ret = -EINVAL;
+        goto done;
+    }
+    memcpy(drift_param, &drift_stats.drift_param,
+            sizeof(struct audio_avt_device_drift_param));
+done:
+    return ret;
+}
diff --git a/hal/audio_hw_extn_api.c b/hal/audio_hw_extn_api.c
index f36d85d..25c4503 100644
--- a/hal/audio_hw_extn_api.c
+++ b/hal/audio_hw_extn_api.c
@@ -48,6 +48,61 @@
 };
 #endif
 
+static void lock_output_stream(struct stream_out *out)
+{
+    pthread_mutex_lock(&out->pre_lock);
+    pthread_mutex_lock(&out->lock);
+    pthread_mutex_unlock(&out->pre_lock);
+}
+
+/* API to send playback stream specific config parameters */
+int qahwi_out_set_param_data(struct audio_stream_out *stream __unused,
+                             audio_extn_param_id param_id __unused,
+                             audio_extn_param_payload *payload __unused) {
+    return -ENOSYS;
+}
+
+/* API to get playback stream specific config parameters */
+int qahwi_out_get_param_data(struct audio_stream_out *stream,
+                             audio_extn_param_id param_id,
+                             audio_extn_param_payload *payload)
+{
+    int ret = -EINVAL;
+    struct stream_out *out = (struct stream_out *)stream;
+    struct audio_usecase *uc_info;
+
+    if (!stream || !payload) {
+        ALOGE("%s:: Invalid Param",__func__);
+        return ret;
+    }
+
+    lock_output_stream(out);
+    ALOGD("%s: enter: stream (%p) usecase(%d: %s) param_id %d", __func__,
+           stream, out->usecase, use_case_table[out->usecase], param_id);
+
+    switch (param_id) {
+        case AUDIO_EXTN_PARAM_AVT_DEVICE_DRIFT:
+            uc_info = get_usecase_from_list(out->dev, out->usecase);
+            if (uc_info == NULL) {
+                ALOGE("%s: Could not find the usecase (%d) in the list",
+                       __func__, out->usecase);
+                ret = -EINVAL;
+            } else {
+                ret = audio_extn_utils_get_avt_device_drift(uc_info,
+                        (struct audio_avt_device_drift_param *)payload);
+                if(ret)
+                    ALOGE("%s:: avdrift query failed error %d", __func__, ret);
+            }
+            break;
+        default:
+            ALOGE("%s:: unsupported param_id %d", __func__, param_id);
+            break;
+    }
+
+    pthread_mutex_unlock(&out->lock);
+    return ret;
+}
+
 int qahwi_get_param_data(const struct audio_hw_device *adev,
                          audio_extn_param_id param_id,
                          audio_extn_param_payload *payload)
diff --git a/qahw_api/inc/qahw_api.h b/qahw_api/inc/qahw_api.h
index 8a65686..0f3fc50 100644
--- a/qahw_api/inc/qahw_api.h
+++ b/qahw_api/inc/qahw_api.h
@@ -141,6 +141,16 @@
 char* qahw_out_get_parameters(const qahw_stream_handle_t *stream,
                                const char *keys);
 
+/* API to set playback stream specific config parameters */
+int qahw_out_set_param_data(qahw_stream_handle_t *out_handle,
+                            qahw_param_id param_id,
+                            qahw_param_payload *payload);
+
+/* API to get playback stream specific config parameters */
+int qahw_out_get_param_data(qahw_stream_handle_t *out_handle,
+                            qahw_param_id param_id,
+                            qahw_param_payload *payload);
+
 /*
  * Return the audio hardware driver estimated latency in milliseconds.
  */
@@ -270,7 +280,6 @@
 
 int qahw_close_input_stream(qahw_stream_handle_t *in_handle);
 
-
 /*
  * Return the sampling rate in Hz - eg. 44100.
  */
diff --git a/qahw_api/inc/qahw_defs.h b/qahw_api/inc/qahw_defs.h
index eb500a2..63dc4da 100644
--- a/qahw_api/inc/qahw_defs.h
+++ b/qahw_api/inc/qahw_defs.h
@@ -243,19 +243,37 @@
    struct aptx_dec_bt_addr bt_addr;
 };
 
+struct qahw_avt_device_drift_param {
+   /* Flag to indicate if resync is required on the client side for
+    * drift correction. Flag is set to TRUE for the first get_param response
+    * after device interface starts. This flag value can be used by client
+    * to identify if device interface restart has happened and if any
+    * re-sync is required at their end for drift correction.
+    */
+    uint32_t        resync_flag;
+    /* Accumulated drift value in microseconds.
+     * Positive drift value indicates AV timer is running faster than device.
+     * Negative drift value indicates AV timer is running slower than device.
+     */
+    int32_t         avt_device_drift_value;
+    /* 64-bit absolute timestamp of reference */
+    uint64_t        ref_timer_abs_ts;
+};
+
 typedef union {
     struct qahw_source_tracking_param st_params;
     struct qahw_sound_focus_param sf_params;
     struct qahw_aptx_dec_param aptx_params;
+    struct qahw_avt_device_drift_param drift_params;
 } qahw_param_payload;
 
 typedef enum {
     QAHW_PARAM_SOURCE_TRACK,
     QAHW_PARAM_SOUND_FOCUS,
-    QAHW_PARAM_APTX_DEC
+    QAHW_PARAM_APTX_DEC,
+    QAHW_PARAM_AVT_DEVICE_DRIFT /* PARAM to query AV timer vs device drift */
 } qahw_param_id;
 
-
 __END_DECLS
 
 #endif  // QTI_AUDIO_HAL_DEFS_H
diff --git a/qahw_api/src/qahw.c b/qahw_api/src/qahw.c
index 393dba9..c5cd636 100644
--- a/qahw_api/src/qahw.c
+++ b/qahw_api/src/qahw.c
@@ -47,15 +47,23 @@
  */
 #define QAHW_MODULE_API_VERSION_CURRENT QAHW_MODULE_API_VERSION_0_0
 
-typedef uint64_t (*qahwi_in_read_v2_t)(audio_stream_in_t *in, void* buffer,
-                                       size_t bytes, int64_t *timestamp);
-
 typedef int (*qahwi_get_param_data_t) (const audio_hw_device_t *,
                               qahw_param_id, qahw_param_payload *);
 
 typedef int (*qahwi_set_param_data_t) (audio_hw_device_t *,
                               qahw_param_id, qahw_param_payload *);
 
+typedef uint64_t (*qahwi_in_read_v2_t)(audio_stream_in_t *in, void* buffer,
+                                       size_t bytes, int64_t *timestamp);
+
+typedef int (*qahwi_out_set_param_data_t)(struct audio_stream_out *out,
+                                      qahw_param_id param_id,
+                                      qahw_param_payload *payload);
+
+typedef int (*qahwi_out_get_param_data_t)(struct audio_stream_out *out,
+                                      qahw_param_id param_id,
+                                      qahw_param_payload *payload);
+
 typedef struct {
     audio_hw_device_t *audio_device;
     char module_name[MAX_MODULE_NAME_LENGTH];
@@ -80,6 +88,8 @@
     qahw_module_t *module;
     struct listnode list;
     pthread_mutex_t lock;
+    qahwi_out_set_param_data_t qahwi_out_get_param_data;
+    qahwi_out_get_param_data_t qahwi_out_set_param_data;
 } qahw_stream_out_t;
 
 typedef struct {
@@ -397,6 +407,67 @@
     return str_param;
 }
 
+/* API to get playback stream specific config parameters */
+int qahw_out_set_param_data(qahw_stream_handle_t *out_handle,
+                            qahw_param_id param_id,
+                            qahw_param_payload *payload)
+{
+    int rc = -EINVAL;
+    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
+    audio_stream_out_t *out = NULL;
+
+    if (!payload) {
+        ALOGE("%s::Invalid param", __func__);
+        goto exit;
+    }
+
+    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
+        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_stream_out->lock);
+    out = qahw_stream_out->stream;
+    if (qahw_stream_out->qahwi_out_set_param_data) {
+        rc = qahw_stream_out->qahwi_out_set_param_data(out, param_id, payload);
+    } else {
+        rc = -ENOSYS;
+        ALOGW("%s not supported", __func__);
+    }
+    pthread_mutex_unlock(&qahw_stream_out->lock);
+
+exit:
+    return rc;
+}
+
+/* API to get playback stream specific config parameters */
+int qahw_out_get_param_data(qahw_stream_handle_t *out_handle,
+                            qahw_param_id param_id,
+                            qahw_param_payload *payload)
+{
+    int rc = -EINVAL;
+    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
+    audio_stream_out_t *out = NULL;
+
+    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
+        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_stream_out->lock);
+    out = qahw_stream_out->stream;
+    if (qahw_stream_out->qahwi_out_get_param_data) {
+        rc = qahw_stream_out->qahwi_out_get_param_data(out, param_id, payload);
+    } else {
+        rc = -ENOSYS;
+        ALOGW("%s not supported", __func__);
+    }
+    pthread_mutex_unlock(&qahw_stream_out->lock);
+
+exit:
+    return rc;
+}
+
 uint32_t qahw_out_get_latency(const qahw_stream_handle_t *out_handle)
 {
     uint32_t latency = 0;
@@ -1373,7 +1444,29 @@
         *out_handle = (void *)qahw_stream_out;
         pthread_mutex_init(&qahw_stream_out->lock, (const pthread_mutexattr_t *)NULL);
         list_add_tail(&qahw_module->out_list, &qahw_stream_out->list);
-    }
+
+        /* clear any existing errors */
+        const char *error;
+        dlerror();
+        qahw_stream_out->qahwi_out_get_param_data = (qahwi_out_get_param_data_t)
+                                                 dlsym(qahw_module->module->dso,
+                                                 "qahwi_out_get_param_data");
+        if ((error = dlerror()) != NULL) {
+            ALOGI("%s: dlsym error %s for qahwi_out_get_param_data",
+                   __func__, error);
+            qahw_stream_out->qahwi_out_get_param_data = NULL;
+        }
+
+        dlerror();
+        qahw_stream_out->qahwi_out_set_param_data = (qahwi_out_set_param_data_t)
+                                                 dlsym(qahw_module->module->dso,
+                                                 "qahwi_out_set_param_data");
+        if ((error = dlerror()) != NULL) {
+            ALOGI("%s: dlsym error %s for qahwi_out_set_param_data",
+                   __func__, error);
+            qahw_stream_out->qahwi_out_set_param_data = NULL;
+        }
+}
 
 exit:
     pthread_mutex_unlock(&qahw_module->lock);
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 4a7a382..2237d1d 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -120,6 +120,11 @@
     struct wav_header hdr;
 };
 
+struct drift_data {
+    qahw_module_handle_t *out_handle;
+    volatile bool thread_exit;
+};
+
 typedef struct {
     qahw_module_handle_t *qahw_mod_handle;
     audio_io_handle_t handle;
@@ -137,6 +142,7 @@
     char *kvpair_values;
     bool flags_set;
     int effect_index;
+    bool drift_query;
     thread_func_t ethread_func;
     thread_data_t *ethread_data;
     cmd_data_t cmd_data;
@@ -152,8 +158,6 @@
 const char *log_filename = NULL;
 float vol_level = 0.01;
 struct proxy_data proxy_params;
-bool proxy_thread_active;
-pthread_t proxy_thread;
 pthread_t playback_thread[MAX_PLAYBACK_STREAMS];
 bool thread_active[MAX_PLAYBACK_STREAMS] = { false };
 
@@ -384,6 +388,30 @@
     return 0;
 }
 
+void *drift_read(void* data)
+{
+    struct drift_data* params = (struct drift_data*) data;
+    qahw_stream_handle_t* out_handle = params->out_handle;
+    struct qahw_avt_device_drift_param drift_param;
+    int rc = -EINVAL;
+
+    printf("drift quried at 100ms interval \n");
+    while (!(params->thread_exit)) {
+        memset(&drift_param, 0, sizeof(struct qahw_avt_device_drift_param));
+        rc = qahw_out_get_param_data(out_handle, QAHW_PARAM_AVT_DEVICE_DRIFT,
+                (qahw_param_payload *)&drift_param);
+        if (!rc) {
+            printf("resync flag = %d, drift %d, av timer %lld\n",
+                    drift_param.resync_flag,
+                    drift_param.avt_device_drift_value,
+                    drift_param.ref_timer_abs_ts);
+        } else {
+            printf("drift query failed rc = %d retry after 100ms\n", rc);
+        }
+
+        usleep(100000);
+    }
+}
 
 /* Entry point function for stream playback
  * Opens the stream
@@ -397,6 +425,12 @@
     int rc = 0;
     stream_config *params = (stream_config*) stream_data;
     const char* stream_name = "output_stream";
+    bool proxy_thread_active = false;
+    pthread_t proxy_thread;
+
+    bool drift_thread_active = false;
+    pthread_t drift_query_thread;
+    struct drift_data drift_params;
 
     rc = qahw_open_output_stream(params->qahw_mod_handle,
                              params->handle,
@@ -521,6 +555,17 @@
         rc = pthread_create(&proxy_thread, NULL, proxy_read, (void *)&proxy_params);
         if (!rc)
             proxy_thread_active = true;
+    } else if (params->drift_query &&
+              (params->output_device & AUDIO_DEVICE_OUT_HDMI) &&
+              !drift_thread_active) {
+        drift_params.out_handle = params->out_handle;
+        drift_params.thread_exit = false;
+        fprintf(log_file, "create thread to read avtime vs hdmi drift\n");
+        rc = pthread_create(&drift_query_thread, NULL, drift_read, (void *)&drift_params);
+        if (!rc)
+            drift_thread_active = true;
+        else
+            fprintf(log_file, "drift query thread creation failure %d\n", rc);
     }
 
     rc = qahw_out_set_volume(params->out_handle, vol_level, vol_level);
@@ -615,6 +660,11 @@
         pthread_join(proxy_thread, NULL);
     }
 
+    if (drift_thread_active) {
+        usleep(500000);
+        drift_params.thread_exit = true;
+        pthread_join(drift_query_thread, NULL);
+    }
     rc = qahw_out_standby(params->out_handle);
     if (rc) {
         fprintf(log_file, "stream %d: out standby failed %d \n", params->stream_index, rc);
@@ -973,6 +1023,7 @@
     printf(" -e  --effect-type <effect type>           - Effect used for test\n");
     printf("                                             0:bassboost 1:virtualizer 2:equalizer 3:visualizer(NA) 4:reverb 5:audiosphere others:null\n\n");
     printf(" -A  --bt-addr <bt device addr>            - Required to set bt device adress for aptx decoder\n\n");
+    printf(" -q  --query drift                         - Required for querying avtime vs hdmi drift\n");
     printf(" -P                                        - Argument to do multi-stream playback, currently 2 streams are supported to run concurrently\n");
     printf("                                             Put -P and mention required attributes for the next stream\n");
     printf(" \n Examples \n");
@@ -1071,7 +1122,6 @@
     int i = 0;
     int j = 0;
     kpi_mode = false;
-    proxy_thread_active = false;
 
     log_file = stdout;
     proxy_params.acp.file_name = "/data/pcm_dump.wav";
@@ -1098,6 +1148,7 @@
         {"plus",          no_argument,          0, 'P'},
         {"effect-path",   required_argument,    0, 'e'},
         {"bt-addr",       required_argument,    0, 'A'},
+        {"query drift",   no_argument,          0, 'q'},
         {"help",          no_argument,          0, 'h'},
         {0, 0, 0, 0}
     };
@@ -1121,7 +1172,7 @@
 
     while ((opt = getopt_long(argc,
                               argv,
-                              "-f:r:c:b:d:v:l:t:a:w:k:PD:KF:e:A:h",
+                              "-f:r:c:b:d:v:l:t:a:w:k:PD:KF:e:A:qh",
                               long_options,
                               &option_index)) != -1) {
 
@@ -1197,6 +1248,9 @@
         case 'A':
             ba = optarg;
             break;
+        case 'q':
+             stream_param[i].drift_query = true;
+             break;
         case 'P':
             if(i >= MAX_PLAYBACK_STREAMS - 1) {
                 fprintf(log_file, "cannot have more than %d streams\n", MAX_PLAYBACK_STREAMS);