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