You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2138 lines
70 KiB

/*
* 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,
},
};