710 lines
24 KiB
C
710 lines
24 KiB
C
/*
|
|||
|
|
* Copyright (C) 2017 Amlogic Corporation.
|
||
|
|
*
|
||
|
|
* 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_hal_primary"
|
||
|
|
//#define LOG_NDEBUG 0
|
||
|
|
|
||
|
|
#include "aml_audio_log.h"
|
||
|
|
#include <linux/ioctl.h>
|
||
|
|
#include <inttypes.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#include "audio_hw.h"
|
||
|
|
#include "audio_hw_utils.h"
|
||
|
|
#include "aml_audio_stream.h"
|
||
|
|
#include "tv_patch_avsync.h"
|
||
|
|
#include "alsa_manager.h"
|
||
|
|
#include "dolby_lib_api.h"
|
||
|
|
#include "aml_alsa_mixer.h"
|
||
|
|
#include "audio_hw_resource_mgr.h"
|
||
|
|
#include "amlAudioMixer.h"
|
||
|
|
#include "tv_patch_ctrl.h"
|
||
|
|
#include "audio_port.h"
|
||
|
|
#include "aml_audio_output.h"
|
||
|
|
#include "tv_patch.h"
|
||
|
|
#include "aml_audio_property.h"
|
||
|
|
|
||
|
|
static int get_tvin_delay(struct aml_mixer_handle *mixer_handle);
|
||
|
|
static int get_tvin_max_delay(struct aml_mixer_handle *mixer_handle);
|
||
|
|
static int get_tvin_min_delay(struct aml_mixer_handle *mixer_handle);
|
||
|
|
static int aml_dev_tune_video_path_latency(struct aml_mixer_handle *mixer_handle, unsigned int video_val);
|
||
|
|
|
||
|
|
static int get_tvin_delay(struct aml_mixer_handle *mixer_handle)
|
||
|
|
{
|
||
|
|
int delay = 0;
|
||
|
|
|
||
|
|
delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_DELAY);
|
||
|
|
|
||
|
|
return delay;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int get_tvin_max_delay(struct aml_mixer_handle *mixer_handle)
|
||
|
|
{
|
||
|
|
int delay = 0;
|
||
|
|
|
||
|
|
delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_MAX_DELAY);
|
||
|
|
|
||
|
|
return delay;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int get_tvin_min_delay(struct aml_mixer_handle *mixer_handle)
|
||
|
|
{
|
||
|
|
int delay = 0;
|
||
|
|
|
||
|
|
delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_MIN_DELAY);
|
||
|
|
|
||
|
|
return delay;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void check_skip_frames(struct aml_audio_patch *patch)
|
||
|
|
{
|
||
|
|
struct aml_audio_device *aml_dev;
|
||
|
|
uint64_t frames = 0;
|
||
|
|
uint64_t alsa_out_i2s_ltcy = -1;
|
||
|
|
|
||
|
|
if (!patch) {
|
||
|
|
AM_LOGE("patch is NULL");
|
||
|
|
return ;
|
||
|
|
}
|
||
|
|
aml_dev = (struct aml_audio_device *)patch->dev;
|
||
|
|
patch->skip_frames = 0;
|
||
|
|
/* for spk, check i2s device latency */
|
||
|
|
if (patch->need_do_avsync == true &&
|
||
|
|
patch->is_avsync_start == false && is_arc_connected(aml_dev) == 0) {
|
||
|
|
frames = aml_audio_pcm_out_get_delayframe(aml_dev, false);
|
||
|
|
if (frames > 0) {
|
||
|
|
alsa_out_i2s_ltcy = frames / SAMPLE_RATE_MS;
|
||
|
|
|
||
|
|
/* start to skip the output frames to reduce alsa out latency */
|
||
|
|
if (alsa_out_i2s_ltcy >= AVSYNC_ALSA_OUT_MAX_LATENCY) {
|
||
|
|
patch->skip_frames = 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int alsa_out_spdif_ltcy = -1;
|
||
|
|
/* for arc, check spdif device latency */
|
||
|
|
if (patch->need_do_avsync == true &&
|
||
|
|
patch->is_avsync_start == false && is_arc_connected(aml_dev) == 1) {
|
||
|
|
if ( 0 == aml_alsa_output_getinfo(aml_dev->alsa_handle[DIGITAL_DEVICE], OUTPUT_INFO_DELAYFRAME, (alsa_output_info_t *)&alsa_out_spdif_ltcy)) {
|
||
|
|
/* start to skip the output frames to reduce alsa out latency */
|
||
|
|
if (alsa_out_spdif_ltcy >= AVSYNC_ALSA_OUT_MAX_LATENCY_ARC) {
|
||
|
|
patch->skip_frames = 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (patch->skip_frames > 0) {
|
||
|
|
ALOGI("spdif latency:%d i2s latency: %" PRIu64 " skip frame:%d",
|
||
|
|
alsa_out_spdif_ltcy, alsa_out_i2s_ltcy, patch->skip_frames);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int calc_frame_to_latency(int frames, audio_format_t format)
|
||
|
|
{
|
||
|
|
int latency = frames / SAMPLE_RATE_MS;
|
||
|
|
|
||
|
|
if (format == AUDIO_FORMAT_E_AC3) {
|
||
|
|
latency /= EAC3_MULTIPLIER;
|
||
|
|
} else if (format == AUDIO_FORMAT_MAT ||
|
||
|
|
(format == AUDIO_FORMAT_DOLBY_TRUEHD)) {
|
||
|
|
latency /= MAT_MULTIPLIER;
|
||
|
|
}
|
||
|
|
return latency;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int calc_latency_to_frame(int latency, audio_format_t format)
|
||
|
|
{
|
||
|
|
int frames = 0;
|
||
|
|
int coefficient = 1;
|
||
|
|
|
||
|
|
if (format == AUDIO_FORMAT_E_AC3) {
|
||
|
|
coefficient = EAC3_MULTIPLIER;
|
||
|
|
} else if (format == AUDIO_FORMAT_MAT || (format == AUDIO_FORMAT_DOLBY_TRUEHD)) {
|
||
|
|
coefficient = MAT_MULTIPLIER;
|
||
|
|
}
|
||
|
|
|
||
|
|
frames = latency * SAMPLE_RATE_MS * coefficient;
|
||
|
|
|
||
|
|
return frames;
|
||
|
|
}
|
||
|
|
|
||
|
|
int clear_buffer_for_avsync(struct aml_audio_patch *patch)
|
||
|
|
{
|
||
|
|
struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle();
|
||
|
|
|
||
|
|
AM_LOGI("stream_out(%p) is_pcm(%d) is_game_mode(%d)", patch->stream_out, audio_is_linear_pcm(patch->aformat), is_low_latency_mode(aml_dev));
|
||
|
|
if (patch->stream_out) {
|
||
|
|
patch->stream_out->flush(patch->stream_out);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((audio_is_linear_pcm(patch->aformat)) && is_low_latency_mode(aml_dev)) {
|
||
|
|
ring_buffer_reset(&patch->aml_ringbuffer);
|
||
|
|
aml_audio_pcm_out_flush(aml_dev);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ringbuffer_seek(struct aml_audio_patch *patch, int tune_val)
|
||
|
|
{
|
||
|
|
int space = 0, seek_space = 0, frame_size = 0;
|
||
|
|
|
||
|
|
frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT);
|
||
|
|
space = calc_latency_to_frame(tune_val, patch->aformat) * frame_size;
|
||
|
|
|
||
|
|
seek_space = ring_buffer_seek(&patch->aml_ringbuffer, space);
|
||
|
|
|
||
|
|
if (seek_space == space) {
|
||
|
|
ALOGV(" --tuning audio ringbuffer %dms successfully!\n", tune_val);
|
||
|
|
} else {
|
||
|
|
ALOGV(" --tuning audio ringbuffer require %d vs actual seek %d\n", space, seek_space);
|
||
|
|
tune_val = calc_frame_to_latency(seek_space/frame_size, patch->aformat);
|
||
|
|
}
|
||
|
|
|
||
|
|
return tune_val;
|
||
|
|
}
|
||
|
|
|
||
|
|
int submix_ringbuffer_seek(struct aml_audio_patch *patch, int tune_val)
|
||
|
|
{
|
||
|
|
struct aml_audio_device *aml_dev;
|
||
|
|
aml_dev = (struct aml_audio_device *)patch->dev;
|
||
|
|
struct aml_stream_out *out = (struct aml_stream_out *)patch->stream_out;
|
||
|
|
if (!out) {
|
||
|
|
AM_LOGE("Error, Failed to get patch out stream, return!");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
if (!aml_dev->audio_mixer) {
|
||
|
|
AM_LOGE("Error, Failed to get submix, return!");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int space = 0, seek_space = 0, frame_size = 0;
|
||
|
|
|
||
|
|
frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT);
|
||
|
|
space = calc_latency_to_frame(tune_val, AUDIO_FORMAT_PCM_16_BIT) * frame_size;
|
||
|
|
|
||
|
|
seek_space = mixer_seek_inport(aml_dev->audio_mixer, out->inputPortID, space);
|
||
|
|
|
||
|
|
if (seek_space == space) {
|
||
|
|
ALOGV(" --tuning audio smringbuffer %dms successfully!\n", tune_val);
|
||
|
|
} else {
|
||
|
|
ALOGV(" --tuning audio smringbuffer require %d vs actual seek %d\n", space, seek_space);
|
||
|
|
tune_val = calc_frame_to_latency(seek_space/frame_size, AUDIO_FORMAT_PCM_16_BIT);
|
||
|
|
}
|
||
|
|
|
||
|
|
return tune_val;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_decoder_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_DECODER_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_DECODER_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_pipeline_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_PIPELINE_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_PIPELINE_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_dap_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_DAP_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_DAP_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_encoder_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_ENCODER_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_ENCODER_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_dd_ddp_buffer_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_DD_DDP_BUFFER_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_DD_DDP_BUFFER_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_ms12_mat_buffer_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= MS12_MAT_BUFFER_LATENCY_PROPERTY;
|
||
|
|
latency_ms = MS12_MAT_BUFFER_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_avr_pcm_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= AVR_LATENCY_PCM_PROPERTY;
|
||
|
|
latency_ms = AVR_LATENCY_PCM;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_avr_dd_ddp_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= AVR_LATENCY_PROPERTY;
|
||
|
|
latency_ms = AVR_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int hdmi_get_avr_raw_pcm_latency()
|
||
|
|
{
|
||
|
|
int latency_ms = 0;
|
||
|
|
char *prop_name = NULL;
|
||
|
|
prop_name= AVR_RAW_PCM_LATENCY_PROPERTY;
|
||
|
|
latency_ms = AVR_RAW_PCM_LATENCY;
|
||
|
|
if (prop_name) {
|
||
|
|
latency_ms = aml_audio_property_get_int(prop_name, latency_ms);
|
||
|
|
}
|
||
|
|
ALOGV("%s latency ms =%d", __func__, latency_ms);
|
||
|
|
return latency_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
int aml_dev_sample_audio_path_latency(struct aml_audio_patch *patch, char *latency_details)
|
||
|
|
{
|
||
|
|
struct aml_audio_device *aml_dev;
|
||
|
|
int rbuf_ltcy = 0, spk_tuning_ltcy = 0, ms12_ltcy = 0, alsa_in_ltcy = 0, submix_in_ltcy = 0;
|
||
|
|
int alsa_out_i2s_ltcy = 0, alsa_out_spdif_ltcy = 0, alsa_output_latency = 0;
|
||
|
|
int whole_path_ltcy = 0, in_path_ltcy = 0, out_path_ltcy = 0, cap_ltcy = 0;
|
||
|
|
uint64_t frames = 0;
|
||
|
|
int alsa_frames = 0;
|
||
|
|
int frame_size = 0;
|
||
|
|
int rbuf_avail = 0;
|
||
|
|
|
||
|
|
if (!patch) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
aml_dev = (struct aml_audio_device *)patch->dev;
|
||
|
|
|
||
|
|
frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT);
|
||
|
|
rbuf_avail = get_buffer_read_space(&patch->aml_ringbuffer);
|
||
|
|
if (rbuf_avail == -1) {
|
||
|
|
AM_LOGE("get_buffer_read_space err, need check!!");
|
||
|
|
}
|
||
|
|
rbuf_ltcy = calc_frame_to_latency(rbuf_avail / frame_size, patch->aformat);
|
||
|
|
patch->audio_latency.ringbuffer_latency = rbuf_ltcy;
|
||
|
|
ALOGV(" audio ringbuf latency = %d", rbuf_ltcy);
|
||
|
|
|
||
|
|
if (aml_dev->useSubMix && patch->stream_out) {
|
||
|
|
struct aml_stream_out *out = (struct aml_stream_out *)patch->stream_out;
|
||
|
|
submix_in_ltcy = mixer_get_inport_latency_frames(aml_dev->audio_mixer, out->inputPortID) / SAMPLE_RATE_MS;
|
||
|
|
patch->audio_latency.submix_in_latency = submix_in_ltcy;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aml_dev->spk_tuning_lvl) {
|
||
|
|
rbuf_avail = get_buffer_read_space(&aml_dev->spk_tuning_rbuf);
|
||
|
|
if (rbuf_avail == -1) {
|
||
|
|
AM_LOGE("get_buffer_read_space err, need check!!");
|
||
|
|
}
|
||
|
|
spk_tuning_ltcy = rbuf_avail / frame_size / SAMPLE_RATE_MS;
|
||
|
|
patch->audio_latency.user_tune_latency = spk_tuning_ltcy;
|
||
|
|
ALOGV(" audio spk tuning latency = %d", spk_tuning_ltcy);
|
||
|
|
} else {
|
||
|
|
patch->audio_latency.user_tune_latency = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((eDolbyMS12Lib == aml_dev->dolby_lib_type) &&
|
||
|
|
aml_dev->ms12.dolby_ms12_enable) {
|
||
|
|
|
||
|
|
audio_format_t format = patch->aformat;
|
||
|
|
int ms12_latency_decoder = hdmi_get_ms12_decoder_latency();
|
||
|
|
int ms12_latency_pipeline = hdmi_get_ms12_pipeline_latency();
|
||
|
|
int ms12_latency_dap = hdmi_get_ms12_dap_latency();
|
||
|
|
int ms12_latency_encoder = hdmi_get_ms12_encoder_latency();
|
||
|
|
int ms12_dd_ddp_buffer = hdmi_get_ms12_dd_ddp_buffer_latency();
|
||
|
|
int ms12_mat_buffer = hdmi_get_ms12_mat_buffer_latency();
|
||
|
|
|
||
|
|
ms12_ltcy += ms12_latency_pipeline;
|
||
|
|
|
||
|
|
if (!audio_is_linear_pcm(format)) {
|
||
|
|
if ((format == AUDIO_FORMAT_AC3) || (format == AUDIO_FORMAT_E_AC3))
|
||
|
|
ms12_ltcy += ms12_latency_decoder + ms12_dd_ddp_buffer;
|
||
|
|
else if ((format == AUDIO_FORMAT_MAT) || (format == AUDIO_FORMAT_DOLBY_TRUEHD))
|
||
|
|
ms12_ltcy += ms12_latency_decoder + ms12_mat_buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aml_dev->sink_format == AUDIO_FORMAT_PCM_16_BIT) {
|
||
|
|
ms12_ltcy += ms12_latency_dap;
|
||
|
|
} else if ((aml_dev->optical_format == AUDIO_FORMAT_AC3) ||
|
||
|
|
(aml_dev->optical_format == AUDIO_FORMAT_E_AC3)) {
|
||
|
|
ms12_ltcy += ms12_latency_encoder;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* if arc is connected and format setting is "Passthrough", bypass MS12 */
|
||
|
|
if (is_arc_connected(aml_dev) == 1 && aml_dev->hdmi_format == BYPASS)
|
||
|
|
ms12_ltcy = 0;
|
||
|
|
|
||
|
|
patch->audio_latency.ms12_latency = ms12_ltcy;
|
||
|
|
ALOGV(" audio ms12 latency = %d, format = %x", ms12_ltcy, format);
|
||
|
|
} else {
|
||
|
|
patch->audio_latency.ms12_latency = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aml_dev->alsa_handle[I2S_DEVICE]) {
|
||
|
|
frames = aml_audio_pcm_out_get_delayframe(aml_dev, false);
|
||
|
|
alsa_out_i2s_ltcy = frames / SAMPLE_RATE_MS;
|
||
|
|
|
||
|
|
patch->audio_latency.alsa_i2s_out_latency = alsa_out_i2s_ltcy;
|
||
|
|
ALOGV(" audio_hw_primary audio i2s latency = %d", alsa_out_i2s_ltcy);
|
||
|
|
} else if (aml_dev->useSubMix) {
|
||
|
|
alsa_out_i2s_ltcy = mixer_get_outport_latency_frames(aml_dev->audio_mixer) / SAMPLE_RATE_MS;
|
||
|
|
patch->audio_latency.alsa_i2s_out_latency = alsa_out_i2s_ltcy;
|
||
|
|
} else {
|
||
|
|
alsa_out_i2s_ltcy = 40;
|
||
|
|
patch->audio_latency.alsa_i2s_out_latency = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aml_dev->alsa_handle[DIGITAL_DEVICE]) {
|
||
|
|
aml_alsa_output_getinfo(aml_dev->alsa_handle[DIGITAL_DEVICE],
|
||
|
|
OUTPUT_INFO_DELAYFRAME,
|
||
|
|
(alsa_output_info_t *)&alsa_out_spdif_ltcy);
|
||
|
|
patch->audio_latency.alsa_spdif_out_latency = alsa_out_spdif_ltcy;
|
||
|
|
ALOGV(" audio spdif latency = %d", alsa_out_spdif_ltcy);
|
||
|
|
} else {
|
||
|
|
alsa_out_spdif_ltcy = 40;
|
||
|
|
patch->audio_latency.alsa_spdif_out_latency = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
alsa_frames = audio_input_get_delay(patch->input_type);
|
||
|
|
if (alsa_frames >= 0) {
|
||
|
|
alsa_in_ltcy = calc_frame_to_latency(alsa_frames, patch->aformat);
|
||
|
|
}
|
||
|
|
patch->audio_latency.alsa_in_latency = alsa_in_ltcy;
|
||
|
|
|
||
|
|
int avr_pcm_ltcy = hdmi_get_avr_pcm_latency();
|
||
|
|
int avr_dd_ddp_ltcy = hdmi_get_avr_dd_ddp_latency();
|
||
|
|
int avr_raw_pcm_ltcy = hdmi_get_avr_raw_pcm_latency();
|
||
|
|
|
||
|
|
if (aml_dev->sink_format == AUDIO_FORMAT_PCM_16_BIT) {
|
||
|
|
out_path_ltcy = alsa_out_i2s_ltcy + spk_tuning_ltcy;
|
||
|
|
alsa_output_latency = alsa_out_i2s_ltcy;
|
||
|
|
/* In AVR case, for PCM only AVR or UI setting to PCM */
|
||
|
|
/* add more 10ms AVR latency */
|
||
|
|
if (aml_dev->active_outport == OUTPORT_HDMI_ARC) {
|
||
|
|
alsa_output_latency += avr_pcm_ltcy;
|
||
|
|
}
|
||
|
|
} else if (aml_dev->sink_format == AUDIO_FORMAT_AC3 ||
|
||
|
|
aml_dev->sink_format == AUDIO_FORMAT_E_AC3 ||
|
||
|
|
aml_dev->sink_format == AUDIO_FORMAT_MAT) {
|
||
|
|
if ((aml_dev->optical_format == AUDIO_FORMAT_AC3) ||
|
||
|
|
(aml_dev->optical_format == AUDIO_FORMAT_E_AC3)) {
|
||
|
|
/* For dd/ddp output of AVR, add more 80ms latency */
|
||
|
|
out_path_ltcy = alsa_out_spdif_ltcy + avr_dd_ddp_ltcy;
|
||
|
|
alsa_output_latency = alsa_out_spdif_ltcy;
|
||
|
|
} else if (aml_dev->optical_format == AUDIO_FORMAT_MAT) {
|
||
|
|
/* For mat output of AVR, add more 20ms latency */
|
||
|
|
out_path_ltcy = alsa_out_spdif_ltcy + avr_raw_pcm_ltcy;
|
||
|
|
alsa_output_latency = alsa_out_spdif_ltcy;
|
||
|
|
} else {
|
||
|
|
out_path_ltcy = alsa_out_spdif_ltcy;
|
||
|
|
alsa_output_latency = alsa_out_spdif_ltcy;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// if audio_delay feature enable, add port delay to avsync
|
||
|
|
if (aml_dev->max_audio_delay) {
|
||
|
|
int audio_port_delay = 0;
|
||
|
|
switch (aml_dev->active_outport) {
|
||
|
|
case OUTPORT_HDMI_ARC:
|
||
|
|
case OUTPORT_HDMI:
|
||
|
|
case OUTPORT_SPDIF:
|
||
|
|
audio_port_delay = aml_audio_get_port_delay("hdmi");//SPDIF/SPDIF_RAW/SPDIF_B_RAW's delay always is same.
|
||
|
|
break;
|
||
|
|
case OUTPORT_SPEAKER:
|
||
|
|
audio_port_delay = aml_audio_get_port_delay("speaker");
|
||
|
|
break;
|
||
|
|
case OUTPORT_HEADPHONE:
|
||
|
|
audio_port_delay = aml_audio_get_port_delay("headphone");
|
||
|
|
break;
|
||
|
|
default :
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
out_path_ltcy += audio_port_delay;
|
||
|
|
AM_LOGI_IF(aml_dev->debug_flag, "active_outport:%d audio_delay:%d", aml_dev->active_outport, audio_port_delay);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aml_dev->effect_ctrl.vx_enable) {
|
||
|
|
out_path_ltcy += VIRTUAL_X_LATENCY;
|
||
|
|
AM_LOGI(" VX enable. Set default latency:%d", VIRTUAL_X_LATENCY);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef NO_AUDIO_CAP
|
||
|
|
/*coverity[missing_lock]: adev->cap_buffer does not need to be lock*/
|
||
|
|
if (aml_dev->capture_handle) {
|
||
|
|
cap_ltcy = aml_dev->cap_delay;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* calc whole path latency considering with format */
|
||
|
|
in_path_ltcy = alsa_in_ltcy + rbuf_ltcy + ms12_ltcy;
|
||
|
|
out_path_ltcy += submix_in_ltcy;
|
||
|
|
whole_path_ltcy = in_path_ltcy + out_path_ltcy + cap_ltcy;
|
||
|
|
|
||
|
|
AM_LOGI_IF(aml_dev->debug_flag, "total: %d alsa in:%d rbuf:%d ms12:%d submix in:%d cap:%d",
|
||
|
|
whole_path_ltcy, alsa_in_ltcy, rbuf_ltcy, ms12_ltcy, submix_in_ltcy, cap_ltcy);
|
||
|
|
|
||
|
|
if (latency_details) {
|
||
|
|
sprintf(latency_details, "alsa in:%d rbuf:%d ms12:%d alsa out:%d "
|
||
|
|
"speak tuning rbuf:%d",
|
||
|
|
alsa_in_ltcy, rbuf_ltcy, ms12_ltcy, alsa_output_latency,
|
||
|
|
spk_tuning_ltcy);
|
||
|
|
}
|
||
|
|
patch->audio_latency.total_latency = whole_path_ltcy;
|
||
|
|
return whole_path_ltcy;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aml_dev_sample_video_path_latency(struct aml_audio_patch *patch)
|
||
|
|
{
|
||
|
|
struct aml_audio_device *aml_dev;
|
||
|
|
int vltcy = 0;
|
||
|
|
aml_dev = (struct aml_audio_device *)patch->dev;
|
||
|
|
|
||
|
|
patch->max_video_latency = get_tvin_max_delay(&aml_dev->alsa_mixer);
|
||
|
|
if (patch->max_video_latency <= 0) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
patch->min_video_latency = get_tvin_min_delay(&aml_dev->alsa_mixer);
|
||
|
|
if (patch->min_video_latency <= 0) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
vltcy = get_tvin_delay(&aml_dev->alsa_mixer);
|
||
|
|
|
||
|
|
return vltcy;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int calc_diff(int altcy, int vltcy)
|
||
|
|
{
|
||
|
|
return altcy - vltcy;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aml_dev_avsync_diff_in_path(struct aml_audio_patch *patch, int *Vltcy, int *Altcy, char *latency_details)
|
||
|
|
{
|
||
|
|
int altcy = 0, vltcy = 0;
|
||
|
|
int src_diff_err = 0;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (!patch || !Vltcy || !Altcy) {
|
||
|
|
ret = -EINVAL;
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
check_skip_frames(patch);
|
||
|
|
|
||
|
|
vltcy = aml_dev_sample_video_path_latency(patch);
|
||
|
|
if (vltcy < 0) {
|
||
|
|
ret = -EINVAL;
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
*Vltcy = vltcy;
|
||
|
|
|
||
|
|
if (patch->input_device == AUDIO_DEVICE_IN_LINE) {
|
||
|
|
src_diff_err = 30;
|
||
|
|
}
|
||
|
|
|
||
|
|
altcy = aml_dev_sample_audio_path_latency(patch, latency_details);
|
||
|
|
*Altcy = altcy + src_diff_err;
|
||
|
|
|
||
|
|
// FIXME: should be careful of overflow of latency_details
|
||
|
|
int len = strlen(latency_details);
|
||
|
|
sprintf(&latency_details[len], " video:%d", vltcy);
|
||
|
|
ALOGV(" altcy: %dms, vltcy: %dms\n\n", altcy, vltcy);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
err:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void aml_dev_accumulate_avsync_diff(struct aml_audio_patch *patch, int vltcy, int altcy)
|
||
|
|
{
|
||
|
|
patch->vltcy += vltcy;
|
||
|
|
patch->altcy += altcy;
|
||
|
|
patch->avsync_sample_accumulated++;
|
||
|
|
patch->average_vltcy = patch->vltcy / patch->avsync_sample_accumulated;
|
||
|
|
patch->average_altcy = patch->altcy / patch->avsync_sample_accumulated;
|
||
|
|
|
||
|
|
AM_LOGI("latency status[%d]: average average_vltcy = %dms, average_altcy = %dms",
|
||
|
|
patch->avsync_sample_accumulated, patch->average_vltcy, patch->average_altcy);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aml_dev_tune_video_path_latency(struct aml_mixer_handle *mixer_handle, unsigned int video_val)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = aml_mixer_ctrl_set_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_DELAY, video_val);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void aml_dev_avsync_reset(struct aml_audio_patch *patch)
|
||
|
|
{
|
||
|
|
patch->avsync_sample_accumulated = 0;
|
||
|
|
patch->vltcy = 0;
|
||
|
|
patch->average_vltcy = 0;
|
||
|
|
audio_patch_set_av_sync_status(patch, false);
|
||
|
|
patch->is_avsync_start = false;
|
||
|
|
patch->timeout_avsync_cnt = 0;
|
||
|
|
patch->altcy = 0;
|
||
|
|
patch->average_altcy = 0;
|
||
|
|
patch->max_video_latency = 0;
|
||
|
|
patch->min_video_latency = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aml_dev_try_avsync(struct aml_audio_patch *patch)
|
||
|
|
{
|
||
|
|
int vltcy = 0, altcy = 0;
|
||
|
|
struct aml_audio_device *aml_dev;
|
||
|
|
int ret = 0;
|
||
|
|
if (!patch) {
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
aml_dev = (struct aml_audio_device *)patch->dev;
|
||
|
|
|
||
|
|
char latency_details[256] = {0};
|
||
|
|
ret = aml_dev_avsync_diff_in_path(patch, &vltcy, &altcy, latency_details);
|
||
|
|
if (ret < 0) {
|
||
|
|
/* timeout to wait video stability, don't do avsync */
|
||
|
|
patch->timeout_avsync_cnt++;
|
||
|
|
usleep(10*1000);
|
||
|
|
if (patch->timeout_avsync_cnt > AVSYNC_TIMEOUT_CNT) {
|
||
|
|
aml_dev_avsync_reset(patch);
|
||
|
|
ALOGI(" timeout to tune avsync! error status = %d", ret);
|
||
|
|
}
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (patch->is_avsync_start == false) {
|
||
|
|
patch->is_avsync_start = true;
|
||
|
|
/*flush the alsa input buffer*/
|
||
|
|
audio_input_flush(patch->input_type);
|
||
|
|
ALOGV(" /* start calc the average latency of audio & video */");
|
||
|
|
}
|
||
|
|
|
||
|
|
aml_dev_accumulate_avsync_diff(patch, vltcy, altcy);
|
||
|
|
|
||
|
|
int tune_val = patch->average_altcy;
|
||
|
|
int user_tune_val = 0; //aml_audio_get_src_tune_latency(aml_dev->patch_src);
|
||
|
|
int seek_duration_ret = 0;
|
||
|
|
int seek_duration = tune_val;
|
||
|
|
int avDiff = 0;
|
||
|
|
//int sm_tune_val = 0;
|
||
|
|
|
||
|
|
if (patch->avsync_sample_accumulated >= AVSYNC_SAMPLE_MAX_CNT) {
|
||
|
|
tune_val += user_tune_val;
|
||
|
|
|
||
|
|
avDiff = calc_diff(tune_val, patch->min_video_latency);
|
||
|
|
|
||
|
|
ALOGD("--vmax latency = [%dms], vmin latency = [%dms], vltcy = [%dms], altcy = [%dms], altcy - vmin = [%dms]",
|
||
|
|
patch->max_video_latency, patch->min_video_latency, patch->average_vltcy, tune_val, avDiff);
|
||
|
|
|
||
|
|
/* if min video latency is larger than audio latency, seek audio buffer to enlarge the audio delay */
|
||
|
|
if (avDiff < 0) {
|
||
|
|
seek_duration_ret = ringbuffer_seek(patch, avDiff);
|
||
|
|
} else if (patch->audio_latency.ringbuffer_latency > AVSYNC_RINGBUFFER_MIN_LATENCY) {
|
||
|
|
/* if it need reduce audio latency, first do ringbuffer seek */
|
||
|
|
int valid_tune_space = patch->audio_latency.ringbuffer_latency - AVSYNC_RINGBUFFER_MIN_LATENCY;
|
||
|
|
seek_duration = (avDiff < valid_tune_space) ? avDiff : valid_tune_space;
|
||
|
|
seek_duration_ret = ringbuffer_seek(patch, seek_duration);
|
||
|
|
}
|
||
|
|
|
||
|
|
tune_val -= seek_duration_ret;
|
||
|
|
#if 0
|
||
|
|
/* for submix ringbuffer tuning */
|
||
|
|
if (aml_dev->useSubMix) {
|
||
|
|
sm_tune_val = avDiff - seek_duration_ret;
|
||
|
|
if (sm_tune_val < 0) {
|
||
|
|
seek_duration_ret = submix_ringbuffer_seek(patch, sm_tune_val);
|
||
|
|
} else if (patch->audio_latency.smringbuffer_latency > AVSYNC_RINGBUFFER_MIN_LATENCY) {
|
||
|
|
/* if it need reduce audio latency, first do submix ringbuffer seek*/
|
||
|
|
int valid_tune_space = patch->audio_latency.smringbuffer_latency - AVSYNC_RINGBUFFER_MIN_LATENCY;
|
||
|
|
seek_duration = (sm_tune_val < valid_tune_space) ? sm_tune_val : valid_tune_space;
|
||
|
|
seek_duration_ret = submix_ringbuffer_seek(patch, seek_duration);
|
||
|
|
AM_LOGD("seek_duration_ret:%d, tune_val:%d", seek_duration_ret, tune_val);
|
||
|
|
}
|
||
|
|
tune_val -= seek_duration_ret;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
vltcy = aml_dev_sample_video_path_latency(patch);
|
||
|
|
altcy = aml_dev_sample_audio_path_latency(patch, latency_details);
|
||
|
|
|
||
|
|
if (tune_val > patch->max_video_latency) {
|
||
|
|
tune_val = patch->max_video_latency;
|
||
|
|
} else if (tune_val < patch->min_video_latency) {
|
||
|
|
tune_val = patch->min_video_latency;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = aml_dev_tune_video_path_latency(&aml_dev->alsa_mixer, tune_val);
|
||
|
|
ALOGD("--start avsync, tuning video total latency: value [%dms], real vltcy [%dms], real altcy [%dms]", tune_val, vltcy, altcy);
|
||
|
|
aml_dev_avsync_reset(patch);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|