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