audio: hal: support audio zoom interface for qdsp

The interface parse audio zoom parameter and then send to QDSP via
ACDB-LOADER

Bug: 125191693
Test: set parameters successfully

Change-Id: I7a1a74deb5d7dade7faa59c5ec8e2dc3e5648537
Signed-off-by: justinweng <justinweng@google.com>
diff --git a/hal/Android.mk b/hal/Android.mk
index 3bdcf12..e6be0a6 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -207,6 +207,11 @@
     LOCAL_SRC_FILES += audio_extn/maxxaudio.c
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_AUDIO_ZOOM)), true)
+    LOCAL_CFLAGS += -DAUDIOZOOM_QDSP_ENABLED
+    LOCAL_SRC_FILES += audio_extn/audiozoom.c
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_24BITS_CAMCORDER)), true)
     LOCAL_CFLAGS += -DENABLED_24BITS_CAMCORDER
 endif
diff --git a/hal/audio_extn/audiozoom.c b/hal/audio_extn/audiozoom.c
new file mode 100644
index 0000000..86d53b4
--- /dev/null
+++ b/hal/audio_extn/audiozoom.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_audiozoom"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <expat.h>
+#include <audio_hw.h>
+#include <system/audio.h>
+#include <platform_api.h>
+#include "audio_extn.h"
+
+#include "audiozoom.h"
+
+#include <resolv.h>
+
+#define AUDIOZOOM_PRESET_FILE "/vendor/etc/audiozoom.xml"
+#define MIN_BUFSIZE 8
+
+typedef struct qdsp_audiozoom_cfg {
+    uint32_t             topo_id;
+    uint32_t             module_id;
+    uint32_t             instance_id;
+    uint32_t             zoom_param_id;
+    uint32_t             wide_param_id;
+    uint32_t             dir_param_id;
+    uint32_t             app_type;
+} qdsp_audiozoom_cfg_t;
+
+static qdsp_audiozoom_cfg_t qdsp_audiozoom;
+
+static void start_tag(void *userdata __unused, const XML_Char *tag_name,
+                      const XML_Char **attr)
+{
+    uint32_t index = 0;
+
+    if (!attr) {
+        ALOGE("%s: NULL platform/tag_name/attr", __func__);
+        return;
+    }
+
+    if (strcmp(tag_name, "topo") == 0) {
+        if (strcmp(attr[0], "id") == 0) {
+            if (attr[1])
+                qdsp_audiozoom.topo_id = atoi(attr[1]);
+        }
+    } else if (strcmp(tag_name, "module") == 0) {
+        if (strcmp(attr[0], "id") == 0) {
+            if (attr[1])
+                qdsp_audiozoom.module_id = atoi(attr[1]);
+        }
+    } else if (strcmp(tag_name, "param") == 0) {
+        while (attr[index] != NULL) {
+            if (strcmp(attr[index], "zoom_id") == 0) {
+                index++;
+                if (attr[index])
+                    qdsp_audiozoom.zoom_param_id = atoi(attr[index]);
+                else
+                    break;
+            } else if (strcmp(attr[index], "wide_id") == 0) {
+                index++;
+                if (attr[index])
+                    qdsp_audiozoom.wide_param_id = atoi(attr[index]);
+                else
+                    break;
+            } else if (strcmp(attr[index], "dir_id") == 0) {
+                index++;
+                if (attr[index])
+                    qdsp_audiozoom.dir_param_id = atoi(attr[index]);
+                else
+                    break;
+            }
+            index++;
+        }
+    } else if (strcmp(tag_name, "app_type") == 0) {
+        if (strcmp(attr[0], "id") == 0) {
+            if (attr[1])
+                qdsp_audiozoom.app_type = atoi(attr[1]);
+        }
+    } else if (strcmp(tag_name, "instance") == 0) {
+        if (strcmp(attr[0], "id") == 0) {
+            if (attr[1])
+                qdsp_audiozoom.instance_id = atoi(attr[1]);
+        }
+    } else {
+        ALOGE("%s: %s is not a supported tag", __func__, tag_name);
+    }
+
+    return;
+}
+
+static void end_tag(void *userdata __unused, const XML_Char *tag_name)
+{
+    if (strcmp(tag_name, "topo") == 0) {
+    } else if (strcmp(tag_name, "module") == 0) {
+    } else if (strcmp(tag_name, "param") == 0) {
+    } else if (strcmp(tag_name, "app_type") == 0) {
+    } else if (strcmp(tag_name, "instance") == 0) {
+    } else {
+        ALOGE("%s: %s is not a supported tag", __func__, tag_name);
+    }
+}
+
+static int audio_extn_audiozoom_parse_info(const char *filename)
+{
+    XML_Parser      parser;
+    FILE            *file;
+    int             ret = 0;
+    int             bytes_read;
+    void            *buf;
+    static const uint32_t kBufSize = 1024;
+
+    file = fopen(filename, "r");
+    if (!file) {
+        ALOGE("%s: Failed to open %s", __func__, filename);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    parser = XML_ParserCreate(NULL);
+    if (!parser) {
+        ALOGE("%s: Failed to create XML parser!", __func__);
+        ret = -ENODEV;
+        goto err_close_file;
+    }
+
+    XML_SetElementHandler(parser, start_tag, end_tag);
+
+    while (1) {
+        buf = XML_GetBuffer(parser, kBufSize);
+        if (buf == NULL) {
+            ALOGE("%s: XML_GetBuffer failed", __func__);
+            ret = -ENOMEM;
+            goto err_free_parser;
+        }
+
+        bytes_read = fread(buf, 1, kBufSize, file);
+        if (bytes_read < 0) {
+            ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read);
+             ret = bytes_read;
+            goto err_free_parser;
+        }
+
+        if (XML_ParseBuffer(parser, bytes_read,
+                            bytes_read == 0) == XML_STATUS_ERROR) {
+            ALOGE("%s: XML_ParseBuffer failed, for %s",
+                __func__, filename);
+            ret = -EINVAL;
+            goto err_free_parser;
+        }
+
+        if (bytes_read == 0)
+            break;
+    }
+
+err_free_parser:
+    XML_ParserFree(parser);
+err_close_file:
+    fclose(file);
+done:
+    return ret;
+}
+
+int audio_extn_audiozoom_set_microphone_direction(
+    struct stream_in *in, audio_microphone_direction_t dir)
+{
+    (void)in;
+    (void)dir;
+    return 0;
+}
+
+static int audio_extn_audiozoom_set_microphone_field_dimension_zoom(
+    struct stream_in *in, float zoom)
+{
+    struct audio_device *adev = in->dev;
+    struct str_parms *parms = str_parms_create();
+    uint8_t value[MIN_BUFSIZE] = {0};
+    char data[MIN_BUFSIZE * 2] = {0};
+    int32_t ret;
+
+    if (zoom > 1.0 || zoom < 0)
+        return -EINVAL;
+
+    if (qdsp_audiozoom.topo_id == 0 || qdsp_audiozoom.module_id == 0 ||
+        qdsp_audiozoom.zoom_param_id == 0)
+        return -ENOSYS;
+
+    str_parms_add_int(parms, "cal_devid", in->device);
+    str_parms_add_int(parms, "cal_apptype", in->app_type_cfg.app_type);
+    str_parms_add_int(parms, "cal_topoid", qdsp_audiozoom.topo_id);
+    str_parms_add_int(parms, "cal_moduleid", qdsp_audiozoom.module_id);
+    str_parms_add_int(parms, "cal_instanceid", qdsp_audiozoom.instance_id);
+    str_parms_add_int(parms, "cal_paramid", qdsp_audiozoom.zoom_param_id);
+
+    zoom *= 255;
+    value[0] = (uint8_t) zoom; /* Valid value is 0 to 255 */
+
+    ret = b64_ntop(value, sizeof(value), data, sizeof(data));
+    if (ret > 0) {
+        str_parms_add_str(parms, "cal_data", data);
+
+        platform_set_parameters(adev->platform, parms);
+    } else {
+        ALOGE("%s: failed to convert data to string, ret %d", __func__, ret);
+    }
+
+    str_parms_destroy(parms);
+
+    return 0;
+}
+
+static int audio_extn_audiozoom_set_microphone_field_dimension_wide_angle(
+    struct stream_in *in, float zoom)
+{
+    (void)in;
+    (void)zoom;
+    return 0;
+}
+
+int audio_extn_audiozoom_set_microphone_field_dimension(
+    struct stream_in *in, float zoom)
+{
+    if (zoom > 1.0 || zoom < -1.0)
+        return -EINVAL;
+
+    if (zoom >= 0 && zoom <= 1.0)
+        return audio_extn_audiozoom_set_microphone_field_dimension_zoom(in, zoom);
+
+    if (zoom >= -1.0 && zoom <= 0)
+        return audio_extn_audiozoom_set_microphone_field_dimension_wide_angle(in, zoom);
+
+    return 0;
+}
+
+int audio_extn_audiozoom_init()
+{
+    audio_extn_audiozoom_parse_info(AUDIOZOOM_PRESET_FILE);
+
+    ALOGV("%s: topo_id=%d, module_id=%d, instance_id=%d, zoom__id=%d, dir_id=%d, app_type=%d",
+        __func__, qdsp_audiozoom.topo_id, qdsp_audiozoom.module_id, qdsp_audiozoom.instance_id,
+        qdsp_audiozoom.zoom_param_id, qdsp_audiozoom.dir_param_id,qdsp_audiozoom.app_type);
+
+    return 0;
+}
diff --git a/hal/audio_extn/audiozoom.h b/hal/audio_extn/audiozoom.h
new file mode 100644
index 0000000..2c0ad71
--- /dev/null
+++ b/hal/audio_extn/audiozoom.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIOZOOM_H_
+#define AUDIOZOOM_H_
+
+#ifndef AUDIOZOOM_QDSP_ENABLED
+#define audio_extn_audiozoom_init()                                          (0)
+#define audio_extn_audiozoom_set_microphone_direction(stream, dir)           (-ENOSYS)
+#define audio_extn_audiozoom_set_microphone_field_dimension(stream, zoom)    (-ENOSYS)
+#else
+int audio_extn_audiozoom_init();
+int audio_extn_audiozoom_set_microphone_direction(struct stream_in *stream,
+                                           audio_microphone_direction_t dir);
+int audio_extn_audiozoom_set_microphone_field_dimension(struct stream_in *stream, float zoom);
+#endif
+
+#endif /* AUDIOZOOM_H_ */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 4b85b21..d3b4589 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -60,6 +60,7 @@
 #include "sound/compress_params.h"
 #include "audio_extn/tfa_98xx.h"
 #include "audio_extn/maxxaudio.h"
+#include "audio_extn/audiozoom.h"
 
 /* COMPRESS_OFFLOAD_FRAGMENT_SIZE must be more than 8KB and a multiple of 32KB if more than 32KB.
  * COMPRESS_OFFLOAD_FRAGMENT_SIZE * COMPRESS_OFFLOAD_NUM_FRAGMENTS must be less than 8MB. */
@@ -353,6 +354,11 @@
 static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore);
 static int set_compr_volume(struct audio_stream_out *stream, float left, float right);
 
+static int in_set_microphone_direction(const struct audio_stream_in *stream,
+                                           audio_microphone_direction_t dir);
+static int in_set_microphone_field_dimension(const struct audio_stream_in *stream, float zoom);
+
+
 static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
                                int flags __unused)
 {
@@ -1886,6 +1892,8 @@
     }
     register_in_stream(in);
     check_and_enable_effect(adev);
+    audio_extn_audiozoom_set_microphone_direction(in, in->zoom);
+    audio_extn_audiozoom_set_microphone_field_dimension(in, in->direction);
     audio_streaming_hint_end();
     audio_extn_perf_lock_release();
     ALOGV("%s: exit", __func__);
@@ -4606,17 +4614,32 @@
 
 static int in_set_microphone_direction(const struct audio_stream_in *stream,
                                            audio_microphone_direction_t dir) {
-    (void)stream;
-    (void)dir;
-    ALOGVV("%s", __func__);
-    return -ENOSYS;
+    struct stream_in *in = (struct stream_in *)stream;
+
+    ALOGVV("%s: standby %d source %d dir %d", __func__, in->standby, in->source, dir);
+
+    in->direction = dir;
+
+    if (in->standby)
+        return 0;
+
+    return audio_extn_audiozoom_set_microphone_direction(in, dir);
 }
 
 static int in_set_microphone_field_dimension(const struct audio_stream_in *stream, float zoom) {
-    (void)stream;
-    (void)zoom;
-    ALOGVV("%s", __func__);
-    return -ENOSYS;
+    struct stream_in *in = (struct stream_in *)stream;
+
+    ALOGVV("%s: standby %d source %d zoom %f", __func__, in->standby, in->source, zoom);
+
+    if (zoom > 1.0 || zoom < -1.0)
+        return -EINVAL;
+
+    in->zoom = zoom;
+
+    if (in->standby)
+        return 0;
+
+    return audio_extn_audiozoom_set_microphone_field_dimension(in, zoom);
 }
 
 static void in_update_sink_metadata(struct audio_stream_in *stream,
@@ -5642,6 +5665,8 @@
     in->standby = 1;
     in->capture_handle = handle;
     in->flags = flags;
+    in->direction = MIC_DIRECTION_UNSPECIFIED;
+    in->zoom = 0;
 
     ALOGV("%s: source %d, config->channel_mask %#x", __func__, source, config->channel_mask);
     if (source == AUDIO_SOURCE_VOICE_UPLINK ||
@@ -6324,6 +6349,7 @@
 
     audio_extn_tfa_98xx_init(adev);
     audio_extn_ma_init(adev->platform);
+    audio_extn_audiozoom_init();
 
     pthread_mutex_unlock(&adev_init_lock);
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index dbd984e..7bca070 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -298,6 +298,8 @@
     audio_format_t format;
     card_status_t card_status;
     int capture_started;
+    float zoom;
+    audio_microphone_direction_t direction;
 
     struct stream_app_type_cfg app_type_cfg;
 
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index eb4bb4a..21f7cf5 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -961,6 +961,15 @@
         cal->param_id = (uint32_t)strtoul(value, NULL, 0);
         ret = ret | 0x100;
     }
+#ifdef PLATFORM_SM8150
+    err = str_parms_get_str(parms, "cal_instanceid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_instanceid");
+        cal->instance_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x200;
+    }
+#endif
+
     return ret;
 }