blob: e00cdc632ab62df056e2cefb818762533445ba5f [file] [log] [blame]
/*
* 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 <utils/Timers.h>
#include <cutils/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 "aml_audio_ms12_bypass.h"
#include "aml_audio_dev2mix_process.h"
#include "device_patch_mgr.h"
#include "audio_hw_resource_mgr.h"
#include "component_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_src)) {
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;
}
}
}
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);
cur_type = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_TYPE);
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);
struct audio_stream_in *stream_in = NULL;
struct aml_stream_in *in;
struct audio_config stream_config;
struct timespec ts;
int aux_read_bytes, read_bytes;
// FIXME: add calc for read_bytes;
read_bytes = DEFAULT_CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT;
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);
ALOGI("++%s", __FUNCTION__);
ALOGD("%s: enter", __func__);
patch->chanmask = stream_config.channel_mask = patch->in_chanmask;
stream_config.sample_rate = patch->in_sample_rate;
patch->aformat = stream_config.format = patch->in_format;
ret = aml_dev->hw_device.open_input_stream((struct audio_hw_device *)patch->dev, 0, patch->input_src, &stream_config, &stream_in, 0, NULL, 0);
if (ret < 0) {
ALOGE("%s: open input steam failed ret = %d", __func__, ret);
return (void *)0;
}
in = (struct aml_stream_in *)stream_in;
patch->stream_in = in;
/* 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 (in->device & AUDIO_DEVICE_IN_LINE) {
in->mute_flag = true;
}
patch->in_buf_size = read_bytes = in->config.period_size * audio_stream_in_frame_size(&in->stream);
patch->in_buf = aml_audio_calloc(1, patch->in_buf_size);
if (!patch->in_buf) {
AM_LOGE("patch->in_buf malloc fail, close input stream");
aml_dev->hw_device.close_input_stream((struct audio_hw_device *)patch->dev, &in->stream);
patch->stream_in = NULL;
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();
if (ringbuffer) {
ring_buffer_size = ringbuffer->size;
}
while (!patch->input_thread_exit) {
int bytes_avail = 0;
int period_mul = 1;
int read_threshold = 0;
/* For HBR audio from hdmiin/earcin device, set read bytes size to 4 times */
if (IS_DIGITAL_IN_HW(patch->input_src) &&
(audio_parse_get_audio_packet_type(patch->audio_parse_para) == AUDIO_PACKET_HBR || in->spdif_fmt_hw == MAT)) {
period_mul = 4;
if (SRC_ARCIN != patch->patch_src) {
in->tv_param.read_mul_factor = EAC3_MULTIPLIER;
}
else {
in->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;
in->tv_param.read_mul_factor = HBR_MULTIPLIER;
} else if (patch->aformat == AUDIO_FORMAT_E_AC3 || patch->aformat == AUDIO_FORMAT_DTS_HD) {
period_mul = 1;
in->tv_param.read_mul_factor = EAC3_MULTIPLIER;
} else {
period_mul = 1;
in->tv_param.read_mul_factor = 2;
}
if (patch->input_src == AUDIO_DEVICE_IN_HDMI) {
aml_check_pic_mode(patch);
}
if (!is_low_latency_mode(aml_dev)) {
read_bytes = DEFAULT_CAPTURE_PERIOD_SIZE * audio_stream_in_frame_size(&in->stream) * period_mul;
}
else {
read_bytes = LOW_LATENCY_CAPTURE_PERIOD_SIZE * audio_stream_in_frame_size(&in->stream);
}
if (patch->input_src == AUDIO_DEVICE_IN_LINE) {
read_threshold = 4 * 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);
}
if (in->standby) {
ret = start_input_stream(in);
if (ret < 0) {
ALOGE("start_input_stream failed !");
}
in->standby = 0;
}
bytes_avail = read_bytes;
/* if audio is unstable, don't read data from hardware */
stable_flag = check_tv_stream_signal(&in->stream);
if (!stable_flag) {
/* when audio is unstable, start avsync*/
audio_patch_set_av_sync_status(patch, true);
patch->input_signal_stable = false;
aml_dev->mute_start = true;
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 && in->tv_param.audio_packet_type == AUDIO_PACKET_AUDS && in->config.channels != 2) {
input_stream_channels_adjust(&in->stream, 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);
aml_alsa_input_read(&in->stream, patch->in_buf, read_bytes);
//aml_audio_trace_int("input_read_thread", 0);
if (IS_DIGITAL_IN_HW(patch->input_src) && !check_digital_in_stream_signal(patch, &in->stream)) {
memset(patch->in_buf, 0, bytes_avail);
}
}
if (IS_DIGITAL_IN_HW(patch->input_src)) {
cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para);
if (cur_aformat != AUDIO_FORMAT_PCM_16_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 {
memset(patch->in_buf, 0, read_bytes);
}
}
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
aml_dump_audio_bitstreams_with_id("alsa_in_read.raw", patch->in_buf, read_bytes, in->unique_ID);
}
}
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_src)) {
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);
}
}
#ifdef USE_EQ_DRC
/*noise gate is only used in Linein for 16bit audio data*/
if (get_active_inport(aml_dev) == INPORT_LINEIN && is_ng_enable(aml_dev)) {
int ng_status = noise_gate_process(aml_dev, patch->in_buf, bytes_avail >> 1);
/*if (ng_status == NG_MUTE)
ALOGI("noise gate is working!");*/
}
#endif
ALOGV("++%s in read over read_bytes = %d, in_read returns = %d, threshold %d avail data=%d",
__FUNCTION__, 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 (patch->input_src == AUDIO_DEVICE_IN_HDMI)
{
pthread_mutex_lock(&in->lock);
ret = reconfig_read_param_through_hdmiin(patch, in, ringbuffer, ring_buffer_size);
pthread_mutex_unlock(&in->lock);
if (ret == 0) {
break;
}
}
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->hw_device.close_input_stream((struct audio_hw_device *)patch->dev, &in->stream);
patch->stream_in = NULL;
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;
struct dolby_ms12_desc *ms12 = &(aml_dev->ms12);
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 original_write_bytes = write_bytes;
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;
aml_dev->mute_start = true;
char address_buf[50] = {0};
if (patch->input_src == AUDIO_DEVICE_IN_FM_TUNER) {
strncpy(address_buf, "AML_ATV_SOURCE", 14);
} else if (patch->input_src == AUDIO_DEVICE_IN_HDMI) {
strncpy(address_buf, "AML_HDMI_SOURCE", 15);
} else if (patch->input_src == AUDIO_DEVICE_IN_LINE) {
strncpy(address_buf, "AML_AV_SOURCE", 13);
}
ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev,
0,
patch->output_src,
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;
patch->out_buf_size = write_bytes = original_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;
//! check hdmi input format
audio_digital_input_format_check(patch);
if (IS_DIGITAL_IN_HW(patch->input_src) && 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_parse_get_audio_channel_mask (patch->audio_parse_para);
stream_config.format = patch->aformat;
if (patch->aformat == AUDIO_FORMAT_PCM_16_BIT) {//Fix Me:
stream_config.sample_rate = 48000;
} 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_src,
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;
}
//! set stream format and hal rate
patch->stream_out = stream_out;
out = (struct aml_stream_out *)stream_out;
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);
#ifdef ADD_AUDIO_DELAY_INTERFACE
// 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);
#endif
/* 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;
}
period_mul = 1;
write_bytes = original_write_bytes;
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);
if (is_low_latency_mode(aml_dev)) {
write_bytes = LOW_LATENCY_PLAYBACK_PERIOD_SIZE * audio_stream_out_frame_size(&out->stream);
}
// 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);
}
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->mute != patch->need_do_avsync) {
out->mute = patch->need_do_avsync;
AM_LOGI("set stream mute to %d", out->mute);
}
if (patch && (patch->need_do_avsync == true) && (patch->input_signal_stable == true) &&
(SRC_ATV == patch->patch_src || SRC_HDMIIN == patch->patch_src ||
SRC_LINEIN == patch->patch_src || SRC_SPDIFIN == patch->patch_src)) {
if (!is_low_latency_mode(aml_dev)) {
aml_dev_try_avsync(patch);
if (patch->skip_frames) {
ALOGD("%s(), skip this period data for avsync!", __func__);
usleep(5);
continue;
}
} else {
audio_patch_set_av_sync_status(patch, false);
}
}
/* reconfig output in picture mode switch */
if (patch && patch->input_src == AUDIO_DEVICE_IN_HDMI) {
stream_check_reconfig_param(stream_out);
}
stream_out->write(stream_out, patch->out_buf, ret);
} else {
ALOGV("%s(), no enough data in ring buffer, available data size:%d, need data size:%d", __func__,
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;
pthread_attr_t attr;
struct sched_param param;
int ret = 0;
struct tv_private_object *tv_obj = get_tv_object(aml_dev);
AM_LOGD("enter");
patch = aml_audio_calloc(1, sizeof(*patch));
if (!patch) {
return -ENOMEM;
}
patch->dev = aml_dev;
patch->input_src = input;
patch->patch_src = get_patch_source(input, PATCH_ROUTE_DEV_DEV);
patch->is_dtv_src = false;
patch->aformat = AUDIO_FORMAT_PCM_16_BIT;
patch->digital_input_fmt_change = false;
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_src = AUDIO_DEVICE_OUT_SPEAKER;
patch->out_sample_rate = 48000;
patch->out_chanmask = AUDIO_CHANNEL_OUT_STEREO;
patch->in_format = AUDIO_FORMAT_PCM_16_BIT;
patch->out_format = AUDIO_FORMAT_PCM_16_BIT;
/* when audio patch start, singal is unstale or
* patch signal is unstable, it need do avsync
*/
audio_patch_set_av_sync_status(patch, true);
if (patch->out_format == AUDIO_FORMAT_PCM_16_BIT) {
AM_LOGD("init audio ringbuffer game %d", is_low_latency_mode(aml_dev));
if (!is_low_latency_mode(aml_dev))
ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 2 * play_buffer_size * PATCH_PERIOD_COUNT);
else
ret = ring_buffer_init(&patch->aml_ringbuffer, 2 * 4 * LOW_LATENCY_PLAYBACK_PERIOD_SIZE);
} else {
ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 4 * 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_src)) {
//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_src);
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_src)) {
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);
*apatch = patch;
AM_LOGD("exit");
return 0;
err_signal_detect_thread:
patch->signal_detect_thread_exit = 1;
pthread_join(patch->audio_parse_threadID, NULL);
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;
ALOGD("%s: enter", __func__);
bool is_game_mode = patch->is_game_mode;
/* Use flag to indicate that it will start to free patch struct. TBD */
//invalidate_dev_patch(aml_dev);
patch->output_thread_exit = 1;
pthread_join(patch->audio_output_threadID, NULL);
patch->input_thread_exit = 1;
if (patch->stream_in) {
patch->stream_in->tv_param.patch_input_thread_exit = 1;
} else {
AM_LOGE("patch->stream_in is NULL");
}
pthread_join(patch->audio_input_threadID, NULL);
if (IS_DIGITAL_IN_HW(patch->input_src)) {
patch->signal_detect_thread_exit = 1;
pthread_join(patch->audio_signal_detect_threadID, NULL);
}
if (IS_DIGITAL_IN_HW(patch->input_src))
exit_pthread_for_audio_type_parse(patch->audio_parse_threadID,&patch->audio_parse_para);
ring_buffer_release(&patch->aml_ringbuffer);
aml_audio_free(patch);
//after release patch, game mode may change
if (is_game_mode) {
if (false == is_low_latency_mode(aml_dev)) {
AM_LOGI("game mode change to false, reconfig out");
alsa_out_reconfig_params(aml_dev);
}
}
ALOGD("%s: exit is_game_mode:%d(%d)", __func__, is_game_mode, is_low_latency_mode(aml_dev));
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;
}