867 lines
37 KiB
C
867 lines
37 KiB
C
/*
|
|
* Copyright 2023 Amlogic Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "audio_hw_input_tv"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <inttypes.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/prctl.h>
|
|
#include <unistd.h>
|
|
#include "aml_audio_log.h"
|
|
#include <cutils/atomic.h>
|
|
#include <hardware/audio.h>
|
|
#include <aml_data_utils.h>
|
|
#include <audio_utils/channels.h>
|
|
#if ANDROID_PLATFORM_SDK_VERSION >= 25 // 8.0
|
|
#include <system/audio-base.h>
|
|
#endif
|
|
|
|
#include "audio_hw.h"
|
|
#include "aml_audio_stream.h"
|
|
#include "audio_hw_utils.h"
|
|
#include "aml_audio_timer.h"
|
|
#include "alsa_config_parameters.h"
|
|
#include "tv_patch_avsync.h"
|
|
#include "aml_ng.h"
|
|
#include "alsa_device_parser.h"
|
|
#include "tv_patch_ctrl.h"
|
|
#include "tv_patch.h"
|
|
#include "dolby_lib_api.h"
|
|
#include "spdif_encoder_api.h"
|
|
#include "audio_port.h"
|
|
#include "amlAudioMixer.h"
|
|
#include "audio_hw_ms12_common.h"
|
|
#include "device_patch_mgr.h"
|
|
#include "audio_hw_resource_mgr.h"
|
|
#include "aml_noise_gate.h"
|
|
#include "tv_private_object.h"
|
|
#include "aml_dump_debug.h"
|
|
#include "aml_audio_output.h"
|
|
|
|
void audio_digital_input_format_check(struct aml_audio_patch *patch)
|
|
{
|
|
audio_format_t cur_aformat;
|
|
if (IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para);
|
|
if (cur_aformat != patch->aformat) {
|
|
ALOGI ("HDMI/SPDIF input format changed from %#x to %#x\n", patch->aformat, cur_aformat);
|
|
patch->aformat = cur_aformat;
|
|
if (audio_is_linear_pcm(cur_aformat)) {
|
|
patch->IEC61937_format = false;
|
|
} else {
|
|
patch->IEC61937_format = true;
|
|
}
|
|
patch->digital_input_fmt_change = true;
|
|
patch->mode_reconfig_flag = true;
|
|
}
|
|
if (audio_is_linear_pcm(patch->aformat)) {
|
|
if (patch->in_channel_count != patch->out_channel_count) {
|
|
ALOGI ("HDMI/SPDIF PCM ch changed from %d to %d", patch->out_channel_count, patch->in_channel_count);
|
|
patch->out_channel_count = patch->in_channel_count;
|
|
patch->digital_input_fmt_change = true;
|
|
}
|
|
if (patch->in_sample_rate != patch->out_sample_rate) {
|
|
ALOGI ("HDMI/SPDIF PCM sr changed from %d to %d", patch->out_sample_rate, patch->in_sample_rate);
|
|
patch->out_sample_rate = patch->in_sample_rate;
|
|
patch->digital_input_fmt_change = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void *audio_patch_signal_detect_threadloop(void *data)
|
|
{
|
|
struct aml_audio_patch *patch = (struct aml_audio_patch *)data;
|
|
struct aml_audio_device *aml_dev = (struct aml_audio_device *) patch->dev;
|
|
bool cur_hw_stable = false;
|
|
bool last_hw_stable = false;
|
|
int cur_type = 0;
|
|
int lat_type = 0;
|
|
int cur_sr = -1;
|
|
int last_sr = -1;
|
|
int cur_channel = -1;
|
|
int last_channel = -1;
|
|
audio_format_t cur_aformat = AUDIO_FORMAT_INVALID;
|
|
audio_format_t last_aformat = AUDIO_FORMAT_INVALID;
|
|
bool cur_raw_data_change = false;
|
|
bool last_raw_data_change = false;
|
|
struct timespec mute_start_ts;
|
|
int mute_mdelay = 120;
|
|
bool mute_flag = false;
|
|
bool in_mute = false;
|
|
|
|
|
|
ALOGD("%s: in", __func__);
|
|
prctl(PR_SET_NAME, (unsigned long)"audio_signal_detect");
|
|
aml_set_thread_priority("audio_signal_detect", patch->audio_signal_detect_threadID, 5);
|
|
|
|
while (!patch->signal_detect_thread_exit) {
|
|
cur_hw_stable = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_AUDIO_STABLE);
|
|
if (get_hdmiin_audio_mode(&aml_dev->alsa_mixer) != HDMIIN_MODE_I2S) {
|
|
cur_type = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_TYPE);
|
|
} else if (aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_AUDIO_PCPD_MONITOR_ENABLE)) {
|
|
cur_type = audio_pcpd_monitor_format_detection(&aml_dev->alsa_mixer, patch->input_device);
|
|
}
|
|
cur_sr = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_SAMPLERATE);
|
|
cur_channel = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_CHANNELS);
|
|
cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para);
|
|
cur_raw_data_change = patch->start_mute;
|
|
|
|
if ((!cur_hw_stable && cur_hw_stable != last_hw_stable) || (cur_type != lat_type)
|
|
|| (cur_sr != last_sr) || (cur_channel != last_channel)
|
|
|| (cur_raw_data_change && cur_raw_data_change != last_raw_data_change)
|
|
|| ((cur_aformat != last_aformat) && (cur_aformat == AUDIO_FORMAT_DTS || cur_aformat == AUDIO_FORMAT_DTS_HD) )) {// resolve dts resume pop noise
|
|
ALOGI("[%s:%d] hw_stable(%d)(%d) sr(%d)(%d) channel(%d)(%d) aformat(%d)(%d) raw_data_change(%d)(%d) type(%d)(%d)", __FUNCTION__, __LINE__,
|
|
cur_hw_stable, last_hw_stable, cur_sr, last_sr, cur_channel, last_channel, cur_aformat, last_aformat, cur_raw_data_change,
|
|
last_raw_data_change, cur_type, lat_type);
|
|
audiohal_send_msg_2_ms12(&aml_dev->ms12, MS12_MESG_TYPE_FLUSH);
|
|
|
|
set_aed_master_volume_mute(&aml_dev->alsa_mixer, true);
|
|
//set_dac_digital_volume_mute(&aml_dev->alsa_mixer, true);//TBD
|
|
clock_gettime(CLOCK_MONOTONIC, &mute_start_ts);
|
|
mute_flag = true;
|
|
ALOGD("%s: mute", __func__); /*Complete mute*/
|
|
}
|
|
|
|
if (mute_flag) {
|
|
in_mute = Stop_watch(mute_start_ts, mute_mdelay);
|
|
if (!in_mute) {
|
|
set_aed_master_volume_mute(&aml_dev->alsa_mixer, false);
|
|
//set_dac_digital_volume_mute(&aml_dev->alsa_mixer, false);//TBD
|
|
ALOGD("%s: unmute", __func__);
|
|
mute_flag = false;
|
|
}
|
|
}
|
|
|
|
lat_type = cur_type;
|
|
last_hw_stable = cur_hw_stable;
|
|
last_sr = cur_sr;
|
|
last_channel = cur_channel;
|
|
last_aformat = cur_aformat;
|
|
last_raw_data_change = cur_raw_data_change;
|
|
aml_audio_sleep(5*1000);
|
|
}
|
|
|
|
set_aed_master_volume_mute(&aml_dev->alsa_mixer, false);
|
|
//set_dac_digital_volume_mute(&aml_dev->alsa_mixer, false);//TBD
|
|
ALOGD("%s: exit and unmute", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
void audio_patch_set_av_sync_status(struct aml_audio_patch *apatch, bool need_do_avsync)
|
|
{
|
|
if (NULL == apatch) {
|
|
AM_LOGE("apatch is NULL!");
|
|
return;
|
|
}
|
|
//AM_LOGI("apatch(%p) set av sync status to %d", apatch, need_do_avsync);
|
|
apatch->need_do_avsync = need_do_avsync;
|
|
}
|
|
|
|
// buffer/period ratio, bigger will add more latency
|
|
void *audio_patch_input_threadloop(void *data)
|
|
{
|
|
struct aml_audio_patch *patch = (struct aml_audio_patch *)data;
|
|
struct aml_audio_device *aml_dev = (struct aml_audio_device *) patch->dev;
|
|
ring_buffer_t *ringbuffer = & (patch->aml_ringbuffer);
|
|
int read_bytes = 0;
|
|
int ret = 0, retry = 0;
|
|
audio_format_t cur_aformat;
|
|
int ring_buffer_size = 0;
|
|
bool stable_flag = false;
|
|
int old_chip_with_hw_det = check_chip_name("t5d", 3, &aml_dev->alsa_mixer);
|
|
struct component_noise_gate *noise_gate = get_noise_gate_instance(aml_dev);
|
|
audio_input_config_t * input_config = &patch->input_config;
|
|
|
|
AM_LOGI("++in type: %d", patch->input_type);
|
|
patch->aformat = patch->in_format;
|
|
patch->in_channel_count = audio_channel_count_from_in_mask(patch->in_chanmask);
|
|
AM_LOGI("++in format: %d, chan: %d, rate: %d", patch->in_format, patch->in_channel_count, patch->in_sample_rate);
|
|
read_bytes = AUDIO_INPUT_DEFAULT_PERIOD_SIZE * audio_bytes_per_sample(patch->in_format) * patch->in_channel_count;
|
|
/* CVBS IN signal is stable very quickly, sometimes there is no unstable state,
|
|
* we need to do ease in before playing CVBS IN at in_read func.
|
|
*/
|
|
if (patch->input_device & AUDIO_DEVICE_IN_LINE) {
|
|
patch->mute_flag = true;
|
|
}
|
|
patch->in_buf_size = read_bytes;
|
|
patch->in_buf = aml_audio_calloc(1, patch->in_buf_size);
|
|
if (!patch->in_buf) {
|
|
AM_LOGE("patch->in_buf malloc fail!!!");
|
|
return (void *)0;
|
|
}
|
|
|
|
int first_start = 1;
|
|
prctl(PR_SET_NAME, (unsigned long)"audio_input_patch");
|
|
aml_set_thread_priority("audio_input_patch", patch->audio_input_threadID, 5);
|
|
/*affinity the thread to cpu 2/3 which has few IRQ*/
|
|
aml_audio_set_cpu23_affinity();
|
|
|
|
input_config->channel_mask = patch->in_chanmask;
|
|
input_config->sample_rate = patch->in_sample_rate;
|
|
input_config->format = patch->in_format;
|
|
input_config->period_count = AUDIO_INPUT_DEFAULT_PERIOD_COUNT;
|
|
input_config->period_size = AUDIO_INPUT_DEFAULT_PERIOD_SIZE;
|
|
|
|
AM_LOGI("open input type:%d, period_count:%d, period_size:%d, format:%d, sample_rate:%d",
|
|
patch->input_type, input_config->period_count, input_config->period_size, input_config->format, input_config->sample_rate);
|
|
|
|
audio_input_open(patch->input_type, input_config);
|
|
patch->input_open_counter ++;
|
|
|
|
if (ringbuffer) {
|
|
ring_buffer_size = ringbuffer->size;
|
|
}
|
|
|
|
while (!patch->input_thread_exit) {
|
|
int bytes_avail = 0;
|
|
int period_mul = 1;
|
|
int read_threshold = 0;
|
|
|
|
if (patch->input_device == AUDIO_DEVICE_IN_HDMI || patch->input_device == AUDIO_DEVICE_IN_HDMI1) {
|
|
update_low_latency_mode(aml_dev);
|
|
if (aml_dev->low_latency_mode) {
|
|
aml_dev->low_latency_param.is_signal_stable = check_tv_stream_signal(patch);
|
|
if (aml_dev->low_latency_param.is_signal_stable) {
|
|
mixer_enable_standby(aml_dev->audio_mixer, false);
|
|
}
|
|
}
|
|
ret = reconfig_read_param_through_hdmiin(patch, ringbuffer, ring_buffer_size);
|
|
if (-1 == ret) {
|
|
AM_LOGE(" reconfig_read_param_through_hdmiin failed");
|
|
}
|
|
|
|
audio_pcpd_format_detect(patch->audio_parse_para);
|
|
|
|
/* For chip T5D or older version chip, there is limitation to process data type detection in hardware design.*/
|
|
/* So it needs to do process below to make data detection fast than before to avoid noise happen. */
|
|
if (old_chip_with_hw_det && IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para);
|
|
if (patch && patch->audio_parse_para && audio_is_linear_pcm(cur_aformat)) {
|
|
audio_fmt_check(patch->audio_parse_para, patch->in_buf, read_bytes);
|
|
}
|
|
}
|
|
} else if (patch->input_device == AUDIO_DEVICE_IN_HDMI_ARC) {
|
|
ret = reconfig_read_param_for_arc_spdif(patch, ringbuffer, ring_buffer_size);
|
|
if (ret < 0) {
|
|
AM_LOGE(" reconfig_read_param_for_arc_spdif failed");
|
|
}
|
|
}
|
|
|
|
// when it is game mode, the alsa will be read at game_mode_data_read instead of audio_input_read
|
|
// and we don't write data to ring buffer
|
|
if (patch->is_game_mode) {
|
|
usleep(3000);
|
|
continue;
|
|
}
|
|
|
|
/* For HBR audio from hdmiin/earcin device, set read bytes size to 4 times */
|
|
if (IS_DIGITAL_IN_HW(patch->input_device) &&
|
|
(audio_parse_get_audio_packet_type(patch->audio_parse_para) == AUDIO_PACKET_HBR
|
|
|| patch->spdif_fmt_hw == MAT || patch->spdif_fmt_hw == DTSHD_MA)) {
|
|
period_mul = 4;
|
|
if (SRC_ARCIN != patch->patch_src) {
|
|
patch->tv_param.read_mul_factor = EAC3_MULTIPLIER;
|
|
}
|
|
else {
|
|
patch->tv_param.read_mul_factor = HBR_MULTIPLIER;
|
|
}
|
|
} else if ((patch->aformat == AUDIO_FORMAT_DTS || patch->aformat == AUDIO_FORMAT_DTS_HD) && audio_parse_get_audio_samplerate(patch->audio_parse_para) == 192000) {
|
|
period_mul = 1;
|
|
patch->tv_param.read_mul_factor = HBR_MULTIPLIER;
|
|
} else if (patch->aformat == AUDIO_FORMAT_E_AC3 || patch->aformat == AUDIO_FORMAT_DTS_HD) {
|
|
period_mul = 1;
|
|
patch->tv_param.read_mul_factor = EAC3_MULTIPLIER;
|
|
} else {
|
|
period_mul = 1;
|
|
patch->tv_param.read_mul_factor = 2;
|
|
}
|
|
|
|
audio_input_set_read_factor(patch->input_type, patch->tv_param.read_mul_factor);
|
|
read_bytes = AUDIO_INPUT_DEFAULT_PERIOD_SIZE * audio_bytes_per_sample(patch->in_format) * patch->in_channel_count * period_mul;
|
|
|
|
if (patch->input_device == AUDIO_DEVICE_IN_LINE) {
|
|
read_threshold = 4 * read_bytes;
|
|
} else if ((patch->input_device == AUDIO_DEVICE_IN_HDMI_ARC || patch->input_device == AUDIO_DEVICE_IN_SPDIF)
|
|
&& patch->aformat == AUDIO_FORMAT_AC3 && patch->in_sample_rate == 44100) {
|
|
read_threshold = 2 * read_bytes;
|
|
}
|
|
|
|
// buffer size diff from allocation size, need to resize.
|
|
if (patch->in_buf_size < (size_t)read_bytes) {
|
|
ALOGI("%s: !!realloc in buf size from %zu to %d", __func__, patch->in_buf_size, read_bytes);
|
|
patch->in_buf = aml_audio_realloc(patch->in_buf, read_bytes);
|
|
if (!patch->in_buf) {
|
|
break;
|
|
}
|
|
patch->in_buf_size = read_bytes;
|
|
memset(patch->in_buf, 0, patch->in_buf_size);
|
|
}
|
|
|
|
bytes_avail = read_bytes;
|
|
/* if audio is unstable, don't read data from hardware */
|
|
stable_flag = check_tv_stream_signal(patch);
|
|
if (!stable_flag) {
|
|
/* when audio is unstable, start avsync*/
|
|
audio_patch_set_av_sync_status(patch, true);
|
|
patch->input_signal_stable = false;
|
|
|
|
memset(patch->in_buf, 0, bytes_avail);
|
|
ring_buffer_clear(ringbuffer);
|
|
usleep(20*1000);
|
|
} else {
|
|
patch->input_signal_stable = true;
|
|
/*if ((SRC_HDMIIN == patch->patch_src || SRC_HDMIIN1 == patch->patch_src) && patch->tv_param.audio_packet_type == AUDIO_PACKET_AUDS && patch->in_channel_count != 2) {
|
|
input_stream_channels_adjust(patch, patch->in_buf, read_bytes);
|
|
} else */{
|
|
if (is_tv_mute(aml_dev)) {
|
|
clear_buffer_for_avsync(patch);
|
|
enable_tv_mute(aml_dev, false);
|
|
}
|
|
|
|
//aml_audio_trace_int("input_read_thread", read_bytes);
|
|
read_bytes = audio_input_read(patch->input_type, patch->in_buf, read_bytes);
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_IN) && read_bytes >= 0) {
|
|
aml_dump_audio_bitstreams_with_id("alsa_in_read.raw", patch->in_buf, read_bytes, patch->input_open_counter);
|
|
}
|
|
//aml_audio_trace_int("input_read_thread", 0);
|
|
if (IS_DIGITAL_IN_HW(patch->input_device) && !check_digital_in_stream_signal(patch)) {
|
|
memset(patch->in_buf, 0, bytes_avail);
|
|
}
|
|
}
|
|
|
|
if (IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para);
|
|
if (cur_aformat != AUDIO_FORMAT_PCM_16_BIT && cur_aformat != AUDIO_FORMAT_PCM_32_BIT) {
|
|
audio_raw_data_continuous_check(patch, patch->audio_parse_para, patch->in_buf, read_bytes);
|
|
}
|
|
}
|
|
if (patch->start_mute) {
|
|
int flag = Stop_watch(patch->start_ts, patch->mdelay);
|
|
if (!flag) {
|
|
patch->start_mute = false;
|
|
} else {
|
|
/*coverity[NEGATIVE_RETURNS]:read_bytes won't be negative*/
|
|
memset(patch->in_buf, 0, read_bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*The noise gate is only used as an analog signal input*/
|
|
if ((get_active_inport(aml_dev) == INPORT_LINEIN || get_active_inport(aml_dev) == INPORT_ATV) && is_ng_enable(noise_gate)) {
|
|
int sample_size = (patch->in_format == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(int32_t);
|
|
aml_noise_gate_process(noise_gate, patch->in_buf, (bytes_avail/sample_size), sample_size);
|
|
} else if (get_active_inport(aml_dev) == INPORT_HDMIIN && audio_is_linear_pcm(patch->aformat) && 8 == patch->in_channel_count) {
|
|
// The hdmi input ch order is L R LFE C ***
|
|
// The audio hal ch order is L R C LFE ***
|
|
channel_layout_swap_center_lfe(patch->in_buf, bytes_avail, 8, patch->aformat);
|
|
}
|
|
AM_LOGI_IF((aml_dev->debug_flag > 2), "read_bytes = %d, in_read returns = %d, threshold %d avail data=%d",
|
|
read_bytes, bytes_avail, read_threshold, get_buffer_read_space(ringbuffer));
|
|
|
|
if (bytes_avail > 0) {
|
|
//DoDumpData(patch->in_buf, bytes_avail, CC_DUMP_SRC_TYPE_INPUT);
|
|
do {
|
|
if (get_buffer_write_space(ringbuffer) >= bytes_avail) {
|
|
retry = 0;
|
|
aml_audio_trace_int("input_thread_write2buf", bytes_avail);
|
|
ret = ring_buffer_write(ringbuffer,
|
|
(unsigned char*)patch->in_buf,
|
|
bytes_avail, UNCOVER_WRITE);
|
|
if (ret != bytes_avail) {
|
|
ALOGE("%s(), write buffer fails!", __func__);
|
|
}
|
|
aml_audio_trace_int("input_thread_write2buf", 0);
|
|
|
|
if (!first_start || get_buffer_read_space(ringbuffer) >= read_threshold) {
|
|
pthread_cond_signal(&patch->cond);
|
|
if (first_start) {
|
|
first_start = 0;
|
|
}
|
|
}
|
|
//usleep(1000);
|
|
} else {
|
|
retry = 1;
|
|
pthread_cond_signal(&patch->cond);
|
|
//Fixme: if ringbuffer is full enough but no output, reset ringbuffer
|
|
ALOGD("%s(), ring buffer no space to write, buffer size:%d free size:%d, need write size:%d", __func__,
|
|
ringbuffer->size, get_buffer_write_space(ringbuffer), bytes_avail);
|
|
ring_buffer_reset(ringbuffer);
|
|
usleep(3000);
|
|
}
|
|
} while (retry && !patch->input_thread_exit);
|
|
} else {
|
|
ALOGV("%s(), read alsa pcm fails, to _read(%d), bytes_avail(%d)!",
|
|
__func__, read_bytes, bytes_avail);
|
|
if (get_buffer_read_space(ringbuffer) >= bytes_avail) {
|
|
pthread_cond_signal(&patch->cond);
|
|
}
|
|
usleep(3000);
|
|
}
|
|
}
|
|
|
|
aml_dev->low_latency_mode = false;
|
|
mixer_enable_standby(aml_dev->audio_mixer, true);
|
|
audio_input_close(patch->input_type);
|
|
if (patch->in_buf) {
|
|
aml_audio_free(patch->in_buf);
|
|
patch->in_buf = NULL;
|
|
}
|
|
ALOGD("%s: exit", __func__);
|
|
|
|
return (void *)0;
|
|
}
|
|
|
|
void *audio_patch_output_threadloop(void *data)
|
|
{
|
|
struct aml_audio_patch *patch = (struct aml_audio_patch *)data;
|
|
struct aml_audio_device *aml_dev = (struct aml_audio_device *)patch->dev;
|
|
ring_buffer_t *ringbuffer = & (patch->aml_ringbuffer);
|
|
struct audio_stream_out *stream_out = NULL;
|
|
struct aml_stream_out *out= NULL;
|
|
struct audio_config stream_config;
|
|
struct timespec ts;
|
|
int write_bytes = DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT;
|
|
int ret;
|
|
bool find_iec_sync_word = false;
|
|
AM_LOGD("enter");
|
|
stream_config.channel_mask = patch->out_chanmask;
|
|
stream_config.sample_rate = patch->out_sample_rate;
|
|
stream_config.format = patch->out_format;
|
|
|
|
#ifdef DOLBY_MS12_INPUT_FORMAT_TEST
|
|
int format = 0;
|
|
format = aml_audio_property_get_int("vendor.dolby.ms12.input.format", format);
|
|
if (format == 1) {
|
|
stream_config.format = AUDIO_FORMAT_AC3;
|
|
} else if (format == 2) {
|
|
stream_config.format = AUDIO_FORMAT_E_AC3;
|
|
}
|
|
#endif
|
|
aml_dev->mix_init_flag = false;
|
|
|
|
char address_buf[50] = {0};
|
|
if (patch->input_device == AUDIO_DEVICE_IN_FM_TUNER) {
|
|
strncpy(address_buf, "AML_ATV_SOURCE", sizeof(address_buf) - 1);
|
|
} else if (patch->input_device == AUDIO_DEVICE_IN_HDMI) {
|
|
strncpy(address_buf, "AML_HDMI_SOURCE", sizeof(address_buf) - 1);
|
|
} else if ( patch->input_device == AUDIO_DEVICE_IN_HDMI1) {
|
|
strncpy(address_buf, "AML_HDMI1_SOURCE", sizeof(address_buf) - 1);
|
|
}else if (patch->input_device == AUDIO_DEVICE_IN_LINE) {
|
|
strncpy(address_buf, "AML_AV_SOURCE", sizeof(address_buf) - 1);
|
|
}
|
|
ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev,
|
|
0,
|
|
patch->output_device,
|
|
AUDIO_OUTPUT_FLAG_DIRECT,
|
|
&stream_config,
|
|
&stream_out,
|
|
address_buf);
|
|
if (ret < 0) {
|
|
AM_LOGE("open output stream failed");
|
|
return (void *)0;
|
|
}
|
|
patch->stream_out = stream_out;
|
|
out = (struct aml_stream_out *)stream_out;
|
|
out->decoder_id = patch->decoder_id;
|
|
patch->out_buf_size = write_bytes = out->config.period_size * audio_stream_out_frame_size(&out->stream);
|
|
AM_LOGI("write_bytes:%d out->config.period_size:%d frame_size:%zu", write_bytes,
|
|
out->config.period_size, audio_stream_out_frame_size(&out->stream));
|
|
patch->out_buf = aml_audio_calloc(1, patch->out_buf_size);
|
|
if (!patch->out_buf) {
|
|
aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream);
|
|
AM_LOGE("patch->out_buf calloc fail");
|
|
return (void *)0;
|
|
}
|
|
prctl(PR_SET_NAME, (unsigned long)"audio_output_patch");
|
|
aml_set_thread_priority("audio_output_patch", patch->audio_output_threadID, 5);
|
|
/*affinity the thread to cpu 2/3 which has few IRQ*/
|
|
aml_audio_set_cpu23_affinity();
|
|
|
|
while (!patch->output_thread_exit) {
|
|
int period_mul;
|
|
if (out->external_mute != aml_dev->patch_mute) {
|
|
out_set_stream_mute((struct audio_stream_out *)out, aml_dev->patch_mute, true);
|
|
}
|
|
//! check hdmi input format
|
|
audio_digital_input_format_check(patch);
|
|
if (IS_DIGITAL_IN_HW(patch->input_device) && patch->digital_input_fmt_change) {
|
|
if (out) {
|
|
AM_LOGI("HDMI format change from %x to %x", out->hal_internal_format, patch->aformat);
|
|
}
|
|
|
|
//! close old stream
|
|
if (out != NULL) {
|
|
aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream);
|
|
stream_out = NULL;
|
|
out = NULL;
|
|
}
|
|
//! open new stream
|
|
stream_config.channel_mask = audio_channel_out_mask_from_count(patch->out_channel_count);
|
|
stream_config.format = patch->aformat;
|
|
if (AUDIO_FORMAT_PCM_16_BIT == patch->aformat || AUDIO_FORMAT_PCM_32_BIT == patch->aformat) {//Fix Me:
|
|
stream_config.sample_rate = patch->out_sample_rate;;
|
|
} else {
|
|
stream_config.sample_rate = audio_parse_get_audio_samplerate(patch->audio_parse_para);
|
|
}
|
|
|
|
ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev,
|
|
0,
|
|
patch->output_device,
|
|
AUDIO_OUTPUT_FLAG_DIRECT,
|
|
&stream_config,
|
|
&stream_out,
|
|
address_buf);
|
|
if (ret < 0) {
|
|
AM_LOGE("open output stream failed");
|
|
stream_out = NULL;
|
|
out = NULL;
|
|
continue;
|
|
}
|
|
//hdmi avsync need update sink format and optical format before do sync;
|
|
//Otherwise, it will lead to inaccurate delay calculations.
|
|
get_sink_format(aml_dev);
|
|
//! set stream format and hal rate
|
|
patch->stream_out = stream_out;
|
|
out = (struct aml_stream_out *)stream_out;
|
|
write_bytes = out->config.period_size * audio_stream_out_frame_size(&out->stream);
|
|
if (out->hal_internal_format != AUDIO_FORMAT_PCM_16_BIT &&
|
|
out->hal_internal_format != AUDIO_FORMAT_PCM_32_BIT) {
|
|
out->hal_format = AUDIO_FORMAT_IEC61937;
|
|
find_iec_sync_word = false;
|
|
//Check DTS-CD
|
|
if (out->hal_internal_format == AUDIO_FORMAT_DTS ||
|
|
out->hal_internal_format == AUDIO_FORMAT_DTS_HD) {
|
|
if (audio_parse_get_audio_type_direct(patch->audio_parse_para) == DTSCD ) {
|
|
out->is_dtscd = true;
|
|
} else {
|
|
out->is_dtscd = false;
|
|
}
|
|
}
|
|
//Reset dd and ddp sample rate
|
|
if (out->hal_internal_format == AUDIO_FORMAT_AC3 ||
|
|
out->hal_internal_format == AUDIO_FORMAT_E_AC3) {
|
|
if (out->hal_rate == 96000 || out->hal_rate == 192000) {
|
|
out->hal_rate = 48000;
|
|
} else if (out->hal_rate == 88200 || out->hal_rate == 176400) {
|
|
out->hal_rate = 44100;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* reset audio patch ringbuffer */
|
|
ring_buffer_reset(&patch->aml_ringbuffer);
|
|
|
|
// fixed switch between RAW and PCM noise, drop delay residual data
|
|
aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF);
|
|
aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF_RAW);
|
|
aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF_B_RAW);
|
|
|
|
/* we need standby a2dp when switch the format, in order to prevent UNDERFLOW in a2dp stack. */
|
|
if (aml_dev->active_outport == OUTPORT_A2DP) {
|
|
aml_dev->need_reset_a2dp = true;
|
|
}
|
|
|
|
//when format change, we need do av sync again
|
|
audio_patch_set_av_sync_status(patch, true);
|
|
patch->digital_input_fmt_change = false;
|
|
}
|
|
|
|
// when it is game mode, we don't read data from ring buffer
|
|
if (patch->is_game_mode) {
|
|
usleep(3000);
|
|
continue;
|
|
}
|
|
|
|
period_mul = 1;
|
|
if (out == NULL) {
|
|
AM_LOGI("out == NULL, need check!!");
|
|
}
|
|
if (out->hal_internal_format == AUDIO_FORMAT_AC3) {
|
|
write_bytes = AC3_PERIOD_SIZE;
|
|
} else if (out->hal_internal_format == AUDIO_FORMAT_E_AC3) {
|
|
write_bytes = EAC3_PERIOD_SIZE;
|
|
} else if (out->hal_internal_format == AUDIO_FORMAT_MAT) {
|
|
write_bytes = MAT_PERIOD_SIZE;
|
|
} else if (patch->aformat == AUDIO_FORMAT_DTS_HD){
|
|
period_mul = HBR_MULTIPLIER;
|
|
}
|
|
|
|
//ALOGI("%s period_mul = %d ", __func__, period_mul);
|
|
// buffer size diff from allocation size, need to resize.
|
|
if (patch->out_buf_size < (size_t)write_bytes * period_mul) {
|
|
AM_LOGI("!!realloc out buf size from %zu to %d", patch->out_buf_size, write_bytes * period_mul);
|
|
patch->out_buf = aml_audio_realloc(patch->out_buf, write_bytes * period_mul);
|
|
patch->out_buf_size = write_bytes * period_mul;
|
|
}
|
|
|
|
pthread_mutex_lock(&patch->mutex);
|
|
ALOGV("%s(), ringbuffer level read before wait--%d write_bytes:%d, period_mul:%d",
|
|
__func__, get_buffer_read_space(ringbuffer), write_bytes, period_mul);
|
|
if (get_buffer_read_space(ringbuffer) < (write_bytes * period_mul)) {
|
|
// wait 300ms
|
|
ts_wait_time(&ts, 300000);
|
|
pthread_cond_timedwait(&patch->cond, &patch->mutex, &ts);
|
|
if (patch->output_thread_exit) {
|
|
AM_LOGI("need to exit");
|
|
pthread_mutex_unlock(&patch->mutex);
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&patch->mutex);
|
|
|
|
ALOGV("%s(), ringbuffer level read after wait-- %d, %d * %d",
|
|
__func__, get_buffer_read_space(ringbuffer), write_bytes, period_mul);
|
|
|
|
if (is_dolby_ms12_support_compression_format(out->hal_internal_format)) {
|
|
int pos_sync_word = -1;
|
|
int need_drop_size = 0;
|
|
int ring_buffer_read_space = get_buffer_read_space(ringbuffer);
|
|
pos_sync_word = find_61937_sync_word_position_in_ringbuffer(ringbuffer);
|
|
|
|
if (pos_sync_word >= 0) {
|
|
need_drop_size = pos_sync_word;
|
|
find_iec_sync_word = true;
|
|
} else if (pos_sync_word == -1 && (find_iec_sync_word == false || ring_buffer_read_space > write_bytes)) {
|
|
//write_bytes is frame size.
|
|
//We should be able to find sync word within a frame size.
|
|
//If we cannot find it, it is considered that there is a problem with the data
|
|
need_drop_size = ring_buffer_read_space;
|
|
} else if (pos_sync_word == -1) {
|
|
continue;
|
|
}
|
|
|
|
if (need_drop_size >0) {
|
|
AM_LOGE("data error, we drop data(%d) pos_sync_word(%d) find_sync_word(%d) read_space(%d)(%d) out_buf_size(%zu), write_bytes(%d)",
|
|
need_drop_size, pos_sync_word, find_iec_sync_word, ring_buffer_read_space, ringbuffer->size, patch->out_buf_size, write_bytes);
|
|
int drop_size = ring_buffer_seek(ringbuffer, need_drop_size);
|
|
if (drop_size != need_drop_size) {
|
|
AM_LOGE("drop fail, need_drop_size(%d) actual drop_size(%d)", need_drop_size, drop_size);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (get_buffer_read_space(ringbuffer) >= (write_bytes * period_mul)) {
|
|
ret = ring_buffer_read(ringbuffer,
|
|
(unsigned char*)patch->out_buf, write_bytes * period_mul);
|
|
if (ret == 0) {
|
|
AM_LOGE("ring_buffer read 0 data! write_bytes(%d), period_mul(%d)", write_bytes, period_mul);
|
|
}
|
|
|
|
/* avsync for dev->dev patch*/
|
|
//When doing AV sync, it is necessary to mute, and then unmute after AV sync ends
|
|
if ((out->internal_mute != patch->need_do_avsync) && (patch->patch_src != SRC_ARCIN) && (patch->patch_src != SRC_SPDIFIN)) {
|
|
out_set_stream_mute (stream_out, patch->need_do_avsync, false);
|
|
AM_LOGI("set stream mute to %d", patch->need_do_avsync);
|
|
}
|
|
if (patch && (patch->need_do_avsync == true) && (patch->input_signal_stable == true) &&
|
|
(SRC_ATV == patch->patch_src || SRC_HDMIIN == patch->patch_src || SRC_HDMIIN1 == patch->patch_src ||
|
|
(SRC_LINEIN == patch->patch_src && !patch->av_audio_only))) {
|
|
|
|
aml_dev_try_avsync(patch);
|
|
if (patch->skip_frames) {
|
|
ALOGD("%s(), skip this period data for avsync!", __func__);
|
|
usleep(5);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
stream_out->write(stream_out, patch->out_buf, ret);
|
|
} else {
|
|
AM_LOGV("no enough data in ring buffer, available data size:%d, need data size:%d", get_buffer_read_space(ringbuffer), (write_bytes * period_mul));
|
|
usleep( (DEFAULT_PLAYBACK_PERIOD_SIZE) * 1000000 / 4 /stream_config.sample_rate);
|
|
}
|
|
}
|
|
|
|
patch->stream_out = NULL;
|
|
aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream);
|
|
if (patch->out_buf) {
|
|
aml_audio_free(patch->out_buf);
|
|
patch->out_buf = NULL;
|
|
}
|
|
AM_LOGD("exit");
|
|
|
|
return (void *)0;
|
|
}
|
|
|
|
|
|
static int create_patch_l(struct aml_audio_device *aml_dev, audio_devices_t input, struct aml_audio_patch ** apatch)
|
|
{
|
|
struct aml_audio_patch *patch;
|
|
int play_buffer_size = DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT;
|
|
int ret = 0;
|
|
|
|
AM_LOGD("enter");
|
|
|
|
patch = aml_audio_calloc(1, sizeof(*patch));
|
|
if (!patch) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
patch->dev = aml_dev;
|
|
patch->input_device = input;
|
|
patch->patch_src = get_patch_source(input, PATCH_ROUTE_DEV_DEV);
|
|
patch->digital_input_fmt_change = false;
|
|
patch->input_type = audio_input_get_type(input);
|
|
AM_LOGI("input 0x%x patch_src=%d, input_type=%d", input, patch->patch_src, patch->input_type);
|
|
pthread_mutex_init(&patch->mutex, NULL);
|
|
pthread_cond_init(&patch->cond, NULL);
|
|
|
|
patch->in_sample_rate = 48000;
|
|
patch->in_chanmask = AUDIO_CHANNEL_IN_STEREO;
|
|
patch->output_device = AUDIO_DEVICE_OUT_SPEAKER;
|
|
patch->out_sample_rate = 48000;
|
|
patch->out_channel_count = 2;
|
|
patch->out_chanmask = AUDIO_CHANNEL_OUT_STEREO;
|
|
if (32 == aml_dev->audio_process_bitwidth) {
|
|
patch->in_format = AUDIO_FORMAT_PCM_32_BIT;
|
|
patch->out_format = AUDIO_FORMAT_PCM_32_BIT;
|
|
patch->aformat = AUDIO_FORMAT_PCM_32_BIT;
|
|
} else {
|
|
patch->in_format = AUDIO_FORMAT_PCM_16_BIT;
|
|
patch->out_format = AUDIO_FORMAT_PCM_16_BIT;
|
|
patch->aformat = AUDIO_FORMAT_PCM_16_BIT;
|
|
}
|
|
patch->last_parser_type = NOT_READY;
|
|
|
|
/* when audio patch start, singal is unstale or
|
|
* patch signal is unstable, it need do avsync
|
|
*/
|
|
audio_patch_set_av_sync_status(patch, true);
|
|
|
|
ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 2 * play_buffer_size * PATCH_PERIOD_COUNT);
|
|
if (ret < 0) {
|
|
AM_LOGE("init audio ringbuffer failed");
|
|
goto err_ring_buf;
|
|
}
|
|
|
|
if (IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
//TODO add sample rate and channel information
|
|
ret = creat_pthread_for_audio_type_parse(&patch->audio_parse_threadID,
|
|
&patch->audio_parse_para, &aml_dev->alsa_mixer, patch->input_device);
|
|
if (ret != 0) {
|
|
AM_LOGE("create format parse thread failed");
|
|
goto err_parse_thread;
|
|
}
|
|
}
|
|
|
|
ret = pthread_create(&patch->audio_input_threadID, NULL,
|
|
&audio_patch_input_threadloop, patch);
|
|
|
|
if (ret != 0) {
|
|
AM_LOGE("Create input thread failed");
|
|
goto err_in_thread;
|
|
}
|
|
ret = pthread_create(&patch->audio_output_threadID, NULL,
|
|
&audio_patch_output_threadloop, patch);
|
|
if (ret != 0) {
|
|
AM_LOGE("Create output thread failed");
|
|
goto err_out_thread;
|
|
}
|
|
|
|
/* if (IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
ret = pthread_create(&patch->audio_signal_detect_threadID, NULL,
|
|
&audio_patch_signal_detect_threadloop, patch);
|
|
if (ret != 0) {
|
|
AM_LOGE("Create signal detect thread failed");
|
|
goto err_signal_detect_thread;
|
|
}
|
|
} */
|
|
|
|
/* Use flag to indicate that patch struct is ready. TBD */
|
|
//validate_dev_patch(aml_dev);
|
|
clock_gettime(CLOCK_MONOTONIC, &patch->patch_start_ts);
|
|
*apatch = patch;
|
|
#if (_TIME_BITS == 64)
|
|
AM_LOGD("exit patch_start_ts[%"PRId64".%ld]", patch->patch_start_ts.tv_sec, patch->patch_start_ts.tv_nsec/1000000);
|
|
#else
|
|
AM_LOGD("exit patch_start_ts[%ld.%ld]", patch->patch_start_ts.tv_sec, patch->patch_start_ts.tv_nsec/1000000);
|
|
#endif
|
|
return 0;
|
|
|
|
err_parse_thread:
|
|
patch->output_thread_exit = 1;
|
|
pthread_join(patch->audio_output_threadID, NULL);
|
|
err_out_thread:
|
|
patch->input_thread_exit = 1;
|
|
pthread_join(patch->audio_input_threadID, NULL);
|
|
err_in_thread:
|
|
ring_buffer_release(&patch->aml_ringbuffer);
|
|
err_ring_buf:
|
|
aml_audio_free(patch);
|
|
return ret;
|
|
}
|
|
|
|
int release_patch_l(struct aml_audio_device *aml_dev, struct aml_audio_patch *apatch)
|
|
{
|
|
struct aml_audio_patch *patch = apatch;
|
|
AM_LOGI(" enter");
|
|
/* Use flag to indicate that it will start to free patch struct. TBD */
|
|
//invalidate_dev_patch(aml_dev);
|
|
|
|
patch->output_thread_exit = 1;
|
|
pthread_cond_signal(&patch->cond);
|
|
pthread_join(patch->audio_output_threadID, NULL);
|
|
|
|
patch->input_thread_exit = 1;
|
|
audio_input_set_exit_flag(patch->input_type);
|
|
patch->tv_param.patch_input_thread_exit = 1;
|
|
|
|
pthread_join(patch->audio_input_threadID, NULL);
|
|
|
|
/* if (IS_DIGITAL_IN_HW(patch->input_device)) {
|
|
patch->signal_detect_thread_exit = 1;
|
|
pthread_join(patch->audio_signal_detect_threadID, NULL);
|
|
} */
|
|
|
|
if (IS_DIGITAL_IN_HW(patch->input_device))
|
|
exit_pthread_for_audio_type_parse(patch->audio_parse_threadID,&patch->audio_parse_para);
|
|
|
|
ring_buffer_release(&patch->aml_ringbuffer);
|
|
aml_audio_free(patch);
|
|
|
|
AM_LOGI(" exit");
|
|
return 0;
|
|
}
|
|
|
|
|
|
int release_tv_patch(struct aml_audio_device *aml_dev, void *apatch)
|
|
{
|
|
pthread_mutex_lock(&aml_dev->patch_lock);
|
|
release_patch_l(aml_dev, (struct aml_audio_patch *)apatch);
|
|
pthread_mutex_unlock(&aml_dev->patch_lock);
|
|
return 0;
|
|
}
|
|
|
|
int create_tv_patch(struct aml_audio_device *aml_dev, audio_devices_t input, void ** apatch)
|
|
{
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&aml_dev->patch_lock);
|
|
ret = create_patch_l(aml_dev, input, (struct aml_audio_patch **)apatch);
|
|
pthread_mutex_unlock(&aml_dev->patch_lock);
|
|
|
|
return ret;
|
|
}
|