/*
 * Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved.
 * Description: audio hal top layer source file.
 * Author: Hisilicon
 * Create: 2020-05-11
 * Notes: NA
 * History: 2020-05-11 yinyingcai for CodingStyle
*/
#define LOG_TAG "audio_hal"
#define LOG_NDEBUG 0

#include "audio_hw.h"
#include <sys/time.h>
#include <cutils/str_parms.h>
#include <cutils/properties.h>
#include <system/audio.h>
#include <hardware/audio.h>
#include <utils/Timers.h>
#include <securec.h>

#include "vnaudio_dbg.h"
#include "hpo.h"
#include "hpi.h"
#include "audio_custom.h"
#include "audio_gain.h"

#ifdef TV_TYPE
#include "audio_out_avplay.h"
#include "hdo.h"
#include "hci.h"
#include "aiao_wrap.h"
#endif

#include "audio_utils.h"

struct channe_type {
    const char *name;
    uint32_t value;
};

static const struct channe_type g_channel_type_table[] = {
    { "AUDIO_CHANNEL_OUT_STEREO", AUDIO_CHANNEL_OUT_STEREO },
    { "AUDIO_CHANNEL_OUT_5POINT1", AUDIO_CHANNEL_OUT_5POINT1 },
    { "AUDIO_CHANNEL_OUT_7POINT1", AUDIO_CHANNEL_OUT_7POINT1 },
    { "AUDIO_CHANNEL_IN_MONO", AUDIO_CHANNEL_IN_MONO },
    { "AUDIO_CHANNEL_IN_STEREO", AUDIO_CHANNEL_IN_STEREO },
    { "AUDIO_CHANNEL_IN_FRONT_BACK", AUDIO_CHANNEL_IN_FRONT_BACK },
    { "AUDIO_CHANNEL_INDEX_MASK_1", AUDIO_CHANNEL_INDEX_MASK_1 },
    { "AUDIO_CHANNEL_INDEX_MASK_2", AUDIO_CHANNEL_INDEX_MASK_2 },
    { "AUDIO_CHANNEL_INDEX_MASK_3", AUDIO_CHANNEL_INDEX_MASK_3 },
    { "AUDIO_CHANNEL_INDEX_MASK_4", AUDIO_CHANNEL_INDEX_MASK_4 },
    { "AUDIO_CHANNEL_INDEX_MASK_5", AUDIO_CHANNEL_INDEX_MASK_5 },
    { "AUDIO_CHANNEL_INDEX_MASK_6", AUDIO_CHANNEL_INDEX_MASK_6 },
    { "AUDIO_CHANNEL_INDEX_MASK_7", AUDIO_CHANNEL_INDEX_MASK_7 },
    { "AUDIO_CHANNEL_INDEX_MASK_8", AUDIO_CHANNEL_INDEX_MASK_8 },
};

#define DEFAULT_OUTPUT_SAMPLATE_RATE 48000
#define PHONE_OUTPUT_SAMPLATE_RATE 16000
#define DEFAULT_OUTPUT_CHANNEL 2
#define PHONE_OUTPUT_CHANNEL 1

#define NORMAL_OUTPUT_PERIOD_SIZE 480
#define NORMAL_OUTPUT_PERIOD_COUNT 4
#define PHONE_TX_OUTPUT_PERIOD_SIZE 320
#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 256 // fastmixer
#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
#define AI_INPUT_PERIOD_SIZE 0x200 // fastmixer
#define CHANNEL_EXCHANGE_BUFFER_HAL_SIZE  (1024*512)
#define DEFAULT_INPUT_BUFFER_SIZE 4096
#define DEFAULT_VALUE_ARRAY_SIZE 256
#define DEFAULT_OUT_PARAMETER_SIZE 256
#define DEFAULT_OUTPUT_PERIOD_SIZE 1024
#define CAST_INPUT_PERIOD_SIZE 960
#define DEFAULT_INPUT_PERIOD_SIZE 512
#define DEFAULT_OUTPUT_PERIOD_COUNT 4
#define CNT_MOD 100000
#define DEFAULT_PARAM_SIZE 32

#define AUDIO_PARAMETER_GTS_MODE               "GTSMODE"
#define AUDIO_PARAMETER_DRC_MODE               "DRCMODE"
#define AUDIO_PARAMETER_DRC_RANGE              "DRCRANGE"
#define AUDIO_PARAMETER_STREAM_DOLBYSTREAMINFO "DOLBYSTREAMINFO"
#define PARAMETER_SET_HDMI_OUTPUT              "SetHdmiOutput"
#define PARAMETER_SET_SPDIF_OUTPUT             "SetSpdifOutput"
#define PARAMETER_SET_HBR_DEGRADE_OUTPUT       "SetHbrDegradeOutput"
#define AUDIO_PARAMETER_SAMPLEING_RATE         "sampling_rate"
#define AUDIO_PARAMETER_CHANNELS               "channels"
#define AUDIO_PARAMETER_FORMAT                 "format"
#define AUDIO_PARAMETER_PATCH_SINK_DEVICE      "patch_sink_device"

/* Get a new HW synchronization source identifier.
 * Return a valid source (positive integer) or AUDIO_HW_SYNC_INVALID if an error occurs
 * or no HW sync is available. */
#define AUDIO_PARAMETER_HW_AV_SYNC "hw_av_sync"

#define HAL_SYSTEMCERT_PROPERTY "persist.vendor.audiohal.dolby.dmacert.enable"

#define SOC_PRODUCT_TYPE_PROPERTY "vendor.prop.product_type"
#define TABLET_PRODUCT             "tablet"
#define STB_PRODUCT                "stb"
#define TV_PRODUCT                 "tv"

#define PRIMARY_DEFAULT_DEC_DB 0
#define PRIMARY_DEFAULT_INT_DB 0

#define DELAYCHECK_PRINT_BYTES 8
#define DELAYCHECK_BYTES_STR_MAX_LEN 64

static struct pcm_config g_pcm_config_ao_normal = {
    .channels = DEFAULT_OUTPUT_CHANNEL,
    .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
    .period_size = NORMAL_OUTPUT_PERIOD_SIZE,  /* means buffersize for AF */
    .period_count = NORMAL_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = NORMAL_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times threshold */
    .stop_threshold = INT_MAX,
    .avail_min = NORMAL_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times avail min */
};

static struct pcm_config g_pcm_config_phone_tx = {
    .channels = PHONE_OUTPUT_CHANNEL,
    .rate = PHONE_OUTPUT_SAMPLATE_RATE,
    .period_size = PHONE_TX_OUTPUT_PERIOD_SIZE,  /* means buffersize for AF */
    .period_count = NORMAL_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = NORMAL_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times threshold */
    .stop_threshold = INT_MAX,
    .avail_min = NORMAL_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times avail min */
};

static struct pcm_config g_pcm_config_ao_low_latency = {
    .channels = DEFAULT_OUTPUT_CHANNEL,
    .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
    .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,  /* means buffersize for AF */
    .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times threshold */
    .stop_threshold = INT_MAX,
    .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 2, /* make period size 2 times avail min */
};

#ifdef TV_TYPE
static struct pcm_config g_pcm_config_ao_direct_output = {
    .channels = DEFAULT_OUTPUT_CHANNEL,
    .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
    .period_size = DEFAULT_OUTPUT_PERIOD_SIZE,
    .period_count = DEFAULT_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
};
#endif

#ifdef TV_TYPE
static struct pcm_config g_pcm_config_hdmi_multi = {
    .channels = AUDIO_5POINT1_CHANNELS,  /* changed when the stream is opened */
    .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
    .period_size = DEFAULT_OUTPUT_PERIOD_SIZE,
    .period_count = DEFAULT_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
};
#endif

#define CHECK_LOCK_RET(x)                                   \
    do {                                                    \
        if ((x) != 0) {                                     \
            ALOGE("%s: mutex lock fail", __func__);         \
        }                                                   \
    } while (0)

#define CHECK_UNLOCK_RET(x)                                 \
    do {                                                    \
        if ((x) != 0) {                                     \
            ALOGE("%s: mutex unlock fail", __func__);       \
        }                                                   \
    } while (0)


static int check_input_parameters(uint32_t sample_rate,
                                  audio_format_t format,
                                  uint32_t channel)
{
    if (format != AUDIO_FORMAT_PCM_16_BIT) {
        ALOGE("Invalid format: 0x%x", format);
        return -EINVAL;
    }

    if ((channel < AUDIO_MONO_CHANNELS) || (channel > AUDIO_STEREO_CHANNELS)) {
        ALOGE("Invalid channel: %d", channel);
        return -EINVAL;
    }

    switch (sample_rate) {
        case SAMPLE_RATE_8000:
        case SAMPLE_RATE_11025:
        case SAMPLE_RATE_12000:
        case SAMPLE_RATE_16000:
        case SAMPLE_RATE_22050:
        case SAMPLE_RATE_24000:
        case SAMPLE_RATE_32000:
        case SAMPLE_RATE_44100:
        case SAMPLE_RATE_48000:
            return 0;
        default:
            ALOGE("Invalid sample_rate: %d", sample_rate);
            return -EINVAL;
    }
}

static void get_channels_string(bool *first, char *value, int index)
{
    int ret;
    if (!(*first)) {
        ret = strcat_s(value, DEFAULT_VALUE_ARRAY_SIZE, "|");
        if (ret != EOK) {
            ALOGE("strcat_s | failed(0x%x)", ret);
        }
    }
    ret = strcat_s(value, DEFAULT_VALUE_ARRAY_SIZE, g_channel_type_table[index].name);
    if (ret != 0) {
        ALOGE("strcat_s %s failed(0x%x)", g_channel_type_table[index].name, ret);
    }
    *first = false;
}

static bool get_parameter_channels(struct str_parms *query,
                                   struct str_parms *reply,
                                   const audio_channel_mask_t *supported_channel_masks,
                                   int array_size)
{
    int i = 0;
    size_t j;
    int tmp_ret;
    bool ret = false;
    bool first = true;
    char value[DEFAULT_VALUE_ARRAY_SIZE];

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS) == 0) {
        return ret;
    }
    ret = true;
    value[0] = '\0';
    while (supported_channel_masks[i] != 0) {
        size_t channel_array_size = sizeof(g_channel_type_table) / sizeof((g_channel_type_table)[0]);
        for (j = 0; j < channel_array_size; j++) {
            if (g_channel_type_table[j].value != supported_channel_masks[i]) {
                continue;
            }
            get_channels_string(&first, value, (int)j);
            break;
        }
        i++;
        if (i >= array_size) {
            return false;
        }
    }
    tmp_ret = str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
    if (tmp_ret != 0) {
        ALOGE("str_parms_add_str AUDIO_PARAMETER_STREAM_SUP_CHANNELS failed(0x%x)", tmp_ret);
    }
    return ret;
}

static bool get_parameter_formats(struct str_parms *query,
                                  struct str_parms *reply,
                                  const audio_format_t *supported_formats,
                                  int array_size __unused)
{
    int ret = -EINVAL;
    char value[DEFAULT_VALUE_ARRAY_SIZE];

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS) != 0) {
        int tmp_ret = -EINVAL;
        ret = 0;
        value[0] = '\0';
        switch (supported_formats[0]) {
            case AUDIO_FORMAT_PCM_16_BIT:
                if (strcat_s(value, DEFAULT_VALUE_ARRAY_SIZE, "AUDIO_FORMAT_PCM_16_BIT") != EOK) {
                    ALOGE("strcat_s AUDIO_FORMAT_PCM_16_BIT faild");
                }
                break;
            case AUDIO_FORMAT_PCM_24_BIT_PACKED:
                if (strcat_s(value, DEFAULT_VALUE_ARRAY_SIZE, "AUDIO_FORMAT_PCM_24_BIT_PACKED") != EOK) {
                    ALOGE("strcat_s AUDIO_FORMAT_PCM_24_BIT faild");
                }
                break;
            case AUDIO_FORMAT_PCM_32_BIT:
                if (strcat_s(value, DEFAULT_VALUE_ARRAY_SIZE, "AUDIO_FORMAT_PCM_32_BIT") != EOK) {
                    ALOGE("strcat_s AUDIO_FORMAT_PCM_32_BIT faild");
                }
                break;
            default:
                ALOGE("%s: unsupported format %#x", __func__, supported_formats[0]);
                break;
        }
        tmp_ret = str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
        if (tmp_ret != 0) {
            ALOGE("str_parms_add_str AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES failed(0x%x)", tmp_ret);
        }
    }

    return (ret == 0) ? true : false;
}

static bool get_parameter_rates(struct str_parms *query,
                                struct str_parms *reply,
                                const uint32_t *supported_sample_rates,
                                int array_size)
{
    int i;
    char value[DEFAULT_VALUE_ARRAY_SIZE];
    int ret = -EINVAL;
    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != 0) {
        int tmp_ret;
        size_t cursor = 0;
        ret = 0;
        value[0] = '\0';
        i = 0;
        while (supported_sample_rates[i] != 0) {
            size_t avail = sizeof(value) - cursor;
            if (cursor >= DEFAULT_VALUE_ARRAY_SIZE) {
                return false;
            }
            // destMax should > count, usually count=destMax - 1
            ret = snprintf_s(value + cursor, avail, avail - 1, "%s%u",
                             cursor > 0 ? "|" : "",
                             supported_sample_rates[i]);
            if (ret < 0 || ret >= (int)avail) {
                // if cursor is at the last element of the array
                //    overwrite with \0 is duplicate work as
                //    snprintf already put a \0 in place.
                // else
                //    we had space to write the '|' at value[cursor]
                //    (which will be overwritten) or no space to fill
                //    the first element (=> cursor == 0)
                value[cursor] = '\0';
                break;
            }
            cursor += (size_t)ret;
            i++;
            if (i >= array_size) {
                return false;
            }
        }
        tmp_ret = str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value);
        if (tmp_ret != 0) {
            ALOGE("str_parms_add_str AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES failed(0x%x)", tmp_ret);
        }
    }

    return (ret >= 0) ? true : false;
}

static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    trace();

    return in->sample_rate;
}

static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);

    ALOGV("%s: handle(%d) set rate: %u", __func__, in->handle, rate);
    in->sample_rate = rate;
    return 0;
}

static size_t in_get_buffer_size(const struct audio_stream *stream)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    int buffer_size = (int)in->config.period_size * (int)audio_stream_in_frame_size(ptr);
    ALOGD("%s: handle(%d) buffer size %d", __func__, in->handle, buffer_size);
    return (size_t)buffer_size;
}

static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    ALOGD("%s: handle(%d) 0x%x", __func__, in->handle, in->channel_mask);
    return in->channel_mask;
}

static audio_format_t in_get_format(const struct audio_stream *stream)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    ALOGD("%s: handle(%d) 0x%x", __func__, in->handle, in->format);
    return in->format;
}

static int in_set_format(struct audio_stream *stream, audio_format_t format)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    ALOGD("%s: handle(%d) 0x%x", __func__, in->handle, in->format);
    in->format = format;
    return 0;
}

static int in_standby(struct audio_stream *stream)
{
    int ret = 0;
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    trace();

    CHECK_LOCK_RET(pthread_mutex_lock(&in->lock));
    if (in->standby) {
        ALOGE("handle(%d) has standby before", in->handle);
        goto EXIT;
    }

    if (in->hpi != NULL) {
        ALOGD("%s into hpi branch, handle(%d)", __func__, in->handle);
        ret = hpi_close(in->hpi);
        if (ret != 0) {
            ALOGE("hpi_close failed (0x%x)", ret);
            goto EXIT;
        }
#ifdef TV_TYPE
    } else if (in->hci != NULL) {
        ALOGD("%s into hci branch, handle(%d)", __func__, in->handle);
        ret = hci_close(in->hci);
        if (ret != 0) {
            ALOGE("hci_close failed (0x%x)", ret);
            goto EXIT;
        }
#endif
    }
    in->standby = true;

EXIT:
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&in->lock));
    return ret;
}

static int in_dump(const struct audio_stream *stream, int fd)
{
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    dprintf(fd, "%s: cast_read success(%u) fail(%u) \n", LOG_TAG, in->read_success_cnt, in->read_fail_cnt);
    return 0;
}

static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
    char value[DEFAULT_PARAM_SIZE];
    int ret;
    struct str_parms *parms = NULL;
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);

    parms = str_parms_create_str(kvpairs);
    if (parms == NULL) {
        ALOGE("%s: create param fail", __func__);
        return -EINVAL;
    }

    CHECK_LOCK_RET(pthread_mutex_lock(&in->lock));

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
    if (ret >= 0) {
        ALOGV("%s: AUDIO_PARAMETER_STREAM_INPUT_SOURCE=%s", __func__, value);
    }

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
    if (ret >= 0) {
        ALOGV("%s: AUDIO_PARAMETER_STREAM_ROUTING=%s", __func__, value);
    }

    CHECK_UNLOCK_RET(pthread_mutex_unlock(&in->lock));

    str_parms_destroy(parms);
    return 0;
}

static int create_str_parms(const char *keys, struct str_parms **query, struct str_parms **reply)
{
    *query = str_parms_create_str(keys);
    if (*query == NULL) {
        ALOGE("%s: create param fail", __func__);
        return -EINVAL;
    }

    *reply = str_parms_create();
    if (*reply == NULL) {
        ALOGE("%s: create param fail", __func__);
        str_parms_destroy(*query);
        return -EINVAL;
    }
    return 0;
}

static char* in_get_parameters(const struct audio_stream *stream, const char *keys)
{
    char *str = NULL;
    bool replied = false;
    struct audio_stream_in *ptr = audio_container_of(stream, struct audio_stream_in, common);
    struct stream_in *in = audio_container_of(ptr, struct stream_in, stream);
    struct str_parms *query = NULL;
    struct str_parms *reply = NULL;

    int ret = create_str_parms(keys, &query, &reply);
    if (ret != 0) {
        str = strdup("");
        return str;
    }

    ALOGV("handle(%d) get_parameters() enter: keys=%s", in->handle, keys);
    replied = replied ||
        get_parameter_channels(query, reply, in->supported_channel_masks, sizeof(in->supported_channel_masks));
    replied = replied ||
        get_parameter_formats(query, reply, in->supported_formats, sizeof(in->supported_formats));
    replied = replied ||
        get_parameter_rates(query, reply, in->supported_sample_rates, sizeof(in->supported_sample_rates));
    if (replied) {
        str = str_parms_to_str(reply);
    } else {
        str = strdup("");
    }

    str_parms_destroy(query);
    str_parms_destroy(reply);

    ALOGD("in_get_parameters() exit: returns=%s", str);
    return str;
}

static int in_set_gain(struct audio_stream_in *stream, float gain)
{
    struct stream_in *in = audio_container_of(stream, struct stream_in, stream);
    trace();
    ALOGD("%s: handle(%d) Gain:%f", __func__, in->handle, gain);
    return 0;
}

static void in_sleep(const struct audio_stream_in *stream, size_t bytes)
{
    struct stream_in *in = audio_container_of(stream, struct stream_in, stream);
    uint32_t frame_size = audio_stream_in_frame_size(stream);
    if (frame_size == 0) {
        ALOGE("%s: Invalid stream size", __func__);
        return;
    }
    uint32_t sample_rate = in_get_sample_rate(&stream->common);
    if (sample_rate == 0) {
        ALOGE("%s: Invalid sample rate", __func__);
        return;
    }
    uint32_t sleep_time = bytes * 1000000 / frame_size / sample_rate; /* multiply 1000000 convert second to us */
    ALOGD("%s: usecase %d failed  sleep buffersize (%dms)", __func__, in->usecase, sleep_time / SECOND_TO_MS);
    usleep(sleep_time);
}

static void in_read_err(struct stream_in *in, struct audio_stream_in *stream, size_t bytes, int ret)
{
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&in->lock));
    if (ret != 0) {
        in_sleep(stream, bytes);
        in->read_fail_cnt = (in->read_fail_cnt + 1) % CNT_MOD;
    } else {
        in->read_success_cnt = (in->read_success_cnt + 1) % CNT_MOD;
    }
    return;
}

static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes)
{
    struct stream_in *in = audio_container_of(stream, struct stream_in, stream);
    int ret = -EINVAL;
    if ((audio_stream_in_frame_size(stream) == 0) || (in->sample_rate == 0)) {
        ALOGE("in_read: stream framesize or sample_rate is 0");
        return -EINVAL;
    }

    CHECK_LOCK_RET(pthread_mutex_lock(&in->lock));

    if (in->usecase == USECASE_AUDIO_RECORD && in->standby != false) {
        CHECK_LOCK_RET(pthread_mutex_lock(&in->adev->lock));
        ret = hpi_open(in->hpi);
        CHECK_UNLOCK_RET(pthread_mutex_unlock(&in->adev->lock));
        if (ret == 0) {
            in->standby = false;
        } else {
            ALOGE("hpi_open failed(%x)", ret);
            goto ERR;
        }
        ret = hpi_read(in->hpi, buffer, bytes);
    } else if (in->usecase == USECASE_AUDIO_RECORD && in->standby == false) {
        ret = hpi_read(in->hpi, buffer, bytes);
    } else if (in->usecase == USECASE_AUDIO_CAST && in->standby != false) {
#ifdef TV_TYPE
        CHECK_LOCK_RET(pthread_mutex_lock(&in->adev->lock));
        ret = hci_open(in->hci);
        CHECK_UNLOCK_RET(pthread_mutex_unlock(&in->adev->lock));
        if (ret == 0) {
            in->standby = false;
        } else {
            ALOGE("hci_open failed(%x)", ret);
            goto ERR;
        }
        ret = hci_read(in->hci, buffer, bytes);
    } else if (in->usecase == USECASE_AUDIO_CAST && in->standby == false) {
        ret = hci_read(in->hci, buffer, bytes);
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, in->usecase);
    }

ERR:
    in_read_err(in, stream, bytes, ret);

    return bytes;
}

static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream __unused)
{
    trace();
    return 0;
}

static int in_add_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
    trace();
    return 0;
}

static int in_remove_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
    trace();
    return 0;
}

#ifdef TV_TYPE
static int out_set_callback(struct audio_stream_out *stream __unused,
            stream_callback_t callback __unused, void *cookie __unused)
{
    trace();
    return 0;
}
#endif

static int out_pause(struct audio_stream_out* stream)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    if (out == NULL || out->hdo == NULL) {
        return -EINVAL;
    }
    ALOGD("in function %s handle(%d)", __func__, out->handle);
#ifdef TV_TYPE
    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));
    hdo_pause(out->hdo);
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));
#endif
    ALOGD("%s handle(%d)", __func__, out->handle);
    return 0;
}

static int out_resume(struct audio_stream_out* stream)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    if (out == NULL || out->hdo == NULL) {
        return -EINVAL;
    }
    ALOGD("in function %s handle(%d)", __func__, out->handle);

    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));
#ifdef TV_TYPE
    hdo_resume(out->hdo);
#endif
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));
    ALOGD("%s handle(%d)", __func__, out->handle);
    return 0;
}

#ifdef TV_TYPE
static int out_drain(struct audio_stream_out* stream, audio_drain_type_t type)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    int ret = 0;
    trace();

    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));
    if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL) {
        if (type == AUDIO_DRAIN_EARLY_NOTIFY) {
            ret = hdo_drain(out->hdo, CMD_PARTIAL_DRAIN);
        } else {
            ret = hdo_drain(out->hdo, CMD_DRAIN);
        }
    }

    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));
    ALOGD("%s handle(%d) type(%d) ret(0x%x)", __func__, out->handle, type, ret);

    return ret;
}

static int out_flush(struct audio_stream_out* stream)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    int ret = -EINVAL;

    if (out == NULL || out->hdo == NULL) {
        return ret;
    }
    ALOGD("in function %s, handle(%d)\n", __func__, out->handle);

    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));
    ret = hdo_flush(out->hdo);
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));
    ALOGD("%s handle(%d) ret(0x%x)", __func__, out->handle, ret);

    return ret;
}
#endif

static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    ALOGV("%s: %d", __func__, out->sample_rate);
    return out->sample_rate;
}

static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    ALOGV("%s: %d", __func__, rate);
    out->sample_rate = rate;
    return 0;
}

static size_t out_get_buffer_size(const struct audio_stream *stream)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);
    size_t buffer_size = 0;

    if (out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PHONE_TX ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        buffer_size = hpo_get_buffer_size(out->hpo);
#ifdef TV_TYPE
    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
               out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL) {
        buffer_size = hdo_get_buffer_size(out->hdo);
#endif
    } else {
        ALOGE("%s:Invalid usecase %u", __func__, out->usecase);
    }

    ALOGD("%s: %d", __func__, buffer_size);
    return buffer_size;
}

static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    ALOGD("%s: 0x%x", __func__, out->channel_mask);
    return out->channel_mask;
}

static audio_format_t out_get_format(const struct audio_stream *stream)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    ALOGD("%s: 0x%x", __func__, out->format);
    return out->format;
}

static int out_set_format(struct audio_stream *stream __unused, audio_format_t format)
{
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    ALOGD("%s: 0x%x", __func__, format);
    out->format = format;
    return 0;
}

static void out_sleep(const struct audio_stream_out *stream, size_t bytes)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    size_t frame_size;

    frame_size = audio_stream_out_frame_size(stream);
    if (frame_size == 0) {
        ALOGE("%s: Invalid stream size", __func__);
        return;
    }

    if (out_get_sample_rate(&stream->common) == 0) {
        ALOGE("%s: Invalid sample rate", __func__);
        return;
    }
    size_t sleep_time = (bytes * 1000000 / frame_size / /* second to us multiply 1000000 */
                      out_get_sample_rate(&stream->common));
    ALOGD("%s: usecase %d write failed  sleeping for buffer duration(%d)", __func__, out->usecase, sleep_time);
    usleep(sleep_time);
}

static void print_non_silent_bytes(const uint8_t *bytes, uint32_t size)
{
    int len = 0;
    bool all_0 = true;
    char str[DELAYCHECK_BYTES_STR_MAX_LEN];

    len += sprintf_s(str + len, sizeof(str) - len, "time=%llu ms, new hal frame bytes:",
        (unsigned long long)(utils_get_cur_time() / TIME_RADIX_1000));
    for (uint32_t i = 0; i < size; i++) {
        if (bytes[i] != 0) {
            all_0 = false;
        }
        len += sprintf_s(str + len, sizeof(str) - len, "%02x", bytes[i]);
    }

    if (all_0) {
        return;
    }
    ALOGD("%s", str);
}

static ssize_t out_hpo_write(struct stream_out *out, const uint8_t* buffer, size_t bytes)
{
    if (out->usecase != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        return hpo_write(out->hpo, buffer, bytes);
    } else {
        ssize_t ret;
        int mute = (out->adev->vol_index == 0 ? 1 : 0);
        uint8_t frame_data[DELAYCHECK_PRINT_BYTES];

        memset_s(frame_data, sizeof(frame_data), 0, sizeof(frame_data));
        if (hpo_check_equal(DELAYCHECK_ENABLE, DEBUG_TRUE) && (bytes >= DELAYCHECK_PRINT_BYTES)) {
            /* save data before volume adjust */
            ret = memcpy_s(frame_data, sizeof(frame_data), (const void *)buffer, DELAYCHECK_PRINT_BYTES);
            if (ret != EOK) {
                ALOGE("memcpy_s return error %d", ret);
                return -1;
            }
        }
        /* adjust volume here because of ao low latency track do not support it */
        audio_gain_process((int16_t *)buffer, bytes / sizeof (int16_t), mute, out->adev->gain);
        ret = hpo_write(out->hpo, buffer, bytes);
        if (hpo_check_equal(DELAYCHECK_ENABLE, DEBUG_TRUE)) {
            print_non_silent_bytes(frame_data, sizeof(frame_data));
        }

        return ret;
    }
}


static void out_write_exit(struct stream_out *out, struct audio_stream_out *stream, size_t bytes, int ret)
{
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));
    if (ret != 0) {
        out_sleep(stream, bytes);
    }
    return;
}

static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
{
    int ret = 0;
    if ((stream == NULL) || (buffer == NULL)) {
        return -EINVAL;
    }
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);

    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));

    bool isNormalUsecase = out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PHONE_TX ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
    bool isDirectUsecase = out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
        out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL;
    if (isNormalUsecase && out->standby == true) {
            CHECK_LOCK_RET(pthread_mutex_lock(&out->adev->lock));
            ret = hpo_open(out->hpo);
            CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->adev->lock));
            if (ret == 0) {
                out->standby = false;
            } else {
                ALOGE("hpo_open failed %x", ret);
                goto EXIT;
            }
            ret = (int)out_hpo_write(out, buffer, bytes);
        } else if (isNormalUsecase && out->standby != true) {
        ret = (int)out_hpo_write(out, buffer, bytes);
    } else if (isDirectUsecase && out->standby == true) {
#ifdef TV_TYPE
            CHECK_LOCK_RET(pthread_mutex_lock(&out->adev->lock));
            ret = hdo_open(out->hdo);
            CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->adev->lock));
            if (ret == 0) {
                out->standby = false;
            } else {
                ALOGE("hdo_open failed(0x%x)", ret);
                goto EXIT;
            }
            ret = hdo_write(out->hdo, buffer, bytes);
        } else if (isDirectUsecase && out->standby != true) {
            ret = hdo_write(out->hdo, buffer, bytes);
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, out->usecase);
    }

EXIT:
    out_write_exit(out, stream, bytes, ret);

    return bytes;
}

static int out_get_presentation_position(const struct audio_stream_out *stream,
    uint64_t *frames, struct timespec *timestamp)
{
    int ret;
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    uint64_t dsp_frames = 0;
    trace();

    if (out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PHONE_TX ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        ret = hpo_get_tstamp(out->hpo, &dsp_frames);
        if (ret < 0) {
            // if return value is not 0, Audioflinger will not update LOCATION_KERNEL timestamp
            ALOGE("hpo_get_tstamp failed(0x%x)", ret);
            return ret;
        }
#ifdef TV_TYPE
    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
               out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL) {
        ret = hdo_get_tstamp(out->hdo, &dsp_frames);
        if (ret < 0) {
            ALOGE("hdo_get_tstamp failed(0x%x)", ret);
            return ret;
        }
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, out->usecase);
        ret = -EPERM;
    }

    ret = clock_gettime(CLOCK_MONOTONIC, timestamp);
    if (ret < 0) {
        ALOGE("clock_gettime failed %x", ret);
        return ret;
    }

    pts("%s handle(%d) dsp_frames(%llu) ret(0x%x)", __func__, out->handle, dsp_frames, ret);
    *frames = dsp_frames;
    return ret;
}

static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *frames)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);
    int ret = 0;
    uint64_t dsp_frames = 0;
    trace();

    if (out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        (void)hpo_get_tstamp(out->hpo, &dsp_frames);
#ifdef TV_TYPE
    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
               out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL) {
        (void)hdo_get_tstamp(out->hdo, &dsp_frames);
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, out->usecase);
        ret = -EPERM;
    }

    *frames = (uint32_t)dsp_frames;
    return ret;
}

static int out_add_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
    trace();
    return 0;
}

static int out_remove_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
    trace();
    return 0;
}

static int out_get_next_write_timestamp(const struct audio_stream_out *stream __unused, int64_t *timestamp)
{
    trace();
    if (timestamp == NULL) {
        ALOGE("timestamp is null");
        return -1;
    }
    return 0;
}

static int do_out_standby(const struct audio_stream *stream)
{
    int ret = 0;
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);

    if (out->standby) {
        return ret;
    }

    if (out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PHONE_TX ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        ret = hpo_close(out->hpo);
        if (ret != 0) {
            ALOGE("hpo_close failed(0x%x)", ret);
            return ret;
        }
#ifdef TV_TYPE
    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
               out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL) {
        ret = hdo_close(out->hdo);
        if (ret != 0) {
            ALOGE("hdo_close failed(0x%x)", ret);
            return ret;
        }
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, out->usecase);
        ret = -EPERM;
    }

    out->standby = true;
    return ret;
}

static int out_standby(struct audio_stream *stream)
{
    int ret;

    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);
    trace();

    CHECK_LOCK_RET(pthread_mutex_lock(&out->lock));
    ret = do_out_standby(stream);
    if (ret != 0) {
        ALOGE("do_out_standby failed(0x%x)", ret);
    }
    CHECK_UNLOCK_RET(pthread_mutex_unlock(&out->lock));

    return 0;
}

static int out_dump(const struct audio_stream *stream __unused, int fd __unused)
{
    trace();
    return 0;
}

static int str_to_len(const char *str, unsigned int len)
{
    char *endptr = NULL;

    int result = (int)(strtol(str, &endptr, 10)); /* 10 is base num */
    if ((endptr == NULL) || (endptr == str) || ((unsigned int)(endptr - str) != len)) {
        ALOGE("str to len failed");
        return 0;
    }
    return result;
}

static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
    char value[DEFAULT_VALUE_ARRAY_SIZE] = {0};
    int ret;
    int val;
    struct str_parms *parms = NULL;

    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);
    ALOGD("out_set_parameters kvpairs = %s", kvpairs);

    parms = str_parms_create_str(kvpairs);
    if (parms == NULL) {
        ALOGE("func:%s, create parms failed", __func__);
        return -1;
    }

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_SAMPLEING_RATE, value, sizeof(value));
    if (ret >= 0) {
        val = str_to_len(value, strlen(value));
        out->sample_rate = (unsigned int)val;
    }

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_CHANNELS, value, sizeof(value));
    if (ret >= 0) {
        val = str_to_len(value, strlen(value));
        out->channel_mask = (unsigned int)val;
    }

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_FORMAT, value, sizeof(value));
    if (ret >= 0) {
        val = str_to_len(value, strlen(value));
        out->format = val;
    }

    str_parms_destroy(parms);
    return 0;
}

#ifdef TV_TYPE
static void out_get_capbility(const struct stream_out *out, struct str_parms *query, struct str_parms *reply)
{
    char value[DEFAULT_VALUE_ARRAY_SIZE] = {0};
    char tmp[DEFAULT_OUT_PARAMETER_SIZE] = {0};
    int targetFormat;
    int ret;

    ret = str_parms_get_str(query, "format", tmp, sizeof(tmp));
    if (ret >= 0) {
        ALOGD("get parameters for format(%s)", tmp);
        targetFormat = str_to_len(tmp, strlen(tmp));
    } else {
        targetFormat = 0;
    }

    if (str_parms_has_key(query, "sup_formats") != 0) {
        get_support_formats(&out->adev->setting, value, sizeof(value));
        ret = str_parms_add_str(reply, "sup_formats", value);
        if (ret != 0) {
            ALOGE("str_parms_add_str sup_formats failed(0x%x)", ret);
            return;
        }
    }
    if (str_parms_has_key(query, "sup_sampling_rates") != 0) {
        get_support_rates(&out->adev->setting, value, targetFormat, sizeof(value));
        ret = str_parms_add_str(reply, "sup_sampling_rates", value);
        if (ret != 0) {
            ALOGE("str_parms_add_str sup_sampling_rates failed(0x%x)", ret);
            return;
        }
    }
    if (str_parms_has_key(query, "sup_channels") != 0) {
        get_support_channels(&out->adev->setting, value, targetFormat, sizeof(value));
        ret = str_parms_add_str(reply, "sup_channels", value);
        if (ret != 0) {
            ALOGE("str_parms_add_str sup_channels failed(0x%x)", ret);
            return;
        }
    }
}
#endif

static char *out_get_parameters(const struct audio_stream *stream __unused, const char *keys)
{
    char *str = NULL;
#ifdef TV_TYPE
    struct audio_stream_out *ptr = audio_container_of(stream, struct audio_stream_out, common);
    struct stream_out *out = audio_container_of(ptr, struct stream_out, stream);
#endif
    struct str_parms *query = NULL;
    struct str_parms *reply = NULL;

    int ret = create_str_parms(keys, &query, &reply);
    if (ret != 0) {
        str = strdup("");
        return str;
    }
#ifdef TV_TYPE
    out_get_capbility(out, query, reply);
#endif
    str = str_parms_to_str(reply);
    str_parms_destroy(query);
    str_parms_destroy(reply);

    ALOGD("out_get_parameters() exit: returns=%s", str);
    return str;
}

static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);

    if (out->usecase == USECASE_AUDIO_PLAYBACK_NORMAL ||
        out->usecase == USECASE_AUDIO_PHONE_TX ||
        out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
        return hpo_get_latency(out->hpo);
#ifdef TV_TYPE
    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL ||
               out->usecase == USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL) {
        return hdo_get_latency(out->hdo);
#endif
    } else {
        ALOGE("%s:Invalid usecase %d", __func__, out->usecase);
        return -EPERM;
    }
}

static int out_set_volume(struct audio_stream_out *stream __unused, float left __unused,
                          float right __unused)
{
    return 0;
}

#ifdef TV_TYPE
static int adev_obtain_hwsync_source(struct audio_device *adev)
{
    int ret = 0;

    if (adev->hw_sync_id != NOT_INITED_HANDLE) {
        ALOGD("Hw sync source(0x%x) already obtained, reuse it", adev->hw_sync_id);
        return ret;
    }

    ret = hdo_create_hwsync_source(&adev->hw_sync_id);
    if (ret != 0) {
        ALOGE("hdo_create_hwsync_source failed");
    }

    return ret;
}

static int adev_release_hwsync_source(struct audio_device *adev)
{
    int ret = 0;

    if (adev->hw_sync_id == NOT_INITED_HANDLE) {
        ALOGD("Hw sync source already giveback");
        return ret;
    }

    ret = hdo_destroy_hwsync_source(adev->hw_sync_id);
    if (ret != 0) {
        ALOGE("hal_avplay_destroy_ext failed(0x%x)", ret);
    }

    adev->hw_sync_id = NOT_INITED_HANDLE;

    return ret;
}
#endif

static int adev_get_microphones(const struct audio_hw_device *dev __unused,
                                struct audio_microphone_characteristic_t *mic_array __unused,
                                size_t *mic_count)
{
    trace();
    *mic_count = 0;
    return 0;
}

static int adev_get_audio_port(struct audio_hw_device *dev __unused,
                               struct audio_port *port __unused)
{
    ALOGD("get audio port in HAL");
    return 0;
}

static int adev_set_audio_port_config(struct audio_hw_device *dev __unused,
                                      const struct audio_port_config *config __unused)
{
#ifdef TV_TYPE
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);
    ALOGD("set audio port cfg in HAL");

    if (config->type == AUDIO_PORT_TYPE_DEVICE) {
        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
            int index = config->gain.values[0];
            /* set BUS port volume */
            hci_set_audiopatch_src_volume(adev, index);
        }
    }
#endif
    return 0;
}

static int adev_create_audio_patch(struct audio_hw_device *dev __unused, unsigned int num_sources __unused,
    const struct audio_port_config *sources __unused, unsigned int num_sinks __unused,
    const struct audio_port_config *sinks __unused, audio_patch_handle_t *handle __unused)
{
    ALOGD(" create audio patch in HAL");
    return 0;
}

static int adev_release_audio_patch(struct audio_hw_device *dev __unused, audio_patch_handle_t handle __unused)
{
    ALOGD(" release audio patch in HAL");
    return 0;
}

static void setup_output_stream_func(struct stream_out *out)
{
    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.get_channels = out_get_channels;
    out->stream.common.get_format = out_get_format;
    out->stream.common.set_format = out_set_format;
    out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.pause = out_pause;
    out->stream.resume = out_resume;
    out->stream.write = out_write;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_presentation_position = out_get_presentation_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
}

static void setup_output_stream_config(struct stream_out *out, const struct audio_config *config,
    audio_output_flags_t flags)
{
    out->sample_rate = config->sample_rate;
    if (out->sample_rate == 0) {
        out->sample_rate = DEFAULT_OUTPUT_SAMPLATE_RATE;
    }
    out->channel_mask = config->channel_mask;
    if (out->channel_mask == AUDIO_CHANNEL_NONE) {
        out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
    }
    out->format = config->format;
    if (out->format == AUDIO_FORMAT_DEFAULT) {
        out->format = AUDIO_FORMAT_PCM_16_BIT;
    }
    out->supported_channel_masks[0] = config->channel_mask;
    out->supported_formats[0] = config->format;
    out->supported_sample_rates[0] = config->sample_rate;
    out->flags = flags;
    out->standby = true;
    out->is_pause = false;
    out->hdo = NULL;
}

static void select_out_device(struct stream_out *out, const char *address)
{
    if (strncmp(address, "bus100_rear_seat", sizeof("bus100_rear_seat")) == 0 ||
        out->usecase == USECASE_AUDIO_PHONE_TX || out->devices == AUDIO_DEVICE_OUT_WIRED_HEADSET ||
        out->devices == AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
        out->hpo->out_device = AO_DEVICE_ID1;
    } else {
        out->hpo->out_device = AO_DEVICE_ID0;
    }
}

#ifdef TV_TYPE
static void set_direct_func(struct stream_out *out)
{
    out->stream.set_callback = out_set_callback;
    out->stream.pause = out_pause;
    out->stream.resume = out_resume;
    out->stream.drain = out_drain;
    out->stream.flush = out_flush;
}
#endif

#ifdef TV_TYPE
static int setup_output_stream_tv(struct stream_out *out, const struct audio_device *adev,
    const struct audio_config *config)
{
    int ret;
    if (((uint32_t)out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) == 1) {
        out->usecase = USECASE_AUDIO_PLAYBACK_DIRECT_TUNNEL;
        out->config  = g_pcm_config_ao_direct_output;
    } else {
        out->usecase = USECASE_AUDIO_PLAYBACK_DIRECT_NONTUNNEL;
        out->config  = g_pcm_config_hdmi_multi;
    }
    out->config.rate = config->sample_rate;

    if ((config->format == AUDIO_FORMAT_E_AC3) || (config->format == AUDIO_FORMAT_AC3)) {
        out->config.period_size = (adev->CertDeepBuffer == true) ?
            DIRECTOUT_ES_DEEP_PERIOD_SIZE : DIRECTOUT_ES_PERIOD_SIZE;
    }

    set_direct_func(out); // set func pointer only direct output need
    ret = hdo_create(&out->hdo);
    if (ret != 0) {
        ALOGE("%s: hdo_create failed (0x%x)", __func__, ret);
        return ret;
    }
    out->hdo->out = out;
    out->hdo->usecase = out->usecase;
    return 0;
}
#endif

static int setup_output_stream_case(struct stream_out *out, const struct audio_device *adev,
    const struct audio_config *config, const char *address)
{
    int ret;
    if (((uint32_t)out->flags & AUDIO_OUTPUT_FLAG_DIRECT) == 1) {
        if (adev->hdoSupport == false) {
            ALOGE("%s: not support hdo", __func__);
            return -ENOMEM;
        }
#ifdef TV_TYPE
        ret = setup_output_stream_tv(out, adev, config);
        if (ret != 0) {
            return ret;
        }
#endif
    } else {
        if (((uint32_t)out->flags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
            out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
            out->config = g_pcm_config_ao_low_latency;
        } else if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
            out->usecase = USECASE_AUDIO_PHONE_TX;
            out->config = g_pcm_config_phone_tx;
        } else {
            out->usecase = USECASE_AUDIO_PLAYBACK_NORMAL;
            out->config = g_pcm_config_ao_normal;
        }
        ret = hpo_create(&out->hpo);
        if (ret != 0) {
            ALOGE("%s: hpo_create failed (0x%x)", __func__, ret);
            return ret;
        }
        select_out_device(out, address);
        out->hpo->out = out;
    }

    out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
    ALOGD("%s return: handle(%d) sampleRate(%d) channelMask(%#x) flags(%#x) audioformat(0x%x) usecase(0x%x)",
        __func__, out->handle, config->sample_rate, config->channel_mask, out->flags, config->format, out->usecase);
    return 0;
}

static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address)
{
    int ret;
    struct stream_out *out = NULL;
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    ALOGD("%s: enter: devices(%#x) sample_rate(%d) channel_mask(%#x) flags(%#x) audioformat(0x%x) address(%s) ",
        __func__, devices, config->sample_rate, config->channel_mask, flags,
        config->format, address == NULL ? "NULL VALUE" : address);

    *stream_out = NULL;
    out = (struct stream_out *)malloc(sizeof(struct stream_out));
    if (out == NULL) {
        ALOGE("%s: malloc size(%d) failed", __func__, sizeof(struct stream_out));
        return -ENOMEM;
    }

    ret = memset_s(out, sizeof(struct stream_out), 0, sizeof(struct stream_out));
    if (ret != 0) {
        ALOGE("memset_s out failed(0x%x)", ret);
    }
    pthread_mutex_init(&out->lock, NULL);
    setup_output_stream_func(out);
    out->devices = devices;
    out->handle = handle;
    out->adev = adev;

    *stream_out = &out->stream;

    /* custom */
    if (custom_out_implement_needed(devices, flags, config, address) == true) {
        ret = custom_open_output_stream(devices, flags, config, address, out);
        if (ret != 0) {
            free(out);
            *stream_out = NULL;
            ALOGE("%s: custom_open_output_stream failed (0x%x)", __func__, ret);
        }
        return ret;
    }

    setup_output_stream_config(out, config, flags);
    ret = setup_output_stream_case(out, adev, config, address);
    if (ret != 0) {
        ALOGE("setup_output_stream_case failed(0x%x)", ret);
        free(out);
        *stream_out = NULL;
    }
    return ret;
}

static void adev_close_output_stream(struct audio_hw_device *dev __unused,
                                     struct audio_stream_out *stream)
{
    int ret;
    struct stream_out *out = audio_container_of(stream, struct stream_out, stream);

    ALOGD("in function %s, handle(%d)", __func__, out->handle);

    custom_close_output_stream(out);
    ret = out_standby(&stream->common);
    if (ret != 0) {
        ALOGE("out_standby failed(0x%x)", ret);
    }
#ifdef TV_TYPE
    if (out->hdo != NULL) {
        hdo_destroy(out->hdo);
        out->hdo = NULL;
    }
#endif

    if (out->hpo != NULL) {
        hpo_destroy(out->hpo);
        out->hpo = NULL;
    }

    free(stream);
}

static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
    char value[DEFAULT_VALUE_ARRAY_SIZE];
    int ret = -EINVAL;
    struct str_parms *parms = NULL;
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    ALOGD("adev_set_parameters kvpairs = %s", kvpairs);

    parms = str_parms_create_str(kvpairs);
    if (parms == NULL) {
        ALOGE("func:%s, create parms failed", __func__);
        return ret;
    }

    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
    if (ret >= 0) {
        ALOGV("%s: AUDIO_PARAMETER_STREAM_ROUTING=%s", __func__, value);
    }
#ifdef TV_TYPE
    ret = str_parms_get_str(parms, PARAMETER_SET_HDMI_OUTPUT, value, sizeof(value));
    if (ret >= 0) {
        ret = setHdmiMode(&adev->setting, (uint32_t)str_to_len(value, strlen(value)));
        if (ret != 0) {
            ALOGE("setHdmiMode failed(0x%x)", ret);
        }
    }
    ret = str_parms_get_str(parms, PARAMETER_SET_SPDIF_OUTPUT, value, sizeof(value));
    if (ret >= 0) {
        ret = setSpdifMode(&adev->setting, (uint32_t)str_to_len(value, strlen(value)));
        if (ret != 0) {
            ALOGE("setSpdifMode failed(0x%x)", ret);
        }
    }
    ret = str_parms_get_str(parms, PARAMETER_SET_HBR_DEGRADE_OUTPUT, value, sizeof(value));
    if (ret >= 0) {
        ret = setHbr2LbrMode(&adev->setting, (uint32_t)str_to_len(value, strlen(value)));
        if (ret != 0) {
            ALOGE("setHbr2LbrMode failed(0x%x)", ret);
        }
    }
    ret = str_parms_get_str(parms, AUDIO_PARAMETER_PATCH_SINK_DEVICE, value, sizeof(value));
    if (ret >= 0) {
        hci_set_patch_sink_device(&adev->device_attr, str_to_len(value, strlen(value)));
    }
#endif
    custom_adev_set_parameters(adev, parms);
    str_parms_destroy(parms);

    return 0;
}

#ifdef TV_TYPE
static void handle_str_tv(struct audio_device *adev, struct str_parms *query, struct str_parms *result)
{
    int ret;
    char value[DEFAULT_PARAM_SIZE] = {0};
    const char *const stream_info_table[STREAM_MAX] = {
        [STREAM_NONE] = "none",
        [STREAM_DDP_2_0] = "ddp2.0",
        [STREAM_DDP_5_1] = "ddp5.1",
        [STREAM_ATMOS] = "atmos",
    };

    ret = str_parms_get_str(query, AUDIO_PARAMETER_HW_AV_SYNC, value, sizeof(value));
    if (ret >= 0) {
        ret = adev_obtain_hwsync_source(adev);
        if (ret != 0) {
            ALOGE("adev_obtain_hwsync_source failed");
        } else {
            str_parms_add_int(result, AUDIO_PARAMETER_HW_AV_SYNC, adev->hw_sync_id);
        }
    }

    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_DOLBYSTREAMINFO, value, sizeof(value));
    if (ret >= 0 && adev->hdoSupport) {
        ret = hdo_obtain_hwsync_streaminfo(adev);
        if (ret != 0) {
            ALOGE("hdo_obtain_hwsync_streaminfo failed(0x%x)", ret);
        }

        if (adev->stream_type >= STREAM_MAX) {
            ALOGE("Invalid streamtype(%d)", adev->stream_type);
        } else {
            ret = str_parms_add_str(result, AUDIO_PARAMETER_STREAM_DOLBYSTREAMINFO,
                stream_info_table[adev->stream_type]);
            if (ret != 0) {
                ALOGE("str_parms_add_str AUDIO_PARAMETER_STREAM_DOLBYSTREAMINFO failed(0x%x)", ret);
            }
        }
    }
}
#endif

static char *adev_get_parameters(const struct audio_hw_device *dev,
                                 const char *keys)
{
    char *str = NULL;
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);
    struct str_parms *query = str_parms_create_str(keys);
    struct str_parms *result = str_parms_create();

    ALOGD("adev_get_parameters keys:%s", keys);

    if ((query == NULL) || (result == NULL)) {
        ALOGE("func:%s, create query or result failed\n", __func__);
        return NULL;
    }

    CHECK_LOCK_RET(pthread_mutex_lock(&adev->lock));

#ifdef TV_TYPE
    handle_str_tv(adev, query, result);
#endif
    custom_adev_get_parameters(adev, query, result);

    str = str_parms_to_str(result);
    str_parms_destroy(query);
    str_parms_destroy(result);

    CHECK_UNLOCK_RET(pthread_mutex_unlock(&adev->lock));

    ALOGD("%s: returns %s", __func__, (str == NULL) ?  "NULL" : str);
    return str;
}


static int adev_init_check(const struct audio_hw_device *dev __unused)
{
    trace();
    return 0;
}

static int adev_set_voice_volume(struct audio_hw_device *dev __unused, float volume __unused)
{
    trace();
    return 0;
}

static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    adev->vol_master = volume;
    ALOGD("%s: volume(%f)", __func__, volume);
    return 0;
}

static int adev_get_master_volume(struct audio_hw_device *dev, float *volume)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    *volume = adev->vol_master;
    ALOGD("%s: volume(%f)", __func__, *volume);
    return 0;
}

static int adev_set_master_mute(struct audio_hw_device *dev, bool muted)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    adev->muted = muted;
    ALOGD("%s: muted(%d)", __func__, muted);
    return 0;
}

static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    *muted = adev->muted;
    ALOGD("%s: muted(%d)", __func__, *muted);
    return 0;
}

static int adev_set_mode(struct audio_hw_device *dev __unused, audio_mode_t mode __unused)
{
#ifdef TV_TYPE
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);
    if (adev->mode == mode) {
        ALOGD("%s: mode(%d) same do nothing", __func__, mode);
        return 0;
    }
    adev->mode = mode;
#if (UAPI_VERSION_CODE == UAPI_VERSION(1, 0))
    if (mode == AUDIO_MODE_IN_CALL) {
        ALOGD("%s: setup_down_link", __func__);
        create_down_link(&adev->ctx);
    } else {
        ALOGD("%s: destroy_down_link", __func__);
        destroy_down_link(&adev->ctx);
    }
#endif
#endif
    return 0;
}

static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    trace();
    adev->mic_muted = state;
    ALOGD("%s: state(%d)", __func__, state);
    return 0;
}

static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
{
    struct audio_device *adev = audio_container_of(dev, struct audio_device, hw_device);

    trace();
    *state = adev->mic_muted;
    ALOGD("%s: state(%d)", __func__, *state);
    return 0;
}

static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev __unused,
                                         const struct audio_config *config __unused)
{
    ALOGD("%s: size(%d)", __func__, DEFAULT_INPUT_BUFFER_SIZE);
    return DEFAULT_INPUT_BUFFER_SIZE;
}

static void setup_input_stream_func(struct stream_in *in)
{
    in->stream.common.get_sample_rate = in_get_sample_rate;
    in->stream.common.set_sample_rate = in_set_sample_rate;
    in->stream.common.get_buffer_size = in_get_buffer_size;
    in->stream.common.get_channels = in_get_channels;
    in->stream.common.get_format = in_get_format;
    in->stream.common.set_format = in_set_format;
    in->stream.common.standby = in_standby;
    in->stream.common.dump = in_dump;
    in->stream.common.set_parameters = in_set_parameters;
    in->stream.common.get_parameters = in_get_parameters;
    in->stream.common.add_audio_effect = in_add_audio_effect;
    in->stream.common.remove_audio_effect = in_remove_audio_effect;
    in->stream.set_gain = in_set_gain;
    in->stream.read = in_read;
    in->stream.get_input_frames_lost = in_get_input_frames_lost;
}

static void setup_input_stream_config(struct stream_in *in, const struct audio_config *config)
{
    in->sample_rate = config->sample_rate;
    if (in->sample_rate == 0) {
        in->sample_rate = SAMPLE_RATE_48000;
    }
    in->channel_mask = config->channel_mask;
    if (in->channel_mask == AUDIO_CHANNEL_NONE) {
        in->channel_mask = AUDIO_CHANNEL_IN_STEREO;
    }
    in->format = config->format;
    if (in->format == AUDIO_FORMAT_DEFAULT) {
        in->format = AUDIO_FORMAT_PCM_16_BIT;
    }
    in->supported_channel_masks[0] = config->channel_mask;
    in->supported_formats[0] = config->format;
    in->supported_sample_rates[0] = config->sample_rate;
}

static void select_in_device(struct stream_in *in)
{
    if (in->devices == AUDIO_DEVICE_IN_WIRED_HEADSET) {
        in->hpi->in_device = AI_DEVICE_ID1;
    } else {
        in->hpi->in_device = AI_DEVICE_ID;
    }
    ALOGD("select in device alsa cards is %x,%x", in->hpi->in_card, in->hpi->in_device);
}

static struct pcm_config init_pcm_config_ai_low_latency(void)
{
    struct pcm_config pcm_config_ai_low_latency = {
        .channels = DEFAULT_OUTPUT_CHANNEL,
        .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
        .period_size = DEFAULT_INPUT_PERIOD_SIZE,
        .period_count = DEFAULT_OUTPUT_PERIOD_COUNT,
        .format = PCM_FORMAT_S16_LE,
        .start_threshold = 0,
        .stop_threshold = 0,
        .silence_threshold = 0,
        .avail_min = 0,
    };
    return pcm_config_ai_low_latency;
}

static struct pcm_config init_pcm_config_cast(void)
{
    struct pcm_config pcm_config_cast = {
        .channels = DEFAULT_OUTPUT_CHANNEL,
        .rate = DEFAULT_OUTPUT_SAMPLATE_RATE,
        .period_size = CAST_INPUT_PERIOD_SIZE,
        .period_count = DEFAULT_OUTPUT_PERIOD_COUNT,
        .format = PCM_FORMAT_S16_LE,
    };
    return pcm_config_cast;
}

static int setup_input_stream_case(struct stream_in *in, audio_devices_t devices)
{
    int ret;
    struct pcm_config pcm_config_ai_low_latency = init_pcm_config_ai_low_latency();
    struct pcm_config pcm_config_cast = init_pcm_config_cast();
    if (devices == AUDIO_DEVICE_IN_TV_TUNER) {
#ifdef TV_TYPE
        in->usecase = USECASE_AUDIO_CAST;
        in->config = pcm_config_cast;

        ret = hci_create(&in->hci);
        if (ret != 0) {
            ALOGE("%s: hci_create failed(0x%x)", __func__, ret);
            return ret;
        }

        in->hci->in = in;
        ALOGD("adev_open_input_stream AUDIO_CAST return: sample_rate: %u, channels: %x, format: %d",
              in->sample_rate, in->channel_mask, in->format);
#endif
    } else if (devices == AUDIO_DEVICE_IN_TELEPHONY_RX) {
        in->usecase = USECASE_AUDIO_PHONE_RX;
        in->config = pcm_config_cast;
        ALOGD("adev_open_input_stream PHONE_RX return: sample_rate: %u, channels: %x, format: %d",
              in->sample_rate, in->channel_mask, in->format);
    } else {
        in->usecase = USECASE_AUDIO_RECORD;
        in->config = pcm_config_ai_low_latency;
        if (in->channel_mask == AUDIO_CHANNEL_IN_MONO) {
            in->config.channels = 1;
        }
        /*
        * Note hal config peroid size only support 0x200 limited by driver
        */
        in->config.period_size = AI_INPUT_PERIOD_SIZE; // or use g_pcm_config_ai_low_latency default value

        ret = hpi_create(&in->hpi);
        if (ret != 0) {
            ALOGE("%s: hpi_create failed (0x%x)", __func__, ret);
            return ret;
        }
        select_in_device(in);
        in->hpi->in = in;

        ALOGD("adev_open_input_stream BUILTIN_MIC return: sample_rate: %u, channels: %x, format: %d",
              in->sample_rate, in->channel_mask, in->format);
    }

    return 0;
}

static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle,
                                  audio_devices_t devices, struct audio_config *config,
                                  struct audio_stream_in **streamin, audio_input_flags_t flags,
                                  const char *address __unused, audio_source_t source)
{
    int ret;
    struct stream_in *in = NULL;
    uint32_t channel_count = audio_channel_count_from_in_mask(config->channel_mask);

    ALOGD("%s: enter: devices(%#x) sample_rate(%d) channel_mask(%#x) flags(%#x) audioformat(0x%x) source(%#x)",
          __func__, devices, config->sample_rate, config->channel_mask, flags, config->format, source);

    *streamin = NULL;
    if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0) {
        return -EINVAL;
    }
    in = (struct stream_in *)malloc(sizeof(struct stream_in));
    if (in == NULL) {
        ALOGE("%s: malloc size(%d) failed", __func__, sizeof(struct stream_in));
        return -ENOMEM;
    }

    ret = memset_s(in, sizeof(struct stream_in), 0, sizeof(struct stream_in));
    if (ret != 0) {
        ALOGE("memset_s in failed(0x%x)", ret);
        free(in);
        return ret;
    }

    pthread_mutex_init(&in->lock, NULL);
    setup_input_stream_func(in);
    in->handle = handle;
    in->devices = devices;
    in->adev = audio_container_of(dev, struct audio_device, hw_device);
    *streamin = &in->stream;
    /* custom */
    if (custom_in_implement_needed(devices, flags, source) == true) {
        ret = custom_open_input_stream(devices, config, flags, source, in);
        if (ret != 0) {
            free(in);
            *streamin = NULL;
            ALOGE("%s: custom_open_input_stream failed(0x%x)", __func__, ret);
        }
        return ret;
    }

    setup_input_stream_config(in, config);
    ret = setup_input_stream_case(in, devices);
    if (ret != 0) {
        free(in);
        *streamin = NULL;
        ALOGE("%s: setup_input_stream_case failed(0x%x)", __func__, ret);
    }
    return ret;
}

static void adev_close_input_stream(struct audio_hw_device *dev __unused,
                                    struct audio_stream_in *stream)
{
    struct stream_in *in = audio_container_of(stream, struct stream_in, stream);

    custom_close_input_stream(in);
    int ret = in_standby(&stream->common);
    if (ret != 0) {
        ALOGE("in_standby failed (0x%x)", ret);
    }

    if (in->hpi != NULL) {
        ALOGD("%s into hpi branch, handle(%d)", __func__, in->handle);
        hpi_destroy(in->hpi);
#ifdef TV_TYPE
    } else if (in->hci != NULL) {
        ALOGD("%s into hci branch, handle(%d)", __func__, in->handle);
        hci_destroy(in->hci);
#endif
    }

    free(stream);
}

static int adev_dump(const audio_hw_device_t *device __unused, int fd)
{
    dprintf(fd, "primary hal adev:\n");

    return 0;
}

static int adev_close(hw_device_t *device)
{
    int ret = -EINVAL;
    struct audio_hw_device *ptr = audio_container_of(device, struct audio_hw_device, common);
    struct audio_device *adev = audio_container_of(ptr, struct audio_device, hw_device);
    trace();

    if (adev == NULL) {
        ALOGD("%s adev null point", __func__);
        return ret;
    }
    custom_deinit(adev);
#ifdef TV_TYPE
    ret = adev_release_hwsync_source(adev);
    if (ret != 0) {
        ALOGD("call adev_release_hwsync_source failed(0x%x)", ret);
    }

    deinit_and_close_hdmi();
    hdo_deinit();
    wraper_deinit_hal_sound();
#endif
    free(adev);
    adev = NULL;

    return 0;
}

static void init_product_type(struct audio_device *adev)
{
    char value[PROPERTY_VALUE_MAX] = {0};

    property_get(SOC_PRODUCT_TYPE_PROPERTY, value, "tv");
    ALOGD("%s: get product type (%s)", __func__, value);

    if (strncmp(value, TV_PRODUCT, strlen(TV_PRODUCT)) == 0) {
        adev->product_type = PRODUCT_TYPE_TV;
    } else if (strncmp(value, STB_PRODUCT, strlen(STB_PRODUCT)) == 0) {
        adev->product_type = PRODUCT_TYPE_STB;
    } else if (strncmp(value, TABLET_PRODUCT, strlen(TABLET_PRODUCT)) == 0) {
        adev->product_type = PRODUCT_TYPE_TABLET;
    } else {
        adev->product_type = PRODUCT_TYPE_MAX;
    }
}

static void setup_adev(struct audio_device *adev, const hw_module_t* module)
{
    char value[PROPERTY_VALUE_MAX] = {0};

    pthread_mutex_init(&adev->lock, NULL);
    adev->hw_sync_id = NOT_INITED_HANDLE;
    adev->mic_muted = false;
    adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_3_0;
    adev->hw_device.common.module = (struct hw_module_t *) module;
    adev->hw_device.common.close = adev_close;

    adev->hw_device.init_check = adev_init_check;
    adev->hw_device.set_voice_volume = adev_set_voice_volume;
    adev->hw_device.set_master_volume = adev_set_master_volume;
    adev->hw_device.get_master_volume = adev_get_master_volume;
    adev->hw_device.set_master_mute = adev_set_master_mute;
    adev->hw_device.get_master_mute = adev_get_master_mute;
    adev->hw_device.set_mode = adev_set_mode;
    adev->hw_device.set_mic_mute = adev_set_mic_mute;
    adev->hw_device.get_mic_mute = adev_get_mic_mute;
    adev->hw_device.set_parameters = adev_set_parameters;
    adev->hw_device.get_parameters = adev_get_parameters;
    adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
    adev->hw_device.open_output_stream = adev_open_output_stream;
    adev->hw_device.close_output_stream = adev_close_output_stream;
    adev->hw_device.open_input_stream = adev_open_input_stream;
    adev->hw_device.close_input_stream = adev_close_input_stream;
    adev->hw_device.dump = adev_dump;
    adev->hw_device.get_microphones = adev_get_microphones;
    adev->hw_device.get_audio_port = adev_get_audio_port;
    adev->hw_device.set_audio_port_config = adev_set_audio_port_config;
    adev->hw_device.create_audio_patch = adev_create_audio_patch;
    adev->hw_device.release_audio_patch = adev_release_audio_patch;
    adev->vol_master = 1;
    adev->decimal_gain = PRIMARY_DEFAULT_DEC_DB;
    adev->integer_gain = PRIMARY_DEFAULT_INT_DB;
    adev->ac3_lib_type = AC3_LIB_TYPE_NONE;
    adev->cast_muted = false;
    adev->hdoSupport = true;
    adev->custom_private_data = NULL;
    init_product_type(adev);
    property_get(HAL_SYSTEMCERT_PROPERTY, value, "");
    ALOGD("%s: get system certprop(%s)", __func__, value);

    if (strncmp(value, "true", strlen("true")) == 0) {
        ALOGD("%s: system certprop enable", __func__);
        adev->CertDeepBuffer = true;
    }
}

static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
    int ret = -EINVAL;
    struct audio_device *adev = NULL;

    if (strncmp(name, AUDIO_HARDWARE_INTERFACE, strlen(AUDIO_HARDWARE_INTERFACE)) != 0) {
        ALOGE("%s Invalid name", __func__);
        return ret;
    }

    ALOGD("%s name(%s)", __func__, name);

    adev = malloc(sizeof(struct audio_device));
    if (adev == NULL) {
        return -ENOMEM;
    }

    ret = memset_s(adev, sizeof(struct audio_device), 0, sizeof(struct audio_device));
    if (ret != 0) {
        ALOGE("memset_s adev failed(0x%x)", ret);
        free(adev);
        return ret;
    }

    setup_adev(adev, module);

#ifdef TV_TYPE
    ret = wraper_init_hal_sound();
    if (ret != 0) {
        ALOGE("init sound failed ret(0x%x)", ret);
    }

    ret = hdo_init(adev);
    if (ret != 0) {
        ALOGE("hdo_init failed ret(0x%x)", ret);
    }

    ret = initAndOpenHDMI(&adev->setting);
    if (ret != 0) {
        ALOGE("hdmi_init failed ret(0x%x)", ret);
    }

    hci_init_device_attr(&adev->device_attr);
#endif

    *device = &adev->hw_device.common;

    ret = custom_init(adev);
    if (ret != 0) {
        ALOGE("custom_init failed ret(0x%x)", ret);
    }

    return 0;
}

static struct hw_module_methods_t g_hal_module_methods = {
    .open = adev_open,
};

struct audio_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = AUDIO_HARDWARE_MODULE_ID,
        .name = "Vn audio HAL",
        .author = "The Android Open Source Project",
        .methods = &g_hal_module_methods,
    },
};