Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/audio_hal/audio_hw.c
T
2026-08-24 10:03:24 +08:00

9335 lines
369 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 <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 "aml_audio_log.h"
#include <cutils/atomic.h>
#include <cutils/str_parms.h>
#include <linux/ioctl.h>
#include <hardware/hardware.h>
#include <system/audio.h>
#include <audio_utils/channels.h>
#include <memory.h>
#include <unistd.h>
#include "aml_dec_api.h"
#include "Virtualx.h"
#if ANDROID_PLATFORM_SDK_VERSION >= 25 //8.0
#include <system/audio-base.h>
#endif
#include <hardware/audio.h>
#include <audio_route/audio_route.h>
#include <aml_alsa_mixer.h>
#ifndef NO_AUDIO_CAP
#include <IpcBuffer/IpcBuffer_c.h>
#endif
#include "aml_capture_output.h"
#include "tv_patch_format_parser.h"
#include "SPDIFEncoderAD.h"
#include "aml_volume_utils.h"
#include "aml_data_utils.h"
#include "alsa_manager.h"
#include "aml_audio_stream.h"
#include "audio_hw.h"
#include "spdif_encoder_api.h"
#include "audio_hw_utils.h"
#include "audio_hw_profile.h"
#include "aml_dump_debug.h"
#include "alsa_manager.h"
#include "alsa_device_parser.h"
#include "aml_audio_stream.h"
#include "alsa_config_parameters.h"
#include "spdif_encoder_api.h"
#include "tv_patch_avsync.h"
#include "aml_ng.h"
#include "aml_audio_timer.h"
#include "aml_audio_ease.h"
#include "aml_audio_spdifout.h"
#include "aml_config_parser.h"
#include "audio_effect_if.h"
#include "aml_audio_nonms12_render.h"
#include "aml_audio_uevent.h"
#include "external_output_manager.h"
#include "external_output_handle.h"
#include "audio_mpegh.h"
#include "aml_audio_command_worker.h"
#include "aml_audio_ai_config.h"
//#ifdef ENABLE_MMAP
#ifndef BUILD_LINUX
#include "aml_mmap_audio.h"
#endif
// for invoke bluetooth rc hal
#ifndef BUILD_LINUX
#include "audio_hal_thunks.h"
#endif
#include "earc_utils.h"
#include <SPDIFEncoderAD.h>
#include "audio_hw_ms12_common.h"
#include "aml_audio_ms12_sync.h"
#include "tv_patch_ctrl.h"
#include "tv_patch.h"
#include "hdmirx_utils.h"
#include "dmx_audio_es.h"
#include "aml_audio_ms12_render.h"
#include "aml_audio_nonms12_render.h"
#include "aml_config_data.h"
#include "aml_audio_spdifdec.h"
#include "audio_hw_resource_mgr.h"
#include "device_patch.h"
#include "aml_noise_gate.h"
#include "tv_private_object.h"
#include "hdmirx_utils.h"
#include "dtv_private_object.h"
#include "aml_audio_output.h"
#include "audio_ms12_continuous_standby.h"
#include "audio_parser.h"
#include "aml_audio_parser.h"
#include "audio_mpegh.h"
#include "format.h"
#include "aml_audio_limiter.h"
#include "aml_policy.h"
#ifdef USE_MEDIAINFO
#include "aml_media_info.h"
#endif
// for dtv playback
#include "dtv_patch.h"
/*Google Voice Assistant channel_mask */
#define BUILT_IN_MIC 12
//#define SUBMIXER_V1_1
#define HDMI_LATENCY_MS 60
#include "amlAudioMixer.h"
#include "aml_malloc_debug.h"
#include <audio_effects/effect_aec.h>
#include <audio_utils/clock.h>
#include "audio_hal_version.h"
#include "audio_hw_ms12_v2.h"
#include "aml_audio_scaletempo.h"
#include "audio_mediasync.h"
#include "karaoke_manager.h"
#include "aml_audio_property.h"
#include "aml_hwmixer_manager.h"
#include "deep_buffer_unit.h"
#include "aml_dts_chmask_table.h"
#include "audio_llp.h"
#include "mixer_out_unit.h"
#include "deep_buffer_unit.h"
#ifdef L_PROJECT
#include "audio_controls.h"
#endif
#undef PLAYBACK_PERIOD_COUNT
#define PLAYBACK_PERIOD_COUNT 4
/* number of periods for capture */
#undef CAPTURE_PERIOD_COUNT
#define CAPTURE_PERIOD_COUNT 4
/*Google Voice Assistant channel_mask */
#define BUILT_IN_MIC 12
/* minimum sleep time in out_write() when write threshold is not reached */
#define MIN_WRITE_SLEEP_US 5000
#undef RESAMPLER_BUFFER_FRAMES
#define RESAMPLER_BUFFER_FRAMES (PERIOD_SIZE * 6)
#define RESAMPLER_BUFFER_SIZE (4 * RESAMPLER_BUFFER_FRAMES)
#define NSEC_PER_SECOND 1000000000ULL
/* sampling rate when using MM low power port */
#define MM_LOW_POWER_SAMPLING_RATE 44100
/* sampling rate when using MM full power port */
#define MM_FULL_POWER_SAMPLING_RATE 48000
/* sampling rate when using VX port for narrow band */
#define AEC_CAP_SAMPLING_RATE 16000
#define DOLBY_MS12_INPUT_FORMAT_TEST
#define IEC61937_PACKET_SIZE_OF_AC3 (0x1800)
#define IEC61937_PACKET_SIZE_OF_EAC3 (0x6000)
#define MAX_INPUT_STREAM_CNT (3)
#define AML_AEC_MIC_CHANNEL_NUM (2)
#define NETFLIX_DDP_BUFSIZE (768)
#define MAX_SPK_EXTRA_LATENCY_MS (100)
#define DEFAULT_SPK_EXTRA_LATENCY_MS (15)
#define SPDIF_RBUF_DATA_STORAGE_MS (256)
#define AML_LIMITER_PROC_FRAME (128)
/*Tunnel sync HEADER is 20 bytes*/
#define TUNNEL_SYNC_HEADER_SIZE (20)
#define DISABLE_CONTINUOUS_OUTPUT "persist.vendor.audio.continuous.disable"
static const struct pcm_config pcm_config_out = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
.period_size = DEFAULT_PLAYBACK_PERIOD_SIZE,
.period_count = DEFAULT_PLAYBACK_PERIOD_CNT,
.format = PCM_FORMAT_S16_LE,
};
static const struct pcm_config pcm_config_out_direct = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
.period_size = DEFAULT_PLAYBACK_PERIOD_SIZE,
.period_count = PLAYBACK_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
static const struct pcm_config pcm_config_aec = {
.channels = 4,
.rate = AEC_CAP_SAMPLING_RATE,
.period_size = DEFAULT_CAPTURE_PERIOD_SIZE,
.period_count = CAPTURE_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
.start_threshold = 0,
.stop_threshold = 0,
.avail_min = 0,
};
static int adev_set_voice_volume (struct audio_hw_device *dev, float volume);
//static int do_output_standby_l (struct audio_stream *out);
static uint32_t out_get_sample_rate (const struct audio_stream *stream);
static int stream_type_validate_l (struct aml_stream_out *aml_out, uint32_t flags);
static inline bool need_hw_mix(usecase_mask_t masks);
//static int out_standby_new(struct audio_stream *stream);
static int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle __unused,
audio_devices_t devices,
audio_output_flags_t flags,
struct audio_config *config,
struct audio_stream_out **stream_out,
const char *address __unused);
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream);
static int set_hdmi_format(struct aml_audio_device *adev, int val);
static ssize_t out_write_new(struct audio_stream_out *stream,
const void *buffer,
size_t bytes);
static int out_get_presentation_position (const struct audio_stream_out *stream, uint64_t *frames, struct timespec *timestamp);
static int out_standby_new(struct audio_stream *stream);
static int adev_get_mic_mute(const struct audio_hw_device* dev, bool* state);
static int adev_get_microphones(const struct audio_hw_device* dev,
struct audio_microphone_characteristic_t* mic_array,
size_t* mic_count);
static void get_mic_characteristics(struct audio_microphone_characteristic_t* mic_data,
size_t* mic_count);
static int aml_audio_focus(struct aml_audio_device *adev, bool on);
static int adev_get_dvb_standards();
static int adev_remove_device_effect(struct audio_hw_device *dev, audio_port_handle_t device, effect_handle_t effect);
static void set_vol_ease_by_stream (struct aml_stream_out *aml_out, int gain, int duration, int shape);
static inline bool need_hw_mix(usecase_mask_t masks)
{
return (masks > 1);
}
static inline short CLIP (int r)
{
return (r > 0x7fff) ? 0x7fff :
(r < -0x8000) ? -0x8000 :
r;
}
static void select_mode (struct aml_audio_device *adev)
{
ALOGD ("%s(out_device=%#x)", __FUNCTION__, adev->out_device);
ALOGD ("%s(in_device=%#x)", __FUNCTION__, adev->in_device);
return;
/* force earpiece route for in call state if speaker is the
only currently selected route. This prevents having to tear
down the modem PCMs to change route from speaker to earpiece
after the ringtone is played, but doesn't cause a route
change if a headset or bt device is already connected. If
speaker is not the only thing active, just remove it from
the route. We'll assume it'll never be used initally during
a call. This works because we're sure that the audio policy
manager will update the output device after the audio mode
change, even if the device selection did not change. */
/*coverity[UNREACHABLE]*/
if ( (adev->out_device & AUDIO_DEVICE_OUT_ALL) == AUDIO_DEVICE_OUT_SPEAKER) {
adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN;
} else {
adev->out_device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
return;
}
static int check_input_parameters(uint32_t sample_rate, audio_format_t format, int channel_count, audio_devices_t devices)
{
ALOGD("%s(sample_rate=%d, format=%d, channel_count=%d, devices = %x)", __FUNCTION__, sample_rate, format, channel_count, devices);
if(AUDIO_DEVICE_IN_DEFAULT == devices && AUDIO_CHANNEL_NONE == channel_count &&
AUDIO_FORMAT_DEFAULT == format && 0 == sample_rate) {
/* Add for Hidl6.0:CloseDeviceWithOpenedInputStreams test */
return -ENOSYS; /*Currently System Not Supported.*/
}
if (format != AUDIO_FORMAT_PCM_16_BIT && format != AUDIO_FORMAT_PCM_32_BIT) {
ALOGE("%s: unsupported AUDIO FORMAT (%d)", __func__, format);
return -EINVAL;
}
if (channel_count < 1 || channel_count > 4 || channel_count == 3) {
ALOGE("%s: unsupported channel count (%d) passed Min / Max (1 / 2)", __func__, channel_count);
return -EINVAL;
}
switch (sample_rate) {
case 8000:
case 11025:
/*fallthrough*/
case 12000:
case 16000:
case 17000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
case 96000:
break;
default:
ALOGE("%s: unsupported (%d) samplerate passed ", __func__, sample_rate);
return -EINVAL;
}
devices &= ~AUDIO_DEVICE_BIT_IN;
if ((devices & AUDIO_DEVICE_IN_LINE) ||
(devices & AUDIO_DEVICE_IN_SPDIF) ||
(devices & AUDIO_DEVICE_IN_TV_TUNER) ||
(devices & AUDIO_DEVICE_IN_HDMI) ||
(devices & AUDIO_DEVICE_IN_HDMI1) ||
(devices & AUDIO_DEVICE_IN_HDMI_ARC)) {
if ((format == AUDIO_FORMAT_PCM_16_BIT || format == AUDIO_FORMAT_PCM_32_BIT) &&
channel_count == 2 &&
sample_rate == 48000) {
ALOGD("%s: audio patch input device %x", __FUNCTION__, devices);
return 0;
} else {
ALOGE("%s: unspported audio patch input device %x", __FUNCTION__, devices);
return -EINVAL;
}
}
return 0;
}
static size_t get_input_buffer_size(unsigned int period_size, uint32_t sample_rate, audio_format_t format, int channel_count)
{
size_t size;
ALOGD("%s(sample_rate=%d, format=%d, channel_count=%d)", __FUNCTION__, sample_rate, format, channel_count);
/*
if (check_input_parameters(sample_rate, format, channel_count, AUDIO_DEVICE_NONE) != 0) {
return 0;
}
*/
/* take resampling into account and return the closest majoring
multiple of 16 frames, as audioflinger expects audio buffers to
be a multiple of 16 frames */
if (period_size == 0)
period_size = (DEFAULT_CAPTURE_PERIOD_SIZE * sample_rate) / MM_FULL_POWER_SAMPLING_RATE;
size = (period_size + 15) / 16 * 16;
if (format == AUDIO_FORMAT_PCM_32_BIT)
return size * channel_count * sizeof(int32_t);
else
return size * channel_count * sizeof(int16_t);
}
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
const struct aml_stream_out *out = (const struct aml_stream_out *) stream;
unsigned int rate = out->hal_rate;
ALOGV("Amlogic_HAL - out_get_sample_rate() = %d", rate);
return rate;
}
static int out_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
{
return 0;
}
static void unmute_after_eos_received(struct aml_stream_out *aml_out) {
struct aml_audio_device *aml_dev = aml_out->dev;
if (true == aml_out->with_header && true == aml_out->eos) {
audio_header_info_reset(aml_out->pheader);
aml_out->eos = false;
if (aml_dev->dolby_lib_type != eDolbyMS12Lib) {
set_aed_master_volume_mute(&aml_dev->alsa_mixer, false);
} else {
//unmute
ease_setting_t vol_setting;
vol_setting.duration = 32;
vol_setting.start_volume = aml_out->volume_l;
vol_setting.target_volume = aml_out->volume_l;
vol_setting.ease_shape = EaseLinear;
aml_audio_ease_config(aml_out->vol_process_handle, &vol_setting);
}
}
AM_LOGI("unmute after eos received done");
}
static uint32_t out_get_latency (const struct audio_stream_out *stream)
{
const struct aml_stream_out *out = (const struct aml_stream_out *) stream;
const struct aml_audio_device *adev = out->dev;
int buffer_frames = 0;
int ret = 0;
if (eDolbyMS12Lib == adev->dolby_lib_type && out->is_normal_pcm) {
ret = dolby_ms12_get_system_buffer_frame(&buffer_frames);
if (ret < 0) {
AM_LOGE("get available system buffer error!");
}
} else {
ret = out_get_latency_frames (stream) * audio_stream_out_frame_size(stream);
}
return buffer_frames * 4;
}
static size_t out_get_buffer_size (const struct audio_stream *stream)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
ALOGI("%s(out->config.rate=%d, format %x,stream format %x)", __FUNCTION__,
out->config.rate, out->hal_internal_format, stream->get_format(stream));
/* take resampling into account and return the closest majoring
* multiple of 16 frames, as audioflinger expects audio buffers to
* be a multiple of 16 frames
*/
int size = out->config.period_size;
int ret = 0;
switch (out->hal_internal_format) {
case AUDIO_FORMAT_AC3:
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = AC3_PERIOD_SIZE;
ALOGI("%s AUDIO_FORMAT_IEC61937 %d)", __FUNCTION__, size);
if ((eDolbyDcvLib == adev->dolby_lib_type) &&
(out->flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// local file playback, data from audio flinger direct mode
// the data is packed by DCV decoder by OMX, 1536 samples per packet
// to match with it, set the size to 1536
// (ms12 decoder doesn't encounter this issue, so only handle with DCV decoder case)
size = AC3_PERIOD_SIZE / 4;
ALOGI("%s AUDIO_FORMAT_IEC61937(DIRECT) (eDolbyDcvLib) size = %d)", __FUNCTION__, size);
}
} else if (out->flags & AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO) {
size = AC3_PERIOD_SIZE;
} else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
size = (DEFAULT_PLAYBACK_PERIOD_SIZE << 1);
} else {
size = DEFAULT_PLAYBACK_PERIOD_SIZE;
}
break;
case AUDIO_FORMAT_E_AC3:
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = EAC3_PERIOD_SIZE;
} else if (out->flags & AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO) {
size = EAC3_PERIOD_SIZE;//one iec61937 packet size
} else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
size = (DEFAULT_PLAYBACK_PERIOD_SIZE << 3) + (DEFAULT_PLAYBACK_PERIOD_SIZE << 1);
} else {
/*frame align*/
if (1 /* adev->continuous_audio_mode */) {
/*Tunnel sync HEADER is 16 bytes*/
if ((out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) && out->with_header) {
size = out->ddp_frame_size + 20;
} else {
size = out->ddp_frame_size * 4;
}
} else {
size = PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE; //PERIOD_SIZE;
}
}
if (eDolbyDcvLib == adev->dolby_lib_type_last) {
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE;
ALOGI("%s eac3 eDolbyDcvLib = size%d)", __FUNCTION__, size);
}
}
/*netflix ddp size is 768, if we change to a big value, then
*every process time is too long, it will cause such case failed SWPL-41439
*/
if (adev->is_netflix) {
if (out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) {
size = NETFLIX_DDP_BUFSIZE + 20;
} else {
size = NETFLIX_DDP_BUFSIZE;
}
}
break;
case AUDIO_FORMAT_AC4:
/*
*1.offload write interval is 10ms
*2.AC4 datarate: ac4 frame size=16391 frame rate =23440 sample rate=48000
* 16391Bytes/42.6ms
*3.set the audio hal buffer size as 8192 Bytes, it is about 24ms
*/
size = (DEFAULT_PLAYBACK_PERIOD_SIZE << 4);
break;
case AUDIO_FORMAT_DOLBY_TRUEHD:
if (out->flags & AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO) {
size = 16 * DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT;
} else {
size = PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE;
}
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = 4 * PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE;
}
break;
case AUDIO_FORMAT_DTS:
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = DTS1_PERIOD_SIZE / 2;
} else {
size = DTS_OFFLOAD_BUFFER_MAX_SIZE;
}
ALOGI("%s AUDIO_FORMAT_DTS buffer size = %d frames", __FUNCTION__, size);
break;
case AUDIO_FORMAT_DTS_HD:
if (stream->get_format(stream) == AUDIO_FORMAT_IEC61937) {
size = 4 * PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE;
} else {
size = DTS_OFFLOAD_BUFFER_MAX_SIZE;
}
ALOGI("%s AUDIO_FORMAT_DTS_HD buffer size = %d frames", __FUNCTION__, size);
break;
#if 0
case AUDIO_FORMAT_PCM:
if (adev->continuous_audio_mode) {
/*Tunnel sync HEADER is 16 bytes*/
if (out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) {
size = (8192 + 16) / 4;
}
}
#endif
default:
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if (out->is_normal_pcm) {
if (adev->dev_ms12_version == eDolbyMS12_V282) {
ret = dolby_ms12_get_system_buffer_avail(NULL) * audio_stream_out_frame_size((struct audio_stream_out *)stream);;
} else {
ret = dolby_ms12_get_system_buffer_avail(&size);
}
if (ret < 0) {
AM_LOGE("get available system buffer error!");
}
}
size += 5 * 48; //system buffer extra continuous buffer size
return size;
}
if (out->config.rate == 96000)
size = DEFAULT_PLAYBACK_PERIOD_SIZE * 2;
else
size = DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT;
}
if (out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC && audio_is_linear_pcm(out->hal_internal_format)) {
size = (size * audio_stream_out_frame_size((struct audio_stream_out *) stream)) + TUNNEL_SYNC_HEADER_SIZE;
} else {
size = (size * audio_stream_out_frame_size((struct audio_stream_out *) stream));
}
// remove alignment to have an accurate size
// size = ( (size + 15) / 16) * 16;
return size;
}
static audio_channel_mask_t out_get_channels(const struct audio_stream *stream __unused)
{
const struct aml_stream_out *out = (const struct aml_stream_out *)stream;
//ALOGV("Amlogic_HAL - out_get_channels return out->hal_channel_mask:%0x", out->hal_channel_mask);
return out->hal_channel_mask;
}
static audio_format_t out_get_format(const struct audio_stream *stream __unused)
{
const struct aml_stream_out *out = (const struct aml_stream_out *)stream;
//ALOGV("Amlogic_HAL - out_get_format() = %d", out->hal_format);
// if hal_format doesn't have a valid value,
// return default value AUDIO_FORMAT_PCM_16_BIT
if (out->hal_format == 0) {
return AUDIO_FORMAT_PCM_16_BIT;
}
return out->hal_format;
}
static int out_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
{
return 0;
}
static int out_dump (const struct audio_stream *stream __unused, int fd __unused)
{
ALOGD ("%s(%p, %d)", __FUNCTION__, stream, fd);
return 0;
}
void set_rate_to_sync (struct aml_stream_out *aml_out, float rate)
{
switch (aml_out->avsync_type)
{
case AVSYNC_TYPE_MEDIASYNC:
if ((NULL == aml_out->avsync_ctx) ||
(NULL == aml_out->avsync_ctx->mediasync_ctx) ||
(NULL == aml_out->avsync_ctx->mediasync_ctx->handle)) {
AM_LOGE("mediasync handle NULL fail");
goto exit;
}
mediasync_wrap_setPlaybackRate(aml_out->avsync_ctx->mediasync_ctx->handle, rate);
break;
case AVSYNC_TYPE_MSYNC:
if ((NULL == aml_out->avsync_ctx) ||
(NULL == aml_out->avsync_ctx->msync_ctx) ||
(NULL == aml_out->avsync_ctx->msync_ctx->msync_session)) {
AM_LOGE("msync_session NULL fail");
goto exit;
}
av_sync_set_speed(aml_out->avsync_ctx->msync_ctx->msync_session, rate);
break;
default :
AM_LOGE("aml_out->avsync_type:%d, not support!", aml_out->avsync_type);
break;
}
exit:
return;
}
void out_set_playback_rate (struct audio_stream *stream, float rate)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = aml_out->dev;
if (false == aml_out->enable_scaletempo)
{
AM_LOGI("set rate:%f, enable_scaletempo is 0", rate);
goto done;
}
if (NULL == aml_out->scaletempo) {
hal_scaletempo_init(&aml_out->scaletempo);
}
hal_scaletempo_update_rate(aml_out->scaletempo, rate);
if ((!adev->is_netflix) && aml_out->scaletempo) {
bool st_bypass = is_double_equal((double)rate, 1.0) ? true : false;
hal_scaletempo_set_bypass(aml_out->scaletempo, st_bypass);
}
done:
if (eDolbyMS12Lib == adev->dolby_lib_type && aml_out->rate != 0.0) {
aml_out->scale_state = SCALE_START;
} else {
set_rate_to_sync(aml_out, rate);
}
aml_out->rate = rate;
return;
}
void eos_timer_callback_handler(union sigval sigv)
{
uint32_t timer_id = sigv.sival_int;
struct audio_stream_out *out = audio_timer_get_privdata_by_id(timer_id);
AM_LOGI("sigv:%d, out:%p ~~~~~~~~~~", timer_id, out);
set_ms12_decoder_continuous_mute(out, 1);
return;
}
static int out_set_parameters (struct audio_stream *stream, const char *kvpairs)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
struct aml_stream_in *in;
struct str_parms *parms;
char value[32];
int ret;
uint32_t val = 0;
bool force_input_standby = false;
do_standby_func standby_func = NULL;
standby_func = do_output_standby_l;
ALOGD ("%s(kvpairs(%s), out_device=%#x)", __FUNCTION__, kvpairs, adev->out_device);
parms = str_parms_create_str (kvpairs);
ret = str_parms_get_str (parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof (value) );
if (ret >= 0) {
val = atoi (value);
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
if ( ( (adev->out_device & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0) ) {
ALOGI ("audio hw select device!\n");
standby_func (stream);
/* a change in output device may change the microphone selection */
if (adev->active_input &&
adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
force_input_standby = true;
}
/* force standby if moving to/from HDMI */
if ( ( (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) ^
(adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) ) ||
( (val & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ^
(adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ) ) {
standby_func (stream);
}
adev->out_device &= ~AUDIO_DEVICE_OUT_ALL;
adev->out_device |= val;
//select_devices(adev);
}
pthread_mutex_unlock (&out->lock);
if (force_input_standby) {
in = adev->active_input;
pthread_mutex_lock (&in->lock);
do_input_standby (in);
pthread_mutex_unlock (&in->lock);
}
pthread_mutex_unlock (&adev->lock);
// We shall return Result::OK, which is 0, if parameter is set successfully,
// or we can not pass VTS test.
ALOGI ("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
ret = 0;
goto exit;
}
int sr = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE, &sr);
if (ret >= 0) {
if (sr > 0) {
struct pcm_config *config = &out->config;
ALOGI ("audio hw sampling_rate change from %d to %d \n", config->rate, sr);
config->rate = sr;
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
if (!out->standby) {
//standy_func (out);
//startup_func (out);
out->standby = 0;
}
// set hal_rate to sr for passing VTS
ALOGI ("Amlogic_HAL - %s: set sample_rate to hal_rate.", __FUNCTION__);
out->hal_rate = sr;
pthread_mutex_unlock (&adev->lock);
pthread_mutex_unlock (&out->lock);
}
// We shall return Result::OK, which is 0, if parameter is set successfully,
// or we can not pass VTS test.
ALOGI ("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
ret = 0;
goto exit;
}
// Detect and set AUDIO_PARAMETER_STREAM_FORMAT for passing VTS
audio_format_t fmt = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_FORMAT, (int *) &fmt);
if (ret >= 0) {
if (fmt > 0) {
struct pcm_config *config = &out->config;
ALOGI ("[%s:%d] audio hw format change from %#x to %#x", __func__, __LINE__, config->format, fmt);
config->format = fmt;
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
if (!out->standby) {
standby_func (stream);
out->standby = 0;
}
// set hal_format to fmt for passing VTS
ALOGI ("Amlogic_HAL - %s: set format to hal_format. fmt = %d", __FUNCTION__, fmt);
out->hal_format = fmt;
pthread_mutex_unlock (&adev->lock);
pthread_mutex_unlock (&out->lock);
}
// We shall return Result::OK, which is 0, if parameter is set successfully,
// or we can not pass VTS test.
ALOGI ("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
ret = 0;
goto exit;
}
// Detect and set AUDIO_PARAMETER_STREAM_CHANNELS for passing VTS
audio_channel_mask_t channels = AUDIO_CHANNEL_OUT_STEREO;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_CHANNELS, (int *) &channels);
if (ret >= 0) {
if (channels > AUDIO_CHANNEL_NONE) {
struct pcm_config *config = &out->config;
ALOGI ("audio hw channel_mask change from %d to %d \n", config->channels, channels);
config->channels = audio_channel_count_from_out_mask (channels);
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
if (!out->standby) {
standby_func (stream);
out->standby = 0;
}
// set out->hal_channel_mask to channels for passing VTS
ALOGI ("Amlogic_HAL - %s: set out->hal_channel_mask to channels. fmt = %d", __FUNCTION__, channels);
out->hal_channel_mask = channels;
pthread_mutex_unlock (&adev->lock);
pthread_mutex_unlock (&out->lock);
}
// We shall return Result::OK, which is 0, if parameter is set successfully,
// or we can not pass VTS test.
ALOGI ("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
ret = 0;
goto exit;
}
int input_eos_value = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_INPUT_END_OF_STREAM, &input_eos_value);
if (ret >= 0) {
if (input_eos_value == 0) {
out->eos = false;
} else {
out->eos = true;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
int codecbuf_frames = 0;
codecbuf_frames = get_ms12_codecbuf_frames((struct audio_stream_out *)out);
if (EOS_EASE_DURATION_FRAMES < codecbuf_frames) {
int diffms = (codecbuf_frames - EOS_EASE_DURATION_FRAMES) / 48;
AM_LOGI("eos_ease_timer start, timer_id:%d, out:%p, diffms:%d, codecbuf_frames:%d",
out->eos_ease_timer, out, diffms, codecbuf_frames);
audio_one_shot_timer_start(out->eos_ease_timer, diffms);
} else {
set_ms12_decoder_continuous_mute((struct audio_stream_out *)out, 1);
}
}
}
AM_LOGI (" Amlogic_HAL - input_eos value changed, value: %d ", out->eos);
ret = 0;
goto exit;
}
int frame_size = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_FRAME_COUNT, &frame_size);
if (ret >= 0) {
if (frame_size > 0) {
struct pcm_config *config = &out->config;
ALOGI ("audio hw frame size change from %d to %d \n", config->period_size, frame_size);
config->period_size = frame_size;
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
if (!out->standby) {
standby_func (stream);
out->standby = 0;
}
pthread_mutex_unlock (&adev->lock);
pthread_mutex_unlock (&out->lock);
}
// We shall return Result::OK, which is 0, if parameter is set successfully,
// or we can not pass VTS test.
ALOGI("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
ret = 0;
goto exit;
}
ret = str_parms_get_str (parms, "adpcm_block_size", value, sizeof(value));
if (ret >=0 ) {
unsigned int block_size = atoi(value);
out->dec_param.hal_decode_block_size = block_size;
ALOGI("[%s:%d]:set block_size=%d", __FUNCTION__, __LINE__, block_size);
ret = 0;
goto exit;
}
ret = str_parms_get_str (parms, "hw_av_sync_type", value, sizeof(value));
if (ret >=0 ) {
int hw_sync_type = atoi(value);
out->avsync_type = hw_sync_type;
ALOGI("[%s:%d]:set av sync type=%d", __FUNCTION__, __LINE__, hw_sync_type);
ret = 0;
goto exit;
}
ret = str_parms_get_str (parms, "hw_av_sync", value, sizeof(value));
if (ret >= 0) {
int hw_sync_id = atoi(value);
int sync_enable = 0;
void *msync_session;
if (hw_sync_id < 0) {
ALOGE("[%s:%d]:The set parameter is abnormal, hw_sync_id=%d", __FUNCTION__, __LINE__, hw_sync_id);
ret = -EINVAL;
goto exit;
}
if (hw_sync_id == 12345678) {
ALOGE("[%s:%d]:tsync not support anymore!", __FUNCTION__, __LINE__);
} else {
if (out->avsync_type == AVSYNC_TYPE_MEDIASYNC) {
if (out->flags & AUDIO_OUTPUT_FLAG_HBBTV) {
int tem_id = adev->hbbtvsync_info.sync_id;
hw_sync_id = tem_id > 0 ? tem_id : hw_sync_id;
AM_LOGD("hw_av_sync id is replaced to %d in hbbtv case", hw_sync_id);
if (out->avsync_ctx && out->avsync_ctx->mediasync_ctx &&
hw_sync_id == out->avsync_ctx->mediasync_ctx->mediasync_id &&
out->avsync_ctx->mediasync_ctx->handle) {
AM_LOGD("hw_av_sync id is same as last, no need recreate");
ret = 0;
goto exit;
}
}
hwsync_mediasync_outset(out, &sync_enable, hw_sync_id, out->hal_format);
} else {
if ((out->avsync_ctx->msync_ctx) && (out->avsync_ctx->msync_ctx->msync_session)) {
ALOGW("[%s:%d]:hw_av_sync id set w/o release previous session.", __FUNCTION__, __LINE__);
av_sync_destroy(out->avsync_ctx->msync_ctx->msync_session);
pthread_mutex_lock(&(out->avsync_ctx->lock));
/*coverity[LOCK_EVASION]: There will be no competition here*/
out->avsync_ctx->msync_ctx->msync_session = NULL;
pthread_mutex_unlock(&(out->avsync_ctx->lock));
avsync_ctx_reset(out->avsync_ctx);
}
else {
pthread_mutex_lock(&(out->avsync_ctx->lock));
out->avsync_ctx->msync_ctx = msync_ctx_init();
pthread_mutex_unlock(&(out->avsync_ctx->lock));
}
audio_msync_t *msync_ctx = out->avsync_ctx->msync_ctx;
msync_session = av_sync_attach(hw_sync_id, AV_SYNC_TYPE_AUDIO);
if (!msync_session) {
AM_LOGE("Cannot attach hw_sync_id %d, error!", hw_sync_id);
ret = -EINVAL;
goto exit;
}
sync_enable = 1;
out->avsync_type = AVSYNC_TYPE_MSYNC;
out->with_header = true;
pthread_mutex_lock(&(out->avsync_ctx->lock));
out->avsync_ctx->get_tuning_latency = get_latency_pts;
msync_ctx->first_apts_flag = false;
msync_ctx->msync_first_insert_flag = false;
msync_ctx->msync_session = msync_session;
pthread_mutex_unlock(&(out->avsync_ctx->lock));
AM_LOGI("The current sync type:MSYNC, av_sync_attach success");
}
}
AM_LOGI("(%p)set hw_sync_id=%d, %s need_sync", out, hw_sync_id, sync_enable ? "enable" : "disable");
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&out->lock);
out->frame_write_sum = 0;
out->last_frames_position = 0;
pthread_mutex_unlock (&out->lock);
pthread_mutex_unlock (&adev->lock);
ret = 0;
goto exit;
}
if (eDolbyMS12Lib == adev->dolby_lib_type || adev->ms12.dap_only_enable) {
ret = str_parms_get_str(parms, "ms12_runtime", value, sizeof(value));
if (ret >= 0) {
char *parm = strstr(kvpairs, "=");
pthread_mutex_lock(&adev->lock);
if (parm) {
if (strstr(parm+1, "-xa") || strstr(parm+1, "-xu") ||
strstr(parm+1, "-at") || strstr(parm+1, "-pat") || strstr(parm+1, "-lang") ||
strstr(parm+1, "-lang2") || strstr(parm+1, "-ac4_pres_group_idx")) {
set_ms12_decoder_parameters((struct audio_stream_out*)out, parm+1);
} else {
aml_ms12_update_runtime_params(&(adev->ms12), parm+1);
}
}
pthread_mutex_unlock(&adev->lock);
goto exit;
}
}
ret = str_parms_get_str (parms, "playback_rate", value, sizeof (value));
if (ret >= 0) {
float rate = atof(value);
ALOGI("change rate to %f, format:0x%x, enable %d", rate, out->hal_format, out->enable_scaletempo);
out_set_playback_rate(stream, rate);
goto exit;
}
ret = str_parms_get_str (parms, "will_pause", value, sizeof (value));
if (ret >= 0) {
int will_pause = atoi(value);
out->will_pause = will_pause ? true : false;
ALOGI("stream(%p) will pause: %d", out, out->will_pause);
goto exit;
}
ret = str_parms_get_str (parms, "will_flush", value, sizeof (value));
if (ret >= 0) {
int will_flush = atoi(value);
out->will_flush = will_flush ? true : false;
ALOGI("stream(%p) will flush: %d", out, out->will_flush);
goto exit;
}
ret = str_parms_get_str (parms, "enable_scaletempo", value, sizeof (value));
if (ret >= 0) {
int enable_scaletempo = atoi(value);
out->enable_scaletempo = enable_scaletempo ? true : false;
ALOGI("change enable_scaletempo to %d", out->enable_scaletempo);
goto exit;
}
int m_val = -1;
ret = str_parms_get_int (parms, "decoder_id", &m_val);
if (ret >= 0) {
if (m_val >= 0) {
out->decoder_id = m_val;
} else {
AM_LOGI ("error. decoder_id: %d", m_val);
}
AM_LOGI ("set audio stream(%p) decoder_id:%d ", out, out->decoder_id);
goto exit;
}
ret = str_parms_get_int (parms, "is_airplay", &m_val);
if (ret >= 0) {
out->is_airplay = m_val == 1? true : false;
adev->audio_hal_info.update_cnt = 0;
AM_LOGI ("is_airplay stream:%d", m_val);
goto exit;
}
ret = str_parms_get_str (parms, "hbbtv_mode", value, sizeof (value));
if (ret >= 0) {
int hbbtv_mode = atoi(value);
if (hbbtv_mode) {
out->flags |= AUDIO_OUTPUT_FLAG_HBBTV;
AM_LOGI("stream(%p) add HBBTV flag: %x", out, out->flags);
if (out->scaletempo) {
hal_scaletempo_set_bypass(out->scaletempo, true);
}
}
goto exit;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
ret = str_parms_get_str(parms, "vol_ease", value, sizeof(value));
if (ret >= 0) {
int gain, duration, shape;// shape(0: linear, 1: in cube, 2: out cube)
AM_LOGI("vol_ease: value= %s", value);
if (adev->master_volume < FLOAT_ZERO) {
ret = 0;
goto exit;
}
if (sscanf(value, "%d,%d,%d", &gain, &duration, &shape) == 3) {
set_vol_ease_by_stream(out, gain, duration, shape);
ret = 0;
goto exit;
}
}
}
ret = str_parms_get_str (parms, "is_aacpcm", value, sizeof (value));
if (ret >= 0) {
int is_aacpcm = atoi(value);
out->is_aacpcm = is_aacpcm ? true : false;;
AM_LOGI("stream(%p) is_aacpcm: %d", out, out->is_aacpcm);
goto exit;
}
exit:
str_parms_destroy (parms);
// We shall return Result::OK, which is 0, if parameter is NULL,
// or we can not pass VTS test.
if (ret < 0) {
if (adev->debug_flag) {
ALOGW("Amlogic_HAL - %s: parameter is NULL, change ret value to 0 in order to pass VTS test.", __func__);
}
ret = 0;
}
return ret;
}
static char *out_get_parameters(const struct audio_stream *stream, const char *keys)
{
char temp_buf[1024] = {0};
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
aml_dec_t *aml_dec = aml_out->aml_dec;
if (strstr(keys, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) || \
strstr(keys, AUDIO_PARAMETER_STREAM_SUP_CHANNELS) || \
strstr(keys, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
return out_get_parameters_wrapper_about_sup_sampling_rates__channels__formats(stream, keys);
} else if (strstr (keys, "stream_sr") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_sr=%d", dec_info.dec_info.stream_sr);
return strdup (temp_buf);
} else if (strstr (keys, "stream_ch") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_ch=%d", dec_info.dec_info.stream_ch);
return strdup (temp_buf);
} else if (strstr (keys, "stream_bitrate") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_bitrate=%d", dec_info.dec_info.stream_bitrate);
return strdup (temp_buf);
} else if (strstr (keys, "stream_error_frames") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_error_frames=%d", dec_info.dec_info.stream_error_num);
return strdup (temp_buf);
} else if (strstr (keys, "stream_drop_frames") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_drop_frames=%d", dec_info.dec_info.stream_drop_num);
return strdup (temp_buf);
} else if (strstr (keys, "stream_decode_frames") ) {
aml_dec_info_t dec_info;
memset(&dec_info, 0x00, sizeof(dec_info));
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
sprintf (temp_buf, "stream_decode_frames=%d", dec_info.dec_info.stream_decode_num);
return strdup (temp_buf);
} else if (strstr (keys, "alsa_device_config")) {
sprintf (temp_buf, "period_cnt=%d;period_sz=%d", aml_out->config.period_count, aml_out->config.period_size);
return strdup (temp_buf);
} /*else if (strstr (keys, "get_buffer_avail")) {
if ((is_dolby_ms12_support_compression_format(aml_out->hal_internal_format) || is_multi_channel_pcm((struct audio_stream_out *)stream))) {
sprintf (temp_buf, "remain_frame=%d", (dolby_ms12_get_main_buffer_avail(NULL)));
return strdup (temp_buf);
}
return strdup ("");
} */ else if (strstr (keys, "decoder_end_of_stream")) {
sprintf (temp_buf, "decoder_end_of_stream=%d", aml_out->eos);
AM_LOGI_IF(get_debug_value(AML_DEBUG_AUDIOHAL_DEBUG), "%s", temp_buf);
return strdup (temp_buf);
} else if (strstr (keys, "get_metric_info")) {
if (eDolbyMS12Lib == aml_out->dev->dolby_lib_type) {
sprintf (temp_buf, "drop_start=%"PRId64",drop_duration=%"PRId64"", aml_out->drop_start, aml_out->drop_duration);
}
AM_LOGI_IF(get_debug_value(AML_DEBUG_AUDIOHAL_DEBUG), "%s", temp_buf);
aml_out->drop_start = -1;
aml_out->drop_duration = -1;
return strdup (temp_buf);
} else if (strstr(keys, "main_input_underrun")) {
if (eDolbyMS12Lib == aml_out->dev->dolby_lib_type) {
sprintf(temp_buf, "main_input_underrun=%d", get_ms12_main_underrun((struct audio_stream_out *)aml_out));
} else {
sprintf(temp_buf, "main_input_underrun=0");
}
AM_LOGI("%s", temp_buf);
return strdup(temp_buf);
} else if (strstr(keys, "av_sync_id")) {
if (aml_out->avsync_ctx && aml_out->avsync_ctx->mediasync_ctx) {
sprintf (temp_buf, "av_sync_id=%d", aml_out->avsync_ctx->mediasync_ctx->mediasync_id);
}
AM_LOGI("%s", temp_buf);
return strdup(temp_buf);
} else {
ALOGE("%s() keys %s is not supported! TODO!\n", __func__, keys);
return strdup ("");
}
}
static int out_set_volume_l (struct audio_stream_out *stream, float left, float right)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
bool is_dolby_format = is_dolby_ms12_support_compression_format(out->hal_internal_format);
ALOGI("%s(), stream(%p), left:%f right:%f, master_volume:%f, format:%x, is dolby:%d, is_new_vol_ease:%d \n",
__func__, stream, left, right, adev->master_volume, out->hal_internal_format, is_dolby_format, out->stream_vol_setting.is_new_setting);
out->last_volume_l_org = left;
out->last_volume_r_org = right;
out->volume_l_org = left;
out->volume_r_org = right;
if (STREAM_MAIN_INPUT == out->stream_type) {
out->volume_l = out->volume_l_org * out->source_volume * adev->master_volume;
out->volume_r = out->volume_r_org * out->source_volume * adev->master_volume;
} else {
out->volume_l = out->volume_l_org * adev->master_volume;
out->volume_r = out->volume_r_org * adev->master_volume;
}
if (adev->audio_focus_enable && STREAM_APP_INPUT != out->stream_type) {
out->volume_l = out->volume_l * adev->audio_focus_ratio;
out->volume_r = out->volume_r * adev->audio_focus_ratio;
AM_LOGI("audio focus set vol to :%f ratio:%f", out->volume_l, adev->audio_focus_ratio);
}
AM_LOGI("last_volume(L:%f,R:%f) volume_org(L:%f,R:%f) volume(L:%f,R:%f)",
out->last_volume_l_org, out->last_volume_r_org, out->volume_l_org, out->volume_r_org, out->volume_l, out->volume_r);
/* for not use ms12 case, we can use spdif enc mute, other wise ms12 can handle it*/
if (is_dts_format(out->hal_internal_format) ||
(is_dolby_format && (eDolbyDcvLib == adev->dolby_lib_type || is_bypass_dolbyms12(stream) || adev->hdmi_format == BYPASS))) {
if (out->volume_l_org * adev->master_volume > FLOAT_ZERO) {
ALOGI("set offload mute: false");
out->offload_mute = false;
} else if (out->volume_l_org * adev->master_volume < FLOAT_ZERO) {
ALOGI("set offload mute: true");
out->offload_mute = true;
}
}
if (out->volume_l != out->volume_r) {
AM_LOGI("left:%f(%f) right:%f(%f) NOT match", left, out->volume_l, right, out->volume_r);
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if (out->is_normal_pcm) {
set_ms12_aux_audio_volume(&adev->ms12, out->volume_l); /* system sound */
} else if (STREAM_APP_INPUT == out->stream_type) {
set_ms12_app_audio_volume(&adev->ms12, out->volume_l); /* app sound */
} else { /* main sound*/
ease_setting_t vol_setting;
//NTS vol_ease
if (out->stream_vol_setting.is_new_setting && STREAM_MAIN_INPUT == out->stream_type) {
out->volume_l = out->volume_l * out->stream_vol_setting.ease_setting.target_volume;
vol_setting.duration = out->stream_vol_setting.ease_setting.duration;
vol_setting.start_volume = out->volume_l;// start_volume = target_volume, then ease_status will be VolProcess in aml_audio_ease_config
vol_setting.target_volume = out->volume_l;
vol_setting.ease_shape = out->stream_vol_setting.ease_setting.ease_shape;
out->stream_vol_setting.is_new_setting = false;
AM_LOGI("apply NTS vol_ease to %f", out->volume_l);
} else {// MS12 volume process
vol_setting.duration = 32;
vol_setting.start_volume = out->volume_l;// start_volume = target_volume, then ease_status will be VolProcess in aml_audio_ease_config
vol_setting.target_volume = out->volume_l;
vol_setting.ease_shape = EaseLinear;
AM_LOGI("dolby_ms12 set vol to %f", out->volume_l);
}
aml_audio_ease_config(out->vol_process_handle, &vol_setting);
}
}
return 0;
}
static int set_volume_by_decoder_id (int id, float left, float right) {
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
pthread_mutex_lock (&adev->active_outputs_lock);
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
//struct audio_stream_out *out = (struct audio_stream_out *)aml_out;
if (aml_out && STREAM_MAIN_INPUT == aml_out->stream_type && id == aml_out->decoder_id) {
out_set_volume_l((struct audio_stream_out *)aml_out, left, right);
//break; //if stream not set decoder_id, set vol for all main stream.
}
if (i == STREAM_NUM_MAX) {
AM_LOGI("can't find stream which id = %d", id);
}
}
pthread_mutex_unlock (&adev->active_outputs_lock);
return 0;
}
void set_ms12_focus_by_stream_volume (struct aml_audio_device *adev) {
int main_stream_cnt = 0;
int is_focus = 1;
struct aml_stream_out *main1_out = NULL;
struct aml_stream_out *main2_out = NULL;
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out && aml_out->stream_type == STREAM_MAIN_INPUT) {
main_stream_cnt++;
if (main_stream_cnt == 1) {
main1_out = aml_out;
} else if (main_stream_cnt == 2) {
main2_out = aml_out;
//Here is dual main ms12 focus stratrgy, it might not be quite correct
//1. main1 vol is 0 and main2 vol is 0, focus main1
//2. main1 vol is not 0, main2 vol is 0, focus main1
//3. main1 vol is 0 and main2 vol is not 0, focus main2
//4. main1 vol is not 0, main2 vol is not 0, focus main2
//note: if has more than 2 main stream, need change here.
if ((main1_out->vol_process_handle->current_volume <= FLOAT_ZERO && main2_out->vol_process_handle->current_volume <= FLOAT_ZERO) ||
(main1_out->vol_process_handle->current_volume > FLOAT_ZERO && main2_out->vol_process_handle->current_volume <= FLOAT_ZERO)) {
if (main1_out->ms12_dec_handle != NULL) {
aml_ms12_decoder_setparameter(&adev->ms12, main1_out->ms12_dec_handle, MS12_CODEC_PARAMETER_REQUEST_FOCUS, &is_focus, sizeof(int));
}
AM_LOGI("focus main1");
} else if ((main1_out->vol_process_handle->current_volume <= FLOAT_ZERO && main2_out->vol_process_handle->current_volume > FLOAT_ZERO) ||
(main1_out->vol_process_handle->current_volume > FLOAT_ZERO && main2_out->vol_process_handle->current_volume > FLOAT_ZERO)) {
if (main2_out->ms12_dec_handle != NULL) {
aml_ms12_decoder_setparameter(&adev->ms12, main2_out->ms12_dec_handle, MS12_CODEC_PARAMETER_REQUEST_FOCUS, &is_focus, sizeof(int));
}
AM_LOGI("focus main2");
}
}
}
}
AM_LOGI("main stream cnt:%d", main_stream_cnt);
}
static void set_vol_ease_by_stream (struct aml_stream_out *aml_out, int gain, int duration, int shape) {
float vol;
vol = DbToAmpl(gain/128.0);
if (-12288 == gain) {//gain:(-12288~0) DbToAmpl(-12288/128.0) == 0.000016, it should be 0
vol = 0;
}
aml_out->stream_vol_setting.is_new_setting = true;
aml_out->stream_vol_setting.ease_setting.duration = duration;
aml_out->stream_vol_setting.ease_setting.start_volume = vol;
aml_out->stream_vol_setting.ease_setting.target_volume = vol;
aml_out->stream_vol_setting.ease_setting.ease_shape = EaseLinear;
if (1 == shape) {
aml_out->stream_vol_setting.ease_setting.ease_shape = EaseInCubic;
} else if (2 == shape) {
aml_out->stream_vol_setting.ease_setting.ease_shape = EaseOutCubic;
}
AM_LOGI("stream vol_ease gain:%d(%f) duration:%d shape:%d", gain, vol, duration, shape);
out_set_volume_l((struct audio_stream_out *)aml_out, aml_out->volume_l_org, aml_out->volume_r_org);
return;
}
static int set_vol_ease_by_decoder_id (int id, int gain, int duration, int shape) {
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
pthread_mutex_lock (&adev->active_outputs_lock);
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out && STREAM_MAIN_INPUT == aml_out->stream_type && id == aml_out->decoder_id) {
set_vol_ease_by_stream(aml_out, gain, duration, shape);
}
if (i == STREAM_NUM_MAX) {
AM_LOGI("can't find stream which id = %d", id);
}
}
pthread_mutex_unlock (&adev->active_outputs_lock);
return 0;
}
void out_set_stream_mute (struct audio_stream_out *stream, bool mute, bool is_external_mute)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *aml_dev = out->dev;
bool mute_status_change = false;
if (is_external_mute) {
out->external_mute = mute;
} else {
out->internal_mute = mute;
}
if (aml_dev->decoder_mute || out->internal_mute || out->external_mute) {
if (!out->final_mute) mute_status_change = true;
out->final_mute = true;
} else {
if (out->final_mute) mute_status_change = true;
out->final_mute = false;
}
if (mute_status_change && eDolbyMS12Lib == aml_dev->dolby_lib_type) {
if (out->stream_type == STREAM_AUX_INPUT) {
if (out->final_mute) {
set_ms12_aux_audio_volume(&aml_dev->ms12, 0.0f);
} else {
set_ms12_aux_audio_volume(&aml_dev->ms12, out->volume_l);
}
} else if (out->stream_type == STREAM_APP_INPUT) {
if (out->final_mute) {
set_ms12_app_audio_volume(&aml_dev->ms12, 0.0f);
} else {
set_ms12_app_audio_volume(&aml_dev->ms12, out->volume_l);
}
}
}
AM_LOGI("stream:%p, final_mute:%d (decoder_mute:%d internal_mute:%d external_mute:%d)",
out, out->final_mute, aml_dev->decoder_mute, out->internal_mute, is_external_mute);
}
static int out_set_volume (struct audio_stream_out *stream, float left, float right)
{
return out_set_volume_l(stream, left, right);
}
/* use standby instead of pause to fix background pcm playback */
static int out_pause_new (struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *aml_dev = aml_out->dev;
bool is_standby = aml_out->standby;
int ret = 0;
ALOGI("%s(), stream(%p), pause_status = %d,dolby_lib_type = %d, with_header = %d,ms12_enable = %d,ms_conti_paused = %d\n",
__func__, stream, aml_out->pause_status, aml_dev->dolby_lib_type, aml_out->with_header, aml_dev->ms12.dolby_ms12_enable, aml_dev->ms12.is_continuous_paused);
aml_audio_trace_int("out_pause_new", 1);
if (aml_out->stream_type == STREAM_AD_INPUT) {
ALOGI("%s(), do nothing for AD stream\n", __func__);
return 0;
}
pthread_mutex_lock (&aml_dev->lock);
pthread_mutex_lock (&aml_out->lock);
/* a stream should fail to pause if not previously started */
if (aml_out->pause_status == true) {
// If output stream is standby or paused,
// we should return Result::INVALID_STATE (3),
// thus we can pass VTS test.
ALOGE ("%s: stream in wrong status. standby(%d) or paused(%d)",
__func__, aml_out->standby, aml_out->pause_status);
ret = INVALID_STATE;
goto exit;
}
if (eDolbyMS12Lib == aml_dev->dolby_lib_type) {
if ((aml_dev->ms12.dolby_ms12_enable == true) &&
(aml_out->stream_type == STREAM_MAIN_INPUT)) {
aml_out->async_resume = false;
dolby_ms12_main_pause(stream);
}
} else if (aml_dev->useSubMix) {
mixer_pause_inport(aml_dev->audio_mixer, aml_out->inputPortID);
}
exit:
aml_out->pause_status = true;
aml_out->position_update = 0;
/* do avsync pause after 32ms sleep for to avoid the system clock stop early than dolby fading out finish in dolby continue mode. ##SWPL-108005 */
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MSYNC == aml_out->avsync_type) && aml_out->avsync_ctx->msync_ctx) {
av_sync_pause(aml_out->avsync_ctx->msync_ctx->msync_session, true);
}
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MEDIASYNC == aml_out->avsync_type) && aml_out->avsync_ctx->mediasync_ctx && !(aml_out->flags & AUDIO_OUTPUT_FLAG_HBBTV)) {
mediasync_wrap_setPause(aml_out->avsync_ctx->mediasync_ctx->handle, true);
}
pthread_mutex_unlock(&aml_out->lock);
pthread_mutex_unlock(&aml_dev->lock);
//1.audio easing duration is 32ms,
//2.one loop for schedule_run cost about 32ms(contains the hardware costing),
//3.if [pause, vol_ease(in adev_set_parameters by Netflix)] too short, means it need more time to do audio easing
//so, the delay time for 32ms(pause is completed after audio easing is done) is enough.
aml_audio_sleep(32000);
if (is_standby) {
ALOGD("%s(), stream(%p) already in standy, return INVALID_STATE", __func__, stream);
ret = INVALID_STATE;
}
aml_out->will_pause = false;
aml_audio_trace_int("out_pause_new", 0);
ALOGI("%s(), stream(%p) exit", __func__, stream);
return ret;
}
static int out_resume_new (struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *aml_dev = aml_out->dev;
int ret = 0;
ALOGI("%s(), stream(%p),standby = %d,pause_status = %d\n", __func__, stream, aml_out->standby, aml_out->pause_status);
aml_audio_trace_int("out_resume_new", 1);
if (aml_out->stream_type == STREAM_AD_INPUT) {
ALOGI("%s(), do nothing for AD stream\n", __func__);
return 0;
}
pthread_mutex_lock(&aml_dev->lock);
pthread_mutex_lock(&aml_out->lock);
unmute_after_eos_received (aml_out);
/* a stream should fail to resume if not previously paused */
// aml_out->standby is always "1" in ms12 continuous mode, which will cause fail to resume here
if (/*aml_out->standby || */aml_out->pause_status == false) {
// If output stream is not standby or not paused,
// we should return Result::INVALID_STATE (3),
// thus we can pass VTS test.
ALOGE ("Amlogic_HAL - %s: cannot resume, because output stream isn't in standby or paused state.", __FUNCTION__);
ret = 3;
goto exit;
}
if (eDolbyMS12Lib == aml_dev->dolby_lib_type) {
if ((aml_dev->ms12.dolby_ms12_enable == true) &&
(aml_out->stream_type == STREAM_MAIN_INPUT)) {
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MEDIASYNC == aml_out->avsync_type) && aml_out->avsync_ctx->mediasync_ctx) {
aml_out->async_resume = true;
} else {
dolby_ms12_main_resume(stream);
}
}
} else if (aml_dev->useSubMix) {
mixer_resume_inport(aml_dev->audio_mixer, aml_out->inputPortID);
}
if (aml_out->pause_status == false) {
ALOGE("%s(), stream status %d\n", __func__, aml_out->pause_status);
goto exit;
}
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MSYNC == aml_out->avsync_type) && aml_out->avsync_ctx->msync_ctx) {
av_sync_pause(aml_out->avsync_ctx->msync_ctx->msync_session, false);
}
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MEDIASYNC == aml_out->avsync_type) && aml_out->avsync_ctx->mediasync_ctx) {
mediasync_wrap_setPause(aml_out->avsync_ctx->mediasync_ctx->handle, false);
}
exit:
aml_out->pause_status = false;
pthread_mutex_unlock (&aml_out->lock);
pthread_mutex_unlock (&aml_dev->lock);
aml_audio_trace_int("out_resume_new", 0);
return ret;
}
static int out_flush_new (struct audio_stream_out *stream)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
struct dolby_ms12_desc *ms12 = &(adev->ms12);
bool is_patch_stream = false;
ALOGI("%s(), stream(%p)\n", __func__, stream);
out->frame_write_sum = 0;
out->last_frames_position = 0;
out->input_bytes_size = 0;
aml_audio_trace_int("out_flush_new", 1);
if (out->stream_type == STREAM_AD_INPUT) {
ALOGI("%s(), do nothing for AD stream\n", __func__);
return 0;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
unmute_after_eos_received (out);
if (out->total_write_size == 0) {
out->pause_status = false;
ALOGI("%s not writing, do nothing", __func__);
aml_audio_trace_int("out_flush_new", 0);
return 0;
}
if (AVSYNC_TYPE_NULL != out->avsync_type) {
if ((!out->pause_status) &&
(adev->ms12.dolby_ms12_enable == true) &&
(adev->ms12.is_continuous_paused == false) &&
(out->stream_type == STREAM_MAIN_INPUT) &&
(!out->eos)) {
ALOGI("flush easing pause");
/* issue a pause for flush to get easing out w/o stopping msync clock */
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
/*coverity[LOCK_EVASION]: There will be no competition here*/
ms12->is_continuous_paused = true;
dolby_ms12_main_pause(stream);
/*coverity[LOCK_EVASION]: There will be no competition here*/
out->pause_status = true;
pthread_mutex_unlock(&out->lock);
pthread_mutex_unlock(&adev->lock);
//audio easing duration is 32ms.
aml_audio_sleep(32000);
}
audio_header_info_reset(out->pheader);
avsync_ctx_reset(out->avsync_ctx);
adev->gap_passthrough_state = GAP_PASSTHROUGH_STATE_IDLE;
adev->gap_offset = 0;
}
//normal pcm(mixer thread) do not flush dolby ms12 input buffer
if (out->flags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (adev->ms12.dolby_ms12_enable)
dolby_ms12_main_flush(stream);
out->continuous_audio_offset = 0;
/*SWPL-39814, when using exo do seek, sometimes audio track will be reused, then the
*sequece will be pause->flush->writing data, we need to handle this.
*It may causes problem for normal pause/flush/resume
*/
if ((out->pause_status || adev->ms12.is_continuous_paused) && adev->ms12.dolby_ms12_enable) {
//audiohal_send_msg_2_ms12(ms12, MS12_MESG_TYPE_RESUME);
out->async_resume = false;
dolby_ms12_main_resume(stream);
}
}
hal_clip_meta_cleanup(out->clip_meta);
if (NULL != out->scaletempo) {
hal_scaletempo_force_init(out->scaletempo);
}
} else if (adev->useSubMix) {
aml_decoder_set_config(out->aml_dec, AML_DEC_CONFIG_FLUSH, &out->dec_config);
mixer_flush_inport(adev->audio_mixer, out->inputPortID);
}
if (out->aml_parser) {
aml_audio_parser_reset(out->aml_parser);
}
if (true == out->pause_status) {
if ((NULL != out->avsync_ctx) && (AVSYNC_TYPE_MEDIASYNC == out->avsync_type) && out->avsync_ctx->mediasync_ctx) {
mediasync_wrap_setPause(out->avsync_ctx->mediasync_ctx->handle, false);
}
}
out->pause_status = false;
out->will_flush = false;
out->u64DecOutFrame = 0;
ALOGI("%s(), stream(%p) is_patch_stream(%d) exit\n", __func__, stream, is_patch_stream);
aml_audio_trace_int("out_flush_new", 0);
return 0;
}
enum hwsync_status check_hwsync_status (uint32_t apts_gap)
{
enum hwsync_status sync_status;
if (apts_gap < APTS_DISCONTINUE_THRESHOLD_MIN)
sync_status = CONTINUATION;
else if (apts_gap > APTS_DISCONTINUE_THRESHOLD_MAX)
sync_status = RESYNC;
else
sync_status = ADJUSTMENT;
return sync_status;
}
static int out_get_render_position (const struct audio_stream_out *stream,
uint32_t *dsp_frames)
{
int ret = 0;
uint64_t dsp_frame_uint64 = 0;
struct timespec timetamp = {0};
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
*dsp_frames = 0;
ret = out_get_presentation_position(stream, &dsp_frame_uint64,&timetamp);
if (ret == 0)
{
*dsp_frames = (uint32_t)(dsp_frame_uint64 & 0xffffffff);
} else {
ret = -ENOSYS;
}
if (adev->debug_flag) {
ALOGD("%s,pos %d ret =%d \n",__func__,*dsp_frames, ret);
}
return ret;
}
/*support list return format e.g. Balance Hpeq*/
static int aml_report_audio_support_list_to_ui (struct aml_audio_device *adev, char *effect_support_list, int max_len) {
effect_handle_t effect;
int remaining = max_len;
char *pos = effect_support_list;
if (effect_support_list != NULL) {
memset(effect_support_list, 0, remaining);
} else {
return -EINVAL;
}
//1:get amlogic sound effect config
for (int i = 0 ;i < adev->native_postprocess.num_postprocessors; i++) {
effect = adev->native_postprocess.postprocessors[i];
if (!effect || !*effect || !(*effect)->get_descriptor) {
continue;
}
effect_descriptor_t tmpdesc;
(*effect)->get_descriptor(effect, &tmpdesc);
int written = snprintf(pos, remaining, " %s", tmpdesc.name);
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store amlogic sound effect type");
return -EINVAL;
}
pos += written;
remaining -= written;
}
//2:get dolby config type config
if (eDolbyMS12Lib == adev->dolby_lib_type_last) {
int written = snprintf(pos, remaining, " Dolby_MS12_Audio_Config_%s",
(adev->dolby_ms12_audio_config == MS12_CONFIG_Z ? "Z" :\
(adev->dolby_ms12_audio_config == MS12_CONFIG_X ? "X" : "Y")));
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store config type");
return -EINVAL;
}
pos += written;
remaining -= written;
}
//3:get dolby drc mode config
int written = snprintf(pos, remaining, " Drc_%s", dolby_lib_decode_enable(adev->dolby_lib_type)? "Show" : "Hide");
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store drc mode");
return -EINVAL;
}
pos += written;
remaining -= written;
//4. get dts x decoder support
if (adev->dts_lib_type == eDTSXLib) {
int written = snprintf(pos, remaining, " dts_x");
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store dts x");
return -EINVAL;
}
pos += written;
remaining -= written;
}
// get mpegh decoder support
if (adev->mpegh_decode_enable) {
int written = snprintf(pos, remaining, " mpegh");
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store mpegh string");
return -EINVAL;
}
pos += written;
remaining -= written;
}
//get ai audio support
if (adev->aml_ai_audio_type != eAmlAiAudioUnsupported) {
int written = snprintf(pos, remaining, " aml_ai_support_type=%d", adev->aml_ai_audio_type);
if (written < 0 || (size_t)written >= remaining) {
AM_LOGE("not enough space to store aml_ai_support_type string");
return -EINVAL;
}
pos += written;
remaining -= written;
}
return 0;
}
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *dev = out->dev;
int i;
int status = 0;
pthread_mutex_lock (&dev->lock);
pthread_mutex_lock (&out->lock);
if (dev->native_postprocess.num_postprocessors >= MAX_POSTPROCESSORS) {
status = -ENOSYS;
goto exit;
}
/* save audio effect handle in audio hal. if it is saved, skip this. */
for (i = 0; i < dev->native_postprocess.num_postprocessors; i++) {
if (dev->native_postprocess.postprocessors[i] == effect) {
status = 0;
goto exit;
}
}
dev->native_postprocess.postprocessors[dev->native_postprocess.num_postprocessors] = effect;
effect_descriptor_t tmpdesc;
(*effect)->get_descriptor(effect, &tmpdesc);
//Set dts vx sound effect to the first in the sound effect array
if (0 == strncmp(tmpdesc.name, "VirtualX",strlen("VirtualX"))) {
dev->native_postprocess.libvx_exist = Check_VX_lib();
ALOGI("%s, add audio effect: '%s' exist flag : %s", __FUNCTION__, VIRTUALX_LICENSE_LIB_PATH,
(dev->native_postprocess.libvx_exist) ? "true" : "false");
/* specify effect order for virtualx. VX does downmix from 5.1 to 2.0 */
i = dev->native_postprocess.num_postprocessors;
if (dev->native_postprocess.num_postprocessors >= 1) {
effect_handle_t tmp;
tmp = dev->native_postprocess.postprocessors[i];
dev->native_postprocess.postprocessors[i] = dev->native_postprocess.postprocessors[0];
dev->native_postprocess.postprocessors[0] = tmp;
ALOGI("%s, add audio effect: Reorder VirtualX at the first of the effect chain.", __FUNCTION__);
}
}
/*Since HPEQ attenuates the signal, full band drc is used for signal compensation*/
if (0 == strncmp(tmpdesc.name, "Hpeq", strlen("Hpeq"))) {
aml_mixer_ctrl_set_int(&dev->alsa_mixer, AML_MIXER_ID_AED_FULL_DRC_ENABLE, 1);
}
dev->native_postprocess.num_postprocessors++;
ALOGI("%s, add audio effect: %s in audio hal, effect_handle: %p, total num of effects: %d",
__FUNCTION__, tmpdesc.name, effect, dev->native_postprocess.num_postprocessors);
exit:
pthread_mutex_unlock (&out->lock);
pthread_mutex_unlock (&dev->lock);
return status;
}
static int out_remove_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
/*struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *dev = out->dev;
int i;
int status = -EINVAL;
bool found = false;
pthread_mutex_lock (&dev->lock);
pthread_mutex_lock (&out->lock);
if (dev->native_postprocess.num_postprocessors <= 0) {
status = -ENOSYS;
goto exit;
}
for (i = 0; i < dev->native_postprocess.num_postprocessors; i++) {
if (found) {
dev->native_postprocess.postprocessors[i - 1] = dev->native_postprocess.postprocessors[i];
continue;
}
if (dev->native_postprocess.postprocessors[i] == effect) {
dev->native_postprocess.postprocessors[i] = NULL;
status = 0;
found = true;
}
}
if (status != 0)
goto exit;
dev->native_postprocess.num_postprocessors--;
effect_descriptor_t tmpdesc;
(*effect)->get_descriptor(effect, &tmpdesc);
ALOGI("%s, remove audio effect: %s in audio hal, effect_handle: %p, total num of effects: %d",
__FUNCTION__, tmpdesc.name, effect, dev->native_postprocess.num_postprocessors);
exit:
pthread_mutex_unlock (&out->lock);
pthread_mutex_unlock (&dev->lock);
return status;*/
return 0;
}
static int out_get_next_write_timestamp (const struct audio_stream_out *stream __unused,
int64_t *timestamp __unused)
{
// return -EINVAL;
// VTS can only recognizes Result:OK or Result:INVALID_STATE, which is 0 or 3.
// So we return ESRCH (3) in order to pass VTS.
ALOGI ("Amlogic_HAL - %s: return ESRCH (3) instead of -EINVAL (-22)", __FUNCTION__);
return ESRCH;
}
//actually maybe it be not useful now except pass CTS_TEST:
// run cts -c android.media.cts.AudioTrackTest -m testGetTimestamp
static int out_get_presentation_position (const struct audio_stream_out *stream, uint64_t *frames, struct timespec *timestamp)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
uint64_t frames_written_hw = out->last_frames_position;
int frame_latency = 0,timems_latency = 0;
int ret = 0;
if (!frames || !timestamp) {
ALOGI("%s, !frames || !timestamp\n", __FUNCTION__);
return -EINVAL;
}
/* add this code for VTS. */
if (0 == frames_written_hw) {
*frames = frames_written_hw;
*timestamp = out->lasttimestamp;
return ret;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
ret = aml_audio_get_ms12_presentation_position(stream, frames, timestamp);
} else if (eDolbyDcvLib == adev->dolby_lib_type) {
bool is_audio_type_dolby = (adev->audio_type == EAC3 || adev->audio_type == AC3);
bool is_hal_format_dolby = (out->hal_format == AUDIO_FORMAT_AC3 || out->hal_format == AUDIO_FORMAT_E_AC3);
/* Fixme, use the tinymix inside aml_audio_earctx_get_type() everytime!!! */
bool is_earc = (ATTEND_TYPE_EARC == aml_audio_earctx_get_type(adev));
if (is_audio_type_dolby || is_hal_format_dolby) {
timems_latency = aml_audio_get_latency_offset(adev->active_outport,
out->hal_internal_format,
adev->sink_format,
adev->ms12.dolby_ms12_enable,
is_earc);
frame_latency = timems_latency * (out->hal_rate / 1000);
}
if ((frame_latency < 0) && (frames_written_hw < abs(frame_latency))) {
ALOGV("%s(), not ready yet", __func__);
return -EINVAL;
}
if (frame_latency >= 0)
*frames = frame_latency + frames_written_hw;
else if (frame_latency < 0) {
if (frames_written_hw >= abs(frame_latency))
*frames = frame_latency + frames_written_hw;
else
*frames = 0;
}
unsigned int output_sr = (out->config.rate) ? (out->config.rate) : (MM_FULL_POWER_SAMPLING_RATE);
*frames = *frames * out->hal_rate / output_sr;
*timestamp = out->lasttimestamp;
} else {
*frames = out->frame_write_sum;
*timestamp = out->lasttimestamp;
}
if (adev->debug_flag) {
#if (_TIME_BITS == 64)
AM_LOGI("out %p %"PRIu64", sec = %"PRId64", nanosec = %ld tunned_latency_ms %d frame_latency %d",
out, *frames, timestamp->tv_sec, timestamp->tv_nsec, timems_latency, frame_latency);
#else
AM_LOGI("out %p %"PRIu64", sec = %ld, nanosec = %ld tunned_latency_ms %d frame_latency %d",
out, *frames, timestamp->tv_sec, timestamp->tv_nsec, timems_latency, frame_latency);
#endif
int64_t frame_diff_ms = (*frames - out->last_frame_reported) * 1000 / out->hal_rate;
int64_t system_time_ms = 0;
if (timestamp->tv_nsec < out->last_timestamp_reported.tv_nsec) {
system_time_ms = (timestamp->tv_nsec + 1000000000 - out->last_timestamp_reported.tv_nsec)/1000000;
}
else
system_time_ms = (timestamp->tv_nsec - out->last_timestamp_reported.tv_nsec)/1000000;
int64_t jitter_diff = llabs(frame_diff_ms - system_time_ms);
if (jitter_diff > JITTER_DURATION_MS) {
#if (_TIME_BITS == 64)
AM_LOGI("last pos info: %p %"PRIu64", sec = %"PRId64", nanosec = %ld", out, out->last_frame_reported,
out->last_timestamp_reported.tv_sec, out->last_timestamp_reported.tv_nsec);
#else
AM_LOGI("last pos info: %p %"PRIu64", sec = %ld, nanosec = %ld", out, out->last_frame_reported,
out->last_timestamp_reported.tv_sec, out->last_timestamp_reported.tv_nsec);
#endif
AM_LOGI("system time diff %"PRIu64" ms, position diff %"PRIu64" ms, jitter %"PRIu64" ms",
system_time_ms,frame_diff_ms,jitter_diff);
}
out->last_frame_reported = *frames;
out->last_timestamp_reported = *timestamp;
}
return ret;
}
/* must be called with hw device and input stream mutexes locked */
int start_input_stream(struct aml_stream_in *in)
{
struct aml_audio_device *adev = in->dev;
audio_input_config_t config;
memset(&config, 0, sizeof(audio_input_config_t));
config.sample_rate = MM_FULL_POWER_SAMPLING_RATE;
config.channel_mask = AUDIO_CHANNEL_IN_STEREO;
config.format = in->hal_format;
adev->active_input = in;
if (adev->mode != AUDIO_MODE_IN_CALL) {
adev->in_device &= ~AUDIO_DEVICE_IN_ALL;
adev->in_device |= in->device;
}
if (in->input_type == AUDIO_INPUT_TYPE_BUILTIN_MIC) {
config.sample_rate = in->requested_rate;
}
audio_input_open(in->input_type, &config);
AM_LOGI("input type: %d", in->input_type);
#ifndef ANDROID_BUILD
/* if no supported sample rate is available, use the resampler */
if (in->resampler) {
in->resampler->reset(in->resampler);
in->frames_in = 0;
}
#endif
return 0;
}
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
return in->requested_rate;
}
static int in_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
{
return 0;
}
static size_t in_get_buffer_size(const struct audio_stream *stream)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
size_t size;
ALOGD("%s: enter: channel_mask(%#x) rate(%d) format(%#x)", __func__,
in->hal_channel_mask, in->requested_rate, in->hal_format);
size = get_input_buffer_size(in->config.period_size, in->requested_rate,
in->hal_format,
audio_channel_count_from_in_mask(in->hal_channel_mask));
/*coverity[missing_lock]:in->source does not need to be lock*/
if (in->source == AUDIO_SOURCE_ECHO_REFERENCE) {
size_t frames = CAPTURE_PERIOD_SIZE;
frames = CAPTURE_PERIOD_SIZE * PLAYBACK_CODEC_SAMPLING_RATE / CAPTURE_CODEC_SAMPLING_RATE;
size = get_input_buffer_size(frames, in->requested_rate,
in->hal_format,
audio_channel_count_from_in_mask(in->hal_channel_mask));
}
ALOGD("%s: exit: buffer_size = %zu", __func__, size);
return size;
}
static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
return in->hal_channel_mask;
}
static audio_format_t in_get_format(const struct audio_stream *stream)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
ALOGV("%s(%p) in->hal_format=%d", __FUNCTION__, in, in->hal_format);
return in->hal_format;
}
static int in_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
{
return -ENOSYS;
}
/* must be called with hw device and input stream mutexes locked */
int do_input_standby(struct aml_stream_in *in)
{
struct aml_audio_device *adev = in->dev;
ALOGD ("%s(%p) in->standby = %d", __FUNCTION__, in, in->standby);
if (!in->standby) {
audio_input_close(in->input_type);
adev->active_input = NULL;
in->standby = 1;
}
return 0;
}
static int in_standby(struct audio_stream *stream)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
int status;
ALOGD("%s: enter: stream(%p)", __func__, stream);
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
status = do_input_standby(in);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
ALOGD("%s: exit", __func__);
return status;
}
static int in_dump (const struct audio_stream *stream __unused, int fd __unused)
{
return 0;
}
static int in_set_parameters (struct audio_stream *stream, const char *kvpairs)
{
struct aml_stream_in *in = (struct aml_stream_in *) stream;
struct aml_audio_device *adev = in->dev;
struct str_parms *parms;
char value[32];
int ret, val = 0;
bool do_standby = false;
ALOGD ("%s(%p, %s)", __FUNCTION__, stream, kvpairs);
parms = str_parms_create_str (kvpairs);
ret = str_parms_get_str (parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof (value) );
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&in->lock);
if (ret >= 0) {
val = atoi (value);
/* no audio source uses val == 0 */
if ( (in->source != val) && (val != 0) ) {
in->source = val;
do_standby = true;
}
}
ret = str_parms_get_str (parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof (value) );
if (ret >= 0) {
val = atoi (value) & ~AUDIO_DEVICE_BIT_IN;
if ( (in->device != (unsigned)val) && (val != 0) ) {
in->device = val;
do_standby = true;
}
}
if (do_standby) {
do_input_standby (in);
}
pthread_mutex_unlock (&in->lock);
pthread_mutex_unlock (&adev->lock);
int framesize = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_FRAME_COUNT, &framesize);
if (ret >= 0) {
if (framesize > 0) {
ALOGI ("Reset audio input hw frame size from %d to %d\n",
in->config.period_size * in->config.period_count, framesize);
in->config.period_size = framesize / in->config.period_count;
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&in->lock);
if (!in->standby && (in == adev->active_input) ) {
do_input_standby (in);
start_input_stream (in);
in->standby = 0;
}
pthread_mutex_unlock (&in->lock);
pthread_mutex_unlock (&adev->lock);
}
}
int format = 0;
ret = str_parms_get_int (parms, AUDIO_PARAMETER_STREAM_FORMAT, &format);
if (ret >= 0) {
if (format != AUDIO_FORMAT_INVALID) {
in->hal_format = (audio_format_t)format;
ALOGV(" in->hal_format:%d <== format:%d\n",
in->hal_format, format);
pthread_mutex_lock (&adev->lock);
pthread_mutex_lock (&in->lock);
if (!in->standby && (in == adev->active_input) ) {
do_input_standby (in);
start_input_stream (in);
in->standby = 0;
}
pthread_mutex_unlock (&in->lock);
pthread_mutex_unlock (&adev->lock);
}
}
str_parms_destroy (parms);
// VTS can only recognizes Result::OK, which is 0x0.
// So we change ret value to 0 when ret isn't equal to 0
if (ret > 0) {
ALOGI ("Amlogic_HAL - %s: change ret value to 0 if it's greater than 0 for passing VTS test.", __FUNCTION__);
ret = 0;
} else if (ret < 0) {
ALOGI ("Amlogic_HAL - %s: parameter is NULL, change ret value to 0 if it's greater than 0 for passing VTS test.", __FUNCTION__);
ret = 0;
}
return ret;
}
static char * in_get_parameters (const struct audio_stream *stream, const char *keys)
{
char *cap = NULL;
char *para = NULL;
char temp_buf[AUDIO_HAL_CHAR_MAX_LEN] = {0};
struct aml_stream_in *in = (struct aml_stream_in *)stream;
ALOGI ("in_get_parameters %s,in %p\n", keys, in);
if (strstr (keys, AUDIO_PARAMETER_STREAM_SUP_FORMATS) ) {
ALOGV ("Amlogic - return hard coded sup_formats list for in stream.\n");
cap = strdup ("sup_formats=AUDIO_FORMAT_PCM_16_BIT|AUDIO_FORMAT_PCM_32_BIT");
if (cap) {
para = strdup (cap);
aml_audio_free (cap);
return para;
}
} else if (strstr (keys, "alsa_device_config")) {
sprintf (temp_buf, "period_cnt=%d;period_sz=%d", in->config.period_count, in->config.period_size);
return strdup (temp_buf);
} else if (strstr (keys, "get_aml_source_latency")) {
ALOGI ("Error: in_get_parameters can not get_aml_source_latency ");
return strdup (temp_buf);
}
return strdup ("");
}
static int in_set_gain (struct audio_stream_in *stream __unused, float gain __unused)
{
return 0;
}
#define DEBUG_AEC_VERBOSE (0)
#define DEBUG_AEC (0) // Remove after AEC is fine-tuned
#define TIMESTAMP_LEN (8)
static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes)
{
int ret = 0;
struct aml_stream_in *in = (struct aml_stream_in *)stream;
struct aml_audio_device *adev = in->dev;
size_t in_frames = bytes / audio_stream_in_frame_size(&in->stream);
size_t size_aec = 0;
void *buffer_aec = NULL;
size_t read_size = 0;
size_t total_read_size = 0;
AM_LOGV("stream: %d, bytes %zu", in->source, bytes);
/* acquiring hw device mutex systematically is useful if a low priority thread is waiting
* on the input stream mutex - e.g. executing select_mode() while holding the hw device
* mutex
*/
pthread_mutex_lock(&in->lock);
if (in->source == AUDIO_SOURCE_MIXER_OUT || in->source == AUDIO_SOURCE_ENCODER_OUT) {
if (in->source == AUDIO_SOURCE_ENCODER_OUT) {
adev->encoder_ctx.enc_buf_space = bytes;
if (bytes > adev->encoder_ctx.frame_len * audio_stream_in_frame_size(&in->stream)) {
bytes = adev->encoder_ctx.frame_len * audio_stream_in_frame_size(&in->stream);
}
}
do {
if (aml_port_get_avail(in->capture_port) > bytes) {
struct audio_buffer abuffer;
memset(&abuffer, 0, sizeof(abuffer));
abuffer.buffer = buffer;
abuffer.size = bytes;
read_size = aml_port_pull_buffer(in->capture_port, &abuffer);
}
total_read_size += read_size;
if (total_read_size < bytes) {
aml_audio_sleep(1000000 * (bytes - total_read_size) / audio_stream_in_frame_size(&in->stream) /
MIXER_OUT_DEFAULT_SAMPLE_RATE);
}
} while (total_read_size < bytes);
if (in->source == AUDIO_SOURCE_ENCODER_OUT) {
bytes = aml_encoder_capture(&adev->encoder_ctx, buffer, total_read_size, buffer);
}
} else {
if (in->standby) {
ret = start_input_stream(in);
if (ret < 0)
goto exit;
in->standby = 0;
}
/*if need mute input source, don't read data from hardware anymore*/
if (adev->mic_mute) {
memset(buffer, 0, bytes);
/*coverity[sleep]*/
aml_audio_sleep(bytes * 1000000 / audio_stream_in_frame_size(stream) / in_get_sample_rate(&stream->common));
ret = 0;
} else {
/*if (in->resampler) {
ret = read_frames(in, buffer, in_frames);
} else */{
if (in->device & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
size_aec = bytes * 2; /* 2ch mic + 2ch loopback */
buffer_aec = (char *)aml_audio_malloc(size_aec);
if (buffer_aec == NULL) {
ret = -ENOMEM;
goto exit;
}
memset(buffer_aec, 0, size_aec);
/*coverity[sleep]*/
ret = audio_input_read(in->input_type, buffer_aec, size_aec);
aml_dump_audio_bitstreams("dump_pre.raw", buffer_aec, size_aec);
aec_process(adev->aml_aec, buffer_aec, buffer);
aml_dump_audio_bitstreams("dump_post.raw", buffer, bytes);
aml_audio_free(buffer_aec);
} else {
ret = audio_input_read(in->input_type, buffer, bytes);
}
}
if (ret < 0)
goto exit;
}
if (ret >= 0) {
in->frames_read += in_frames;
in->timestamp_nsec = audio_input_get_timestamp(in->input_type);
}
bool mic_muted = false;
adev_get_mic_mute((struct audio_hw_device*)adev, &mic_muted);
if (mic_muted) {
memset(buffer, 0, bytes);
}
}
exit:
if (ret < 0) {
AM_LOGE("%s: read failed - sleeping for buffer duration", __func__);
/*coverity[sleep]*/
aml_audio_sleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /in_get_sample_rate(&stream->common));
}
pthread_mutex_unlock(&in->lock);
if (ret >= 0 && get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
aml_dump_audio_bitstreams("alsa_read.raw", buffer, bytes);
}
return bytes;
}
static int in_get_capture_position (const struct audio_stream_in* stream, int64_t* frames,
int64_t* time) {
if (stream == NULL || frames == NULL || time == NULL) {
return -EINVAL;
}
struct aml_stream_in *in = (struct aml_stream_in *)stream;
if ((is_STB(in->dev)) && (in->device & AUDIO_DEVICE_IN_HDMI)) {
return -ENOSYS;
}
/*coverity[missing_lock]:in->frames_read does not need to be lock*/
*frames = in->frames_read;
/*coverity[missing_lock]:in->timestamp_nsec does not need to be lock*/
*time = in->timestamp_nsec;
return 0;
}
static uint32_t in_get_input_frames_lost (struct audio_stream_in *stream __unused)
{
return 0;
}
static int in_add_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
int status;
effect_descriptor_t desc;
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
if (in->num_preprocessors >= MAX_PREPROCESSORS) {
status = -ENOSYS;
goto exit;
}
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
goto exit;
in->preprocessors[in->num_preprocessors++] = effect;
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = true;
do_input_standby(in);
}
exit:
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
}
static int in_remove_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
struct aml_stream_in *in = (struct aml_stream_in *)stream;
int i;
int status = -EINVAL;
bool found = false;
effect_descriptor_t desc;
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
if (in->num_preprocessors <= 0) {
status = -ENOSYS;
goto exit;
}
for (i = 0; i < in->num_preprocessors; i++) {
if (found) {
in->preprocessors[i - 1] = in->preprocessors[i];
continue;
}
if (in->preprocessors[i] == effect) {
in->preprocessors[i] = NULL;
status = 0;
found = true;
}
}
if (status != 0)
goto exit;
in->num_preprocessors--;
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
goto exit;
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = false;
do_input_standby(in);
}
exit:
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
}
static int in_get_active_microphones (const struct audio_stream_in *stream,
struct audio_microphone_characteristic_t *mic_array,
size_t *mic_count) {
ALOGV("in_get_active_microphones");
if ((mic_array == NULL) || (mic_count == NULL)) {
return -EINVAL;
}
struct aml_stream_in *in = (struct aml_stream_in *)stream;
struct audio_hw_device* dev = (struct audio_hw_device*)in->dev;
bool mic_muted = false;
adev_get_mic_mute(dev, &mic_muted);
/*coverity[missing_lock]:in->source does not need to be lock*/
if ((in->source == AUDIO_SOURCE_ECHO_REFERENCE) || mic_muted) {
*mic_count = 0;
return 0;
}
adev_get_microphones(dev, mic_array, mic_count);
return 0;
}
static int adev_get_microphones (const struct audio_hw_device* dev __unused,
struct audio_microphone_characteristic_t* mic_array,
size_t* mic_count) {
ALOGV("adev_get_microphones");
if ((mic_array == NULL) || (mic_count == NULL)) {
return -EINVAL;
}
get_mic_characteristics(mic_array, mic_count);
return 0;
}
static void get_mic_characteristics (struct audio_microphone_characteristic_t* mic_data,
size_t* mic_count) {
*mic_count = 1;
memset(mic_data, 0, sizeof(struct audio_microphone_characteristic_t));
strncpy(mic_data->device_id, "builtin_mic", sizeof(mic_data->device_id));
strncpy(mic_data->address, "top", sizeof(mic_data->address));
memset(mic_data->channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED,
sizeof(mic_data->channel_mapping));
mic_data->device = AUDIO_DEVICE_IN_BUILTIN_MIC;
mic_data->sensitivity = -37.0;
mic_data->max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
mic_data->min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
mic_data->orientation.x = 0.0f;
mic_data->orientation.y = 0.0f;
mic_data->orientation.z = 0.0f;
mic_data->geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
mic_data->geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
mic_data->geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
}
static int aml_is_audio_playing(struct aml_audio_device *adev) {
pthread_mutex_lock (&adev->lock);
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out) {
if (aml_out->stream_type == STREAM_MAIN_INPUT && (false == aml_out->pause_status)) {
ALOGI("main stream(%p) is playing", aml_out);
pthread_mutex_unlock (&adev->lock);
return 1;
} else if (aml_out->stream_type == STREAM_AUX_INPUT && aml_out->is_airplay) {
ALOGI("aux airplay stream(%p) is playing", aml_out);
pthread_mutex_unlock (&adev->lock);
return 1;
}
}
}
pthread_mutex_unlock (&adev->lock);
ALOGI("main audio is not playing");
return 0;
}
char * get_output_port_information(struct aml_audio_device *adev, char *output_port_info) {
if (output_port_info == NULL)
return "";
int output_channel = 2;
if (adev->active_outport == OUTPORT_SPEAKER || adev->active_outport == OUTPORT_HEADPHONE) {
sprintf(output_port_info, "%s,2,pcm", audioActivePort2NetflixStandardStr(adev->active_outport));
} else {
if ((adev->optical_format == AUDIO_FORMAT_PCM_16_BIT) && (adev->output_multipcm_ch != 0)) {
output_channel = adev->output_multipcm_ch;
} else if (adev->optical_format == AUDIO_FORMAT_E_AC3) {
output_channel = adev->output_ddp_ch;
} else if (adev->optical_format == AUDIO_FORMAT_AC3) {
output_channel = adev->output_dd_ch;
} else if (adev->optical_format == AUDIO_FORMAT_MAT) {
output_channel = adev->output_mat_ch;
}
sprintf(output_port_info, "%s,%d,%s", audioActivePort2NetflixStandardStr(adev->active_outport), output_channel, audioFormat2NetflixStandardStr(adev->optical_format));
}
return output_port_info;
}
static int out_set_event_callback(struct audio_stream_out *stream __unused,
stream_event_callback_t callback, void *cookie /*StreamOut*/)
{
ALOGD("func:%s callback:%p cookie:%p", __func__, callback, cookie);
return 0;
}
static inline int set_stream_config(struct aml_stream_out *out, struct audio_config *config, audio_output_flags_t flags)
{
struct aml_audio_device *adev = out->dev;
out->config.channels = audio_channel_count_from_out_mask(config->channel_mask);
out->config.rate = config->sample_rate;
switch (config->format) {
case AUDIO_FORMAT_PCM_16_BIT:
out->config.format = PCM_FORMAT_S16_LE;
break;
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_32_BIT:
out->config.format = PCM_FORMAT_S32_LE;
break;
case AUDIO_FORMAT_IEC61937:
if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (out->config.channels == 2 && (out->config.rate == 192000 || out->config.rate == 176400 || out->config.rate == 128000)) {
out->config.rate /= 4;
out->hal_internal_format = AUDIO_FORMAT_E_AC3;
} else if (out->config.channels == 2 && out->config.rate >= 32000 && out->config.rate <= 48000) {
if (adev->audio_type == DTS) {
out->hal_internal_format = AUDIO_FORMAT_DTS;
//adev->dts_hd.is_dtscd = false;
adev->dolby_lib_type = eDolbyDcvLib;
out->restore_dolby_lib_type = true;
} else {
out->hal_internal_format = AUDIO_FORMAT_AC3;
}
} else if (out->config.channels >= 6 && out->config.rate == 192000) {
out->hal_internal_format = AUDIO_FORMAT_DTS_HD;
}
ALOGI("convert format IEC61937 to 0x%x\n", out->hal_internal_format);
}
break;
case AUDIO_FORMAT_AC3:
case AUDIO_FORMAT_E_AC3:
case AUDIO_FORMAT_AC4:
case AUDIO_FORMAT_DTS:
case AUDIO_FORMAT_DTS_HD:
break;
default:
break;
}
if (codec_type_is_raw_data(out->codec_type)) {
ALOGI("%s: for raw audio output,force alsa stereo output", __func__);
out->config.channels = 2;
}
return 0;
}
int get_stream_source_volume(struct aml_stream_out *aml_out)
{
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
switch (aml_out->source_type) {
case DTV_SRC:
aml_out->source_volume = get_inport_gain(adev, INPORT_TUNER);
break;
case ATV_SRC:
aml_out->source_volume = get_inport_gain(adev, INPORT_ATV);
break;
case AV_SRC:
aml_out->source_volume = get_inport_gain(adev, INPORT_LINEIN);
break;
case HDMI_SRC:
aml_out->source_volume = get_inport_gain(adev, INPORT_HDMIIN);
break;
case HDMI1_SRC:
aml_out->source_volume = get_inport_gain(adev, INPORT_HDMIIN1);
break;
default:
aml_out->source_volume = get_inport_gain(adev, INPORT_MEDIA);
break;
}
AM_LOGI("stream:%p, source_type:%s, source_volume:%f",aml_out, StreamSourceType2Str(aml_out->source_type), aml_out->source_volume);
return 0;
}
// open corresponding stream by flags, formats and others params
static int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle __unused,
audio_devices_t devices,
audio_output_flags_t flags,
struct audio_config *config,
struct audio_stream_out **stream_out,
const char *address)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_stream_out *out;
int ret = 0;
AM_LOGD("enter: devices(%#x) channel_mask(%#x) rate(%d) format(%#x) flags(%#x) address:%s",
devices, config->channel_mask, config->sample_rate, config->format, flags, (address?address:"NULL"));
ALOGI("%s: enter, ver:%s", __func__, libVersion_audio_hal);
out = (struct aml_stream_out *)aml_audio_calloc(1, sizeof(struct aml_stream_out));
if (!out) {
ALOGE("%s malloc error", __func__);
return -ENOMEM;
}
if (flags == AUDIO_OUTPUT_FLAG_NONE)
flags = AUDIO_OUTPUT_FLAG_PRIMARY;
if (config->channel_mask == AUDIO_CHANNEL_NONE)
config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
if (config->sample_rate == 0)
config->sample_rate = 48000;
if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {
if (config->format == AUDIO_FORMAT_DEFAULT) {
config->format = AUDIO_FORMAT_PCM_16_BIT;
}
out->config = pcm_config_out;
} else if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
out->config = pcm_config_out_direct;
} else {
out->config = pcm_config_out;
}
out->hal_channel_mask = config->channel_mask;
out->hal_rate = config->sample_rate;
out->hal_format = config->format;
out->hal_internal_format = out->hal_format;
out->hal_input_is_float = false;
if (config->format == AUDIO_FORMAT_PCM_FLOAT) {
out->hal_input_is_float = true;
out->hal_format = AUDIO_FORMAT_PCM_32_BIT;
out->hal_internal_format = AUDIO_FORMAT_PCM_32_BIT;
}
if (out->hal_internal_format == AUDIO_FORMAT_E_AC3_JOC) {
out->hal_internal_format = AUDIO_FORMAT_E_AC3;
ALOGD("config hal_format %#x change to hal_internal_format(%#x)!", out->hal_format, out->hal_internal_format);
}
out->codec_type = get_codec_type(out->hal_internal_format);
set_stream_config(out, config, flags);
if (flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) {
out->need_sync = true;
} else {
out->need_sync = false;
}
AM_LOGI("Set out->need_sync:%d", out->need_sync);
out->dev = adev;
if (address) {
AM_LOGI("address: %s", address);
if (!strncmp(address, "AML_", 4)) {
ALOGI("%s(): aml TV source stream = %s", __func__, address);
out->tv_src_stream = true;
}
//set source_type, this must be set before stream_type_validate_l
if (!strncmp(address, "AML_ATV", 7)) {
out->source_type = ATV_SRC;
} else if (!strncmp(address, "AML_DTV", 7)) {
out->source_type = DTV_SRC;
} else if (!strncmp(address, "AML_AV", 6)) {
out->source_type = AV_SRC;
} else if (!strncmp(address, "AML_HDMI1", 9)) {
out->source_type = HDMI1_SRC;
} else if (!strncmp(address, "AML_HDMI", 8)) {
out->source_type = HDMI_SRC;
}
if (!strcmp(address, LLP_BUFFER_NAME)) {
int nChannel = audio_channel_count_from_out_mask(out->hal_channel_mask);
if (0 != llp_init(adev, nChannel)) {
goto err;
}
}
}
if (adev->dolby_lib_type_last == eDolbyMS12Lib && !(flags & AUDIO_OUTPUT_FLAG_AD_STREAM)) {
/*if these format can't be supported by ms12, we can bypass it*/
if (is_bypass_dolbyms12((struct audio_stream_out *)out)) {
if (adev->ms12.dolby_ms12_enable) {
AM_LOGI("bypass aml_ms12, clean up aml_ms12");
adev_ms12_cleanup((struct audio_hw_device *)adev);
}
adev->dolby_lib_type = eDolbyDcvLib;
out->restore_dolby_lib_type = true;
AM_LOGI("bypass aml_ms12 change aml_dolby dcv lib type");
}
}
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.set_format = out_set_format;
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.common.standby = out_standby_new;
out->stream.common.get_channels = out_get_channels;
out->stream.common.get_format = out_get_format;
/* Assign value to out->flags, out->stream_type, out->write, and decide whether enable scaletempo */
if (stream_type_validate_l(out, flags) < 0) {
AM_LOGE("stream_type_validate_l failed");
aml_audio_trace_int("adev_open_output_stream_new", 0);
ret = -ENOMEM;
goto err;
}
out->stream.get_latency = out_get_latency;
out->stream.set_volume = out_set_volume;
out->stream.get_render_position = out_get_render_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
out->stream.get_presentation_position = out_get_presentation_position;
out->stream.set_event_callback = out_set_event_callback;
out->stream.write = out_write_new;
out->stream.pause = out_pause_new;
out->stream.resume = out_resume_new;
out->stream.flush = out_flush_new;
out->out_device = devices;
out->is_normal_pcm = (out->stream_type == STREAM_AUX_INPUT || out->stream_type == STREAM_DEEP_INPUT) ? 1 : 0;
out->hal_ch = audio_channel_count_from_out_mask(out->hal_channel_mask);
out->hal_frame_size = audio_bytes_per_frame(out->hal_ch, out->hal_internal_format);
if (out->hal_ch == 0) {
out->hal_ch = 2;
}
if (out->hal_frame_size == 0) {
out->hal_frame_size = 1;
}
if (out->stream_type == STREAM_MAIN_INPUT) {
adev->audio_hal_info.format = AUDIO_FORMAT_PCM;
adev->audio_hal_info.is_dolby_atmos = 0;
adev->audio_hal_info.update_type = TYPE_PCM;
adev->audio_hal_info.first_decoding_frame = false;
adev->audio_hal_info.dts_type_is_updated = false;
}
if (out->stream_type == STREAM_MAIN_INPUT || out->stream_type == STREAM_AD_INPUT) {
get_stream_source_volume(out);
} else {
out->source_volume = 1.0;
}
if (out->stream_type == STREAM_HWMIXER_INPUT) {
aml_audio_hwmixer_out_open(adev);
}
if (adev->enable_deep_unit && out->stream_type == STREAM_DEEP_INPUT) {
audio_unit_acquire_port(adev->deep_unit, &out->deep_inport, AUDIO_PORT_INPUT);
}
if (out->source_type == DTV_SRC) {
out->volume_l_org = get_dtv_volume(adev);
out->volume_r_org = get_dtv_volume(adev);
} else {
out->volume_l_org = 1.0;//stream volume
out->volume_r_org = 1.0;
}
out->last_volume_l_org = out->volume_l_org;
out->last_volume_r_org = out->volume_r_org;
out->volume_l = adev->master_volume * out->volume_l_org;//out volume
out->volume_r = adev->master_volume * out->volume_r_org;
out->last_volume_l = 0.0;
out->last_volume_r = 0.0;
out->drc_mode_volume = 1.0;
out->last_drc_mode_volume = 1.0;
out->standby = true;
out->frame_write_sum = 0;
out->need_convert = false;
out->position_update = 0;
out->inputPortID = -1;
out->will_pause = false;
out->will_flush = false;
out->eos = false;
out->output_speed = 1.0f;
out->clip_offset = 0;
out->clip_meta = NULL;
out->drop_start = -1;
out->drop_duration = -1;
out->ddp_frame_size = aml_audio_get_ddp_frame_size();
out->resample_handle = NULL;
out->set_init_avsync_policy = false;
out->unique_ID = adev->next_unique_ID;
adev->next_unique_ID++;
out->scale_state = SCALE_NULL;
out->rate = 0.0;
out->decoder_id = -1;
out->u64DecOutFrame = 0;
out->is_aacpcm = false;
out->async_resume = false;
//#ifdef ENABLE_MMAP
#ifndef BUILD_LINUX
if (out->stream_type == STREAM_APP_INPUT) {
outMmapInit(out);
}
#endif
if (out->stream_type != STREAM_AD_INPUT) {
out->avsync_ctx = avsync_ctx_init();
if (NULL == out->avsync_ctx) {
ret = -ENOMEM;
goto err;
}
}
/* set param to ms12, then mcn can be processed correctly when aux input. */
if (out->stream_type == STREAM_AUX_INPUT && eDolbyMS12Lib == adev->dolby_lib_type) {
char parm1[32] = "\0";
char parm2[32] = "\0";
int chs = 2, ls = 0;
if (6 == audio_channel_count_from_out_mask(out->hal_channel_mask)) {
chs = 7;
ls = 1;
}
sprintf(parm1, "%s %d", "-chs", chs);
sprintf(parm2, "%s %d", "-ls", ls);
aml_ms12_update_runtime_params(&(adev->ms12), parm1);
aml_ms12_update_runtime_params(&(adev->ms12), parm2);
}
if (adev->user_setting_scaletempo && out->enable_scaletempo) {
hal_scaletempo_init(&out->scaletempo);
if ((out->flags & AUDIO_OUTPUT_FLAG_HBBTV || is_dts_format(out->hal_internal_format)) && out->scaletempo) {
hal_scaletempo_set_bypass(out->scaletempo, true);
}
AM_LOGI("enable scaletempo %p", out);
}
init_aml_pcm_downmix(&out->pcm_downmix);
out->downmix_tmp_buf_size = 2048 * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT) * 2;
out->downmix_tmp_buf = aml_audio_malloc(out->downmix_tmp_buf_size);
out->float_to_s32_tmp_buf_size = 2048 * audio_bytes_per_sample(AUDIO_FORMAT_PCM_32_BIT) * 2;
out->float_to_s32_tmp_buf = aml_audio_malloc(out->float_to_s32_tmp_buf_size);
/*if tunnel mode pcm is not 48Khz, resample to 48K*/
if (out->stream_type == STREAM_MAIN_INPUT) {
ALOGD("%s format=%d rate=%d", __func__, out->hal_internal_format, out->config.rate);
if (audio_is_linear_pcm(out->hal_internal_format) && out->config.rate != 48000) {
ALOGI("init resampler from %d to 48000!\n", out->config.rate);
out->aml_resample.input_sr = out->config.rate;
out->aml_resample.output_sr = 48000;
out->aml_resample.channels = 2;
resampler_init (&out->aml_resample);
/*max buffer from 32K to 48K*/
if (!out->resample_outbuf) {
out->resample_outbuf = (unsigned char*) aml_audio_malloc (8192 * 10);
if (!out->resample_outbuf) {
ALOGE ("malloc buffer failed\n");
ret = -1;
goto err;
}
}
} else {
if (out->resample_outbuf)
aml_audio_free(out->resample_outbuf);
out->resample_outbuf = NULL;
}
if (adev->is_netflix) {
adev->gap_passthrough_state = GAP_PASSTHROUGH_STATE_IDLE;
adev->gap_offset = 0;
}
}
out->eos_ease_timer = aml_audio_timer_create(eos_timer_callback_handler, (void *)out);
*stream_out = &out->stream;
AM_LOGD("exit!!");
return 0;
err:
llp_deinit(adev);
if (out->avsync_ctx)
aml_audio_free(out->avsync_ctx);
aml_audio_free(out);
ALOGE("%s exit failed =%d", __func__, ret);
return ret;
}
static void close_ms12_output_main_stream(struct audio_stream_out *stream) {
struct aml_stream_out *out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = out->dev;
/*main stream is closed, close the ms12 main decoder*/
if (out->is_ms12_main_decoder) {
pthread_mutex_lock(&adev->ms12.lock);
/*after ms12 lock, dolby_ms12_enable may be cleared with clean up function*/
if (adev->ms12.dolby_ms12_enable) {
adev->ms12.need_ms12_resume = false;
adev->ms12.need_resync = 0;
audiohal_send_msg_2_ms12(&adev->ms12, MS12_MESG_TYPE_FLUSH);
/*coverity[missing_lock]*/
adev->ms12.ms12_resume_state = MS12_RESUME_FROM_CLOSE;
audiohal_send_msg_2_ms12(&adev->ms12, MS12_MESG_TYPE_RESUME);
}
/*coverity[double_unlock]*/
pthread_mutex_unlock(&adev->ms12.lock);
/*main stream is closed, wait mesg processed*/
{
int wait_cnt = 0;
while (!ms12_msg_list_is_empty(&adev->ms12)) {
aml_audio_sleep(5000);
wait_cnt++;
if (wait_cnt >= 200) {
break;
}
}
AM_LOGI("main stream message is processed cost =%d ms", wait_cnt * 5);
}
if (out->pheader) {
/*we only support one stream hw sync and MS12 always attach with it.
So when it is released, ms12 also need set pheader to NULL*/
out->need_sync = 0;
}
dolby_ms12_main_close(stream);
out->is_ms12_main_decoder = false;
}
return;
}
//static int out_standby_new(struct audio_stream *stream);
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct aml_stream_out *out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
if (dev == NULL || stream == NULL) {
ALOGE("%s: Input parameter error!!!", __func__);
return;
}
AM_LOGI("enter: dev(%p) stream(%p) stream ID(%d)", dev, stream, out->unique_ID);
if (out->aml_dec) {
aml_decoder_release(out->aml_dec);
out->aml_dec = NULL;
}
stream->common.standby(&stream->common);
pthread_mutex_lock(&adev->audio_mixer_lock);
if (out->inputPortID != -1 && adev->useSubMix) {
delete_mixer_input_port(adev->audio_mixer, out->inputPortID);
out->inputPortID = -1;
}
pthread_mutex_unlock(&adev->audio_mixer_lock);
pthread_mutex_lock(&out->lock);
aml_audio_timer_delete(out->eos_ease_timer);
if (out->is_ms12_main_decoder) {
close_ms12_output_main_stream(stream);
}
if (out->spdifenc_init) {
aml_spdif_encoder_close(out->spdifenc_handle);
out->spdifenc_handle = NULL;
out->spdifenc_init = false;
}
if (out->avsync_ctx) {
pthread_mutex_lock(&(out->avsync_ctx->lock));
if (out->avsync_ctx->msync_ctx) {
if (out->avsync_ctx->msync_ctx->msync_session) {
msync_unblock_start(out->avsync_ctx->msync_ctx);
av_sync_destroy(out->avsync_ctx->msync_ctx->msync_session);
}
aml_audio_free(out->avsync_ctx->msync_ctx);
out->avsync_ctx->msync_ctx = NULL;
}
if (out->avsync_ctx->mediasync_ctx) {
if (out->avsync_ctx->mediasync_ctx->handle) {
mediasync_wrap_destroy(out->avsync_ctx->mediasync_ctx->handle);
}
aml_audio_free(out->avsync_ctx->mediasync_ctx);
out->avsync_ctx->mediasync_ctx = NULL;
}
pthread_mutex_unlock(&(out->avsync_ctx->lock));
aml_audio_free(out->avsync_ctx);
out->avsync_ctx = NULL;
}
#ifdef USE_MEDIAINFO
if (adev->minfo_h) {
if (adev->info_rid != -1) {
minfo_retrieve_record(adev->minfo_h, adev->info_rid);
adev->info_rid = -1;
adev->minfo_atmos_flag = -1;
}
}
#endif
//#ifdef ENABLE_MMAP
#ifndef BUILD_LINUX
if (out->stream_type == STREAM_APP_INPUT) {
outMmapDeInit(out);
}
#endif
if (out->restore_hdmitx_selection) {
/* switch back to spdifa when the dual stream is done */
aml_audio_select_spdif_to_hdmi(AML_SPDIF_A_TO_HDMITX);
out->restore_hdmitx_selection = false;
}
if (out->resample_outbuf) {
aml_audio_free(out->resample_outbuf);
out->resample_outbuf = NULL;
}
if (out->aml_parser) {
aml_audio_parser_close(out->aml_parser);
out->aml_parser = NULL;
}
if (out->pheader) {
aml_audio_free(out->pheader);
out->pheader = NULL;
}
if (adev->dolby_lib_type != eDolbyMS12Lib) {
aml_audio_spdif_output_close_all();
}
if (out->resample_handle) {
aml_audio_resample_close(out->resample_handle);
out->resample_handle = NULL;
}
if (out->scaletempo) {
ALOGI("%s %d, release scaletempo %p", __FUNCTION__, __LINE__, out);
out->enable_scaletempo = false;
hal_scaletempo_release(out->scaletempo);
out->scaletempo = NULL;
}
deinit_aml_pcm_downmix(&out->pcm_downmix);
if (out->downmix_tmp_buf) {
aml_audio_free(out->downmix_tmp_buf);
out->downmix_tmp_buf = NULL;
}
if (out->float_to_s32_tmp_buf) {
aml_audio_free(out->float_to_s32_tmp_buf);
out->float_to_s32_tmp_buf = NULL;
}
if (out->clip_meta) {
hal_clip_meta_release(out->clip_meta);
out->clip_meta = NULL;
out->clip_offset = 0;
//dolby_ms12_register_callback_by_type(CALLBACK_CLIP_META, NULL, NULL);
}
if (out->mpegh_uimanager_handle) {
aml_mpegh_close(adev, out->mpegh_uimanager_handle);
AM_LOGI("mpegh stream will close");
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_HAL_FORMAT, TYPE_MPEGH_CLOSE);
out->mpegh_uimanager_handle = NULL;
}
llp_deinit(adev);
out->need_sync = false;
out->with_header = false;
if (out->stream_type == STREAM_MAIN_INPUT) {
//clear the hal format when main close.
adev->audio_hal_info.update_type = TYPE_PCM;
adev->audio_hal_info.is_decoding = false;
adev->audio_hal_info.first_decoding_frame = false;
adev->audio_hal_info.dts_type_is_updated = false;
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_HAL_FORMAT, TYPE_PCM);
}
if (out->stream_type == STREAM_AUX_INPUT && out->is_airplay) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_HAL_FORMAT, TYPE_PCM);
AM_LOGI("airplay stream closed, trigger uevent");
}
pthread_mutex_unlock(&out->lock);
if (out->stream_type == STREAM_HWMIXER_INPUT) {
aml_audio_hwmixer_out_close(adev);
}
if (adev->ms12.dolby_ms12_enable) {
if (!adev->low_latency_mode) //inject Sometimes open->close->open, this action will cancel the response lowlatency
set_ms12_alsa_limit_frame(&adev->ms12, MS12_ALSA_DEFAULT_LIMIT_FRAME);
if (out->stream_type == STREAM_APP_INPUT) {
set_ms12_app_buffer_flush(&adev->ms12, true);
AM_LOGI("flush ms12 app buffer");
} else if (out->stream_type == STREAM_AUX_INPUT) {
set_ms12_system_buffer_flush(&adev->ms12, true);
AM_LOGI("flush ms12 system buffer");
}
}
adev->audio_low_latency = false;
//we muted in audio_header_find_frame when find eos, here recover mute
if (adev->dolby_lib_type != eDolbyMS12Lib && out->stream_type != STREAM_AD_INPUT) {
set_aed_master_volume_mute(&adev->alsa_mixer, false);
}
if (out->stream_type == STREAM_APP_INPUT) {
out_set_volume(stream, 0.0f, 0.0f);
}
/*the dolby lib is changed, so we need restore it*/
if (out->restore_dolby_lib_type) {
adev->dolby_lib_type = adev->dolby_lib_type_last;
if (adev->effect_ctrl.effect_mode == EFFECT_MODE_DAP && is_dts_format(out->hal_internal_format)) {
if (adev->ms12.dap_only_enable) {
aml_dap_close(&adev->ms12);
}
}
AM_LOGI("restore dolby lib =%d, audio_mixer %p", adev->dolby_lib_type, adev->audio_mixer);
/* delete submix after adev->dolby_lib_type restore to eDolbyMS12Lib*/
pthread_mutex_lock(&adev->audio_mixer_lock);
if (eDolbyMS12Lib == adev->dolby_lib_type && adev->audio_mixer) {
deleteHalSubMixing(adev);
adev->useSubMix = false;
pthread_mutex_unlock(&adev->audio_mixer_lock);
int ret = adev_ms12_prepare((struct audio_hw_device *)adev);
if (0 != ret) {
ALOGE("%s, adev_ms12_prepare fail!\n", __func__);
}
} else {
pthread_mutex_unlock(&adev->audio_mixer_lock);
}
}
if (adev->hw_mixer.start_buf && out->stream_type == STREAM_MAIN_INPUT) {
aml_hw_mixer_deinit(&adev->hw_mixer);
}
if (adev->enable_deep_unit && out->stream_type == STREAM_DEEP_INPUT) {
aml_port_command(out->deep_inport, AUDIO_PORT_FLUSH, NULL);
audio_unit_release_port(adev->deep_unit, aml_port_get_ID(out->deep_inport), AUDIO_PORT_INPUT);
}
if (out->stream_type == STREAM_MAIN_INPUT && adev->native_postprocess.effect_in_chmask != AML_DTS_CHANNEL_MASK_2_0) {
adev->native_postprocess.effect_in_chmask = AML_DTS_CHANNEL_MASK_2_0;
}
if (STREAM_MAIN_INPUT == out->stream_type) {
aml_restore_output_device_to_mixer_by_id(out->decoder_id);
}
if (stream) {
aml_audio_free(stream);
stream = NULL;
}
AM_LOGI("exit!!");
}
static void adev_update_active_outport(struct aml_audio_device *adev, audio_devices_t output_destination) {
enum OUT_PORT outport = OUTPORT_SPEAKER;
ALOGI("[%s:%d] output_destination=%#x", __func__, __LINE__, output_destination);
switch (output_destination) {
case (AUDIO_DEVICE_OUT_REMOTE_SUBMIX):
outport = OUTPORT_REMOTE_SUBMIX;
break;
case (AUDIO_DEVICE_OUT_WIRED_HEADPHONE):
outport = OUTPORT_HEADPHONE;
break;
case (AUDIO_DEVICE_OUT_HDMI_ARC):
outport = OUTPORT_HDMI_ARC;
break;
case (AUDIO_DEVICE_OUT_SPEAKER):
case (AML_AUDIO_DEVICE_OUT_EXTERNAL_SPEAKER):
outport = OUTPORT_SPEAKER;
break;
case (AUDIO_DEVICE_OUT_HDMI):
outport = OUTPORT_HDMI;
break;
case (AUDIO_DEVICE_OUT_SPDIF):
outport = OUTPORT_SPDIF;
break;
case (AUDIO_DEVICE_OUT_LINE):
outport = OUTPORT_AUX_LINE;
break;
case (AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET):
outport = OUTPORT_BT_SCO_HEADSET;
break;
case (AUDIO_DEVICE_OUT_USB_HEADSET):
outport = OUTPORT_USB_HEADSET;
break;
default:
outport = OUTPORT_MAX;
break;
}
if (adev->active_outport != outport) {
adev->active_outport_changed = true;
/* When active_outport switche to ARC, it will disable dap,
When active_outport switche to speaker, need to restore */
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if (outport == OUTPORT_SPEAKER) {
set_ms12_full_dap_disable(&adev->ms12, false);
} else if (outport == OUTPORT_HDMI_ARC) {
set_ms12_full_dap_disable(&adev->ms12, true);
}
}
if (adev->hwmixer_manager) {
aml_audio_hwmixer_set_outport(adev, outport);
}
}
adev->active_outport = outport;
AM_LOGI("active_outport:%d", adev->active_outport);
}
static int aml_audio_output_routing(struct audio_hw_device *dev,
enum OUT_PORT outport,
bool user_setting)
{
struct aml_audio_device *aml_dev = (struct aml_audio_device *)dev;
if (aml_dev->active_outport != outport) {
ALOGI("%s: switch from %s to %s", __func__,
outputPort2Str(aml_dev->active_outport), outputPort2Str(outport));
/* switch off the active output */
switch (aml_dev->active_outport) {
case OUTPORT_SPEAKER:
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_SPEAKER, false);
break;
case OUTPORT_HDMI_ARC:
set_output_device_avail(aml_dev, AUDIO_DEVICE_OUT_HDMI_ARC, false);
set_output_device_mute(aml_dev, AUDIO_DEVICE_OUT_HDMI_ARC, true, false);
break;
case OUTPORT_HEADPHONE:
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, false);
break;
case OUTPORT_A2DP:
break;
case OUTPORT_BT_SCO:
case OUTPORT_BT_SCO_HEADSET:
AM_LOGI("to do !!!!!");
break;
default:
ALOGW("%s: pre active_outport:%d unsupport", __func__, aml_dev->active_outport);
break;
}
/* switch on the new output */
switch (outport) {
case OUTPORT_SPEAKER:
if (!aml_dev->speaker_mute) {
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_SPEAKER, true);
}
break;
case OUTPORT_HDMI_ARC:
set_output_device_avail(aml_dev, AUDIO_DEVICE_OUT_HDMI_ARC, true);
set_output_device_mute(aml_dev, AUDIO_DEVICE_OUT_HDMI_ARC, false, false);
break;
case OUTPORT_HEADPHONE:
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, true);
break;
case OUTPORT_A2DP:
case OUTPORT_BT_SCO:
case OUTPORT_BT_SCO_HEADSET:
break;
default:
ALOGW("%s: cur outport:%d unsupport", __func__, outport);
break;
}
aml_dev->active_outport = outport;
} else if (outport == OUTPORT_SPEAKER && user_setting) {
/* In this case, user toggle the speaker_mute menu */
if (aml_dev->speaker_mute) {
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_SPEAKER, false);
}
else {
do_output_device_routing(aml_dev, AUDIO_DEVICE_OUT_SPEAKER, true);
}
} else {
ALOGI("%s: outport %s already exists, do nothing", __func__, outputPort2Str(outport));
}
return 0;
}
static int set_device_output_policy(struct aml_audio_device *adev, bool enable)
{
bool is_arc_connected = enable;
int out_port = adev->active_outport;
set_output_device_mute(adev, AUDIO_DEVICE_OUT_HDMI_ARC, !enable, false/*no fade*/);
/*
* when ARC is connecting, and user switch [Sound Output Device] to "ARC"
* we need to set out_port as OUTPORT_HDMI_ARC ,
* the aml_audio_output_routing() will "UNmute" ARC and "mute" speaker.
*/
if (is_arc_connected) {
out_port = OUTPORT_HDMI_ARC;
}
/*
* when ARC is connecting, and user switch [Sound Output Device] to "speaker"
* we need to set out_port as OUTPORT_SPEAKER ,
* the aml_audio_output_routing() will "mute" ARC and "unmute" speaker.
*/
if (!is_arc_connected) {
out_port = OUTPORT_SPEAKER;
}
ALOGI("[%s:%d] out_port:%s", __func__, __LINE__, outputPort2Str(out_port));
aml_audio_output_routing((struct audio_hw_device *)adev, out_port, true);
return 0;
}
static void adev_set_karaoke_vol (struct aml_audio_device *adev, float vol) {
pthread_mutex_lock (&adev->active_outputs_lock);
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
struct audio_stream_out *out = (struct audio_stream_out *)aml_out;
if (aml_out && aml_out->stream_type == STREAM_APP_INPUT) {
out_set_volume_l(out, vol, vol);
AM_LOGI("set karaoke vol to %f", vol);
}
}
pthread_mutex_unlock (&adev->active_outputs_lock);
}
static int aml_audio_update_arc_status(struct aml_audio_device *adev, bool enable)
{
ALOGI("[%s:%d] set audio hdmi arc status:%d", __func__, __LINE__, enable);
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
if (enable) {
restore_hdmi_desc(adev);
} else {
memset(hdmi_desc, 0, sizeof(struct aml_arc_hdmi_desc));
hdmi_desc->pcm_fmt.fmt = AML_HDMI_FORMAT_LPCM;
hdmi_desc->dts_fmt.fmt = AML_HDMI_FORMAT_DTS;
hdmi_desc->dtshd_fmt.fmt = AML_HDMI_FORMAT_DTSHD;
hdmi_desc->dd_fmt.fmt = AML_HDMI_FORMAT_AC3;
hdmi_desc->ddp_fmt.fmt = AML_HDMI_FORMAT_DDP;
hdmi_desc->mat_fmt.fmt = AML_HDMI_FORMAT_MAT;
}
/*
* when user switch UI setting, means output device changed,
* use "arc_hdmi_updated" paramter to notify HDMI ARC have updated status
* while in config_output(), it detects this change, then re-route output config.
*/
set_arc_hdmi_updated(adev, true);
/*
* when arc connect, need umute arc and mute speaker for roku, but yocto trunk don't need;
* when arc disconnect, need mute arc and unmute speaker for roku, but yocto trunk don't need;
* which device output is controlled by platform_server for yocto trunk, audiohal don't need control.
*/
set_device_output_policy(adev, enable);
return 0;
}
static void check_usb_card_device(struct str_parms *parms, int device)
{
/*usb audio hot plug need delay some time wait alsa file create */
if ((device & AUDIO_DEVICE_OUT_ALL_USB) || (device & AUDIO_DEVICE_IN_ALL_USB)) {
int card = 0, alsa_dev = 0, val, retry;
char fn[256];
int ret = str_parms_get_int(parms, "card", &val);
if (ret >= 0) {
card = val;
}
ret = str_parms_get_int(parms, "device", &val);
if (ret >= 0) {
alsa_dev = val;
}
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, alsa_dev,
device & AUDIO_DEVICE_OUT_ALL_USB ? 'p' : 'c');
for (retry = 0; access(fn, F_OK) < 0 && retry < 10; retry++) {
usleep (20000);
}
if (access(fn, F_OK) < 0 && retry >= 10) {
ALOGE("usb audio create alsa file time out,need check \n");
}
}
}
static void set_device_connect_state(struct aml_audio_device *adev, struct str_parms *parms, int device, bool state)
{
AM_LOGI("state:%d, dev:%#x, pre_out:%#x, pre_in:%#x", state, device, adev->out_device, adev->in_device);
if (state) {
check_usb_card_device(parms, device);
if (audio_is_output_device(device)) {
if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) || (device & AUDIO_DEVICE_OUT_HDMI)) {
set_output_device_avail(adev, device, true);
clear_arc_cached_edid(adev);
if (device & AUDIO_DEVICE_OUT_HDMI_ARC) {
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/*when arc is connected, disable dap*/
set_ms12_full_dap_disable(&adev->ms12, true);
}
aml_audio_update_arc_status(adev, true);
}
#ifdef BUILD_LINUX
if ((device & AUDIO_DEVICE_OUT_HDMI) && is_STB(adev)) {
set_output_device_mute(adev, AUDIO_DEVICE_OUT_SPEAKER, true, false);
adev->active_outport = OUTPORT_HDMI;
set_HDMI_reconnected_flag(adev, true);
}
#endif
update_sink_format_after_hotplug(adev);
} else if (device & AUDIO_DEVICE_OUT_ALL_A2DP) {
adev->a2dp_updated = 1;
adev->out_device |= device;
//a2dp_out_open(adev);
} else if (device & AUDIO_DEVICE_OUT_ALL_USB ||
device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
adev->out_device |= device;
} else if (device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
adev->out_device |= device;
}
}
} else {
if (audio_is_output_device(device)) {
if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) || (device & AUDIO_DEVICE_OUT_HDMI)) {
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
set_output_device_avail(adev, device, false);
adev->is_hdmi_arc_interact_done = false;
clear_arc_cached_edid(adev);
hdmi_desc->pcm_fmt.max_channels = 2;
hdmi_desc->mat_fmt.MAT_PCM_48kHz_only = false;
if (device & AUDIO_DEVICE_OUT_HDMI_ARC) {
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/*when arc is connected, disable dap*/
set_ms12_full_dap_disable(&adev->ms12, false);
}
aml_audio_update_arc_status(adev, false);
}
#ifdef BUILD_LINUX
if ((device & AUDIO_DEVICE_OUT_HDMI) && is_STB(adev)) {
adev->active_outport = OUTPORT_SPEAKER;
set_output_device_mute(adev, AUDIO_DEVICE_OUT_SPEAKER, false, false);
set_HDMI_reconnected_flag(adev, true);
}
#endif
update_sink_format_after_hotplug(adev);
} else if (device & AUDIO_DEVICE_OUT_ALL_A2DP) {
adev->a2dp_updated = 1;
adev->out_device &= (~device);
//a2dp_out_close(adev);
} else if (device & AUDIO_DEVICE_OUT_ALL_USB ||
device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE||
device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
adev->out_device &= (~device);
} else if (device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
adev->out_device &= (~device);
}
}
}
}
static int adev_set_master_volume (struct audio_hw_device *dev, float volume);
static int adev_set_parameters (struct audio_hw_device *dev, const char *kvpairs)
{
AM_LOGD("%p, %s", dev, kvpairs);
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
struct str_parms *parms;
struct dolby_ms12_desc *ms12 = &(adev->ms12);
char value[AUDIO_HAL_CHAR_MAX_LEN] = {0};
int val = 0;
int ret = 0;
float val_f = 0.0;
AM_LOGI ("kv: %s",kvpairs);
parms = str_parms_create_str (kvpairs);
ret = str_parms_get_str (parms, "screen_state", value, sizeof (value) );
if (ret >= 0) {
if (strcmp (value, AUDIO_PARAMETER_VALUE_ON) == 0) {
adev->low_power = false;
} else {
adev->low_power = true;
}
goto exit;
}
ret = str_parms_get_float (parms, "noise_gate_level", &val_f);
if (ret >= 0) {
struct component_noise_gate *noise_gate = get_noise_gate_instance(adev);
if (noise_gate->aml_ng_enable && (noise_gate->aml_ng_level != val_f)) {
aml_noise_gate_deinit(noise_gate);
noise_gate->aml_ng_level = val_f;
aml_noise_gate_init(noise_gate);
}
AM_LOGI ("aml_ng_enable=%s, aml_ng_level set to %f", noise_gate->aml_ng_enable ? "enable":"disable", noise_gate->aml_ng_level);
goto exit;
}
ret = str_parms_get_int (parms, "disable_pcm_mixing", &val);
if (ret >= 0) {
adev->disable_pcm_mixing = val;
AM_LOGI ("ms12 disable_pcm_mixing set to %d", adev->disable_pcm_mixing);
goto exit;
}
ret = str_parms_get_int(parms, "Audio hdmi-out mute", &val);
if (ret >= 0) {
set_output_device_mute(adev, AUDIO_DEVICE_OUT_HDMI, val, false);
AM_LOGI("set Audio hdmi-out mute status: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "Audio spdif mute", &val);
if (ret >= 0) {
set_output_device_mute(adev, AUDIO_DEVICE_OUT_SPDIF, val, false);
AM_LOGI("set SPDIF mute status: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "AED master volume", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_EQ_MASTER_VOLUME, val);
AM_LOGI("audio master volume : %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "AED channel volume", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_EQ_LCH_VOLUME, val);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_EQ_RCH_VOLUME, val);
AM_LOGI("audio channel volume : %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "Master volume", &val);
if (ret >= 0) {
if (is_STB(adev)) {
int dac_vol[2] = {val, val};
aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_DAC_DIGITAL_VOLUME, &dac_vol, 2);
} else if (is_SBR(adev)) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AD82128_C30_MASTER_VOLUME, val);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AD82128_C31_MASTER_VOLUME, val);
} else{
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AMP_MASTER_VOLUME, val);
}
AM_LOGI("audio master volume: %d", val);
goto exit;
}
// HDMI cable plug off
ret = str_parms_get_int(parms, "disconnect", &val);
if (ret >= 0) {
set_device_connect_state(adev, parms, val, false);
goto exit;
}
// HDMI cable plug in
ret = str_parms_get_int(parms, "connect", &val);
if (ret >= 0) {
set_device_connect_state(adev, parms, val, true);
if (val & AUDIO_DEVICE_OUT_HDMI_ARC) {
adev->restart_device_flag = true;
}
goto exit;
}
// HDMI plug in and UI [Sound Output Device] set to "ARC" will recieve HDMI ARC Switch = 1
// HDMI plug off and UI [Sound Output Device] set to "speaker" will recieve HDMI ARC Switch = 0
ret = str_parms_get_int(parms, "HDMI ARC Switch", &val);
if (ret >= 0) {
aml_audio_update_arc_status(adev, (val != 0));
if (0 == val && adev->stream_write_data)
set_ms12_full_dap_disable(&adev->ms12, false);
goto exit;
}
ret = str_parms_get_str(parms, "hal_param_digital_sink_config_dts_capability", value, sizeof(value));
if (ret >= 0) {
adev->auto_test_debug_flag = true;
if (strcmp(value, "dtshd") == 0) {
adev->sink_configured_dts_capability = AUDIO_FORMAT_DTS_HD;
//dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1 | DTSX_SINK_SUPPORTS_P2);
AM_LOGI("sink_configured_dts_capability is dtshd.");
} else if (strcmp(value, "dts") == 0) {
adev->sink_configured_dts_capability = AUDIO_FORMAT_DTS;
//dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_CA);
AM_LOGI("sink_configured_dts_capability is dts.");
} else if (strcmp(value, "pcm") == 0) {
adev->sink_configured_dts_capability = AUDIO_FORMAT_PCM;
AM_LOGI("sink_configured_dts_capability is pcm.");
} else {
AM_LOGI("unknown kvpairs");
goto exit;
}
adev->hdmi_format_updated = 1;
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_earctx_earc_mode", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_EARC_TX_EARC_MODE, val);
AM_LOGI("eARC_TX eARC Mode: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_arc_earc_tx_enable", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_ARC_EARC_TX_ENABLE, val);
AM_LOGI("ARC eARC TX enable: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "spdifin/arcin switch", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIFIN_ARCIN_SWITCH, val);
AM_LOGI("audio source: %s", val?"ARCIN":"SPDIFIN");
goto exit;
}
//add for fireos tv for Dolby audio setting
ret = str_parms_get_int (parms, "hdmi_format", &val);
if (ret >= 0 ) {
ret = set_hdmi_format(adev, val);
if (ret < 0) {
AM_LOGE("An error occurred during setting hdmi_format!");
return ret;
}
goto exit;
}
ret = str_parms_get_int (parms, "spdif_format", &val);
if (ret >= 0 ) {
adev->spdif_format = val;
AM_LOGI("S/PDIF format: %d", adev->spdif_format);
goto exit;
}
ret = str_parms_get_str(parms, "hal_param_digital_sink_config_dolby_capability", value, sizeof(value));
if (ret >= 0) {
adev->auto_test_debug_flag = true;
if (strcmp(value, "mat") == 0) {
adev->sink_configured_dolby_capability = AUDIO_FORMAT_MAT;
AM_LOGI("sink_configured_dolby_capability is MAT.");
} else if (strcmp(value, "ddp") == 0) {
adev->sink_configured_dolby_capability = AUDIO_FORMAT_E_AC3;
AM_LOGI("sink_configured_dolby_capability is DDP.");
} else if (strcmp(value, "dd") == 0) {
adev->sink_configured_dolby_capability = AUDIO_FORMAT_AC3;
AM_LOGI("sink_configured_dolby_capability is DD.");
} else if (strcmp(value, "pcm") == 0) {
adev->sink_configured_dolby_capability = AUDIO_FORMAT_PCM_SUB_16_BIT;
AM_LOGI("sink_configured_dolby_capability is PCM.");
} else {
AM_LOGI("unknown kvpairs");
goto exit;
}
adev->hdmi_format_updated = 1;
goto exit;
}
//"digital_output_format=pcm"
//"digital_output_format=dd"
//"digital_output_format=ddp"
//"digital_output_format=auto"
//"digital_output_format=bypass", the same as "disable_pcm_mixing"
ret = str_parms_get_str(parms, "digital_output_format", value, sizeof(value));
if (ret >= 0) {
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
int hdmi_format = PCM;
if (strcmp(value, "pcm") == 0) {
hdmi_format = PCM;
adev->disable_pcm_mixing = 0;
AM_LOGI("digital_output_format is PCM");
} else if (strcmp(value, "ddp") == 0) {
hdmi_format = DDP;
hdmi_desc->ddp_fmt.is_support = true;
hdmi_desc->dd_fmt.is_support = true;
adev->disable_pcm_mixing = 0;
AM_LOGI("digital_output_format is DDP");
} else if (strcmp(value, "dd") == 0) {
hdmi_format = DD;
adev->disable_pcm_mixing = 0;
hdmi_desc->dd_fmt.is_support = true;
hdmi_desc->ddp_fmt.is_support = false;
AM_LOGI("digital_output_format is DD");
} else if (strcmp(value, "auto") == 0) {
hdmi_format = AUTO;
adev->disable_pcm_mixing = 0;
AM_LOGI("digital_output_format is AUTO");
} else if (strcmp(value, "bypass") == 0) {
//make sink caps as MAX for roku
hdmi_format = BYPASS;
hdmi_desc->ddp_fmt.is_support = true;
hdmi_desc->dd_fmt.is_support = true;
hdmi_desc->dts_fmt.is_support = true;
adev->disable_pcm_mixing = 1;
/* earc AVR connected, we support MAT output*/
if ((ATTEND_TYPE_EARC == aml_audio_earctx_get_type(adev)) && (hdmi_desc->mat_fmt.atmos_supported == true)) {
hdmi_desc->mat_fmt.is_support = true;
} else {
hdmi_desc->mat_fmt.is_support = false;
}
AM_LOGI("digital_output_format is bypass");
} else if (strcmp(value, "mat") == 0) {
hdmi_format = DIGITAL_OUTPUT_MAT;
hdmi_desc->ddp_fmt.is_support = true;
hdmi_desc->dd_fmt.is_support = true;
hdmi_desc->mat_fmt.is_support = true;
ALOGI("digital_output_format is mat");
} else if (strcmp(value, "dts") == 0) {
hdmi_format = DIGITAL_OUTPUT_DTS;
hdmi_desc->dtshd_fmt.is_support = false;
hdmi_desc->dts_fmt.is_support = true;
hdmi_desc->dts_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_CA;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_CA);
ALOGI("digital_output_format is dts");
} else if (strcmp(value, "dtshd") == 0) {
hdmi_format = DIGITAL_OUTPUT_DTSHD;
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA);
ALOGI("digital_output_format is dtshd");
} else if (strcmp(value, "dtsx_p1") == 0) {
hdmi_format = DIGITAL_OUTPUT_DTSX;
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1);
ALOGI("digital_output_format is dtsx");
} else if (strcmp(value, "dtsx_p2") == 0) {
hdmi_format = DIGITAL_OUTPUT_DTSX;
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1 | DTSX_SINK_SUPPORTS_P2;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1 | DTSX_SINK_SUPPORTS_P2);
ALOGI("digital_output_format is dtsx");
} else {
AM_LOGI("unknown kvpairs");
}
if (adev->hdmi_format != hdmi_format)
adev->hdmi_format_updated = 1;
adev->hdmi_format = hdmi_format;
AM_LOGI("HDMI format: %d", adev->hdmi_format);
goto exit;
}
ret = str_parms_get_str(parms, "dtsx_sink_dev_type", value, sizeof(value));
if (ret >= 0) {
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
if (strcmp(value, "dts") == 0) {
hdmi_desc->dtshd_fmt.is_support = false;
hdmi_desc->dts_fmt.is_support = true;
hdmi_desc->dts_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_CA;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_CA);
AM_LOGI("dtsx_sink_dev_type is dts");
} else if (strcmp(value, "dtshd") == 0) {
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA);
AM_LOGI("dtsx_sink_dev_type is dtshd");
} else if (strcmp(value, "dtsx_p1") == 0) {
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1);
AM_LOGI("dtsx_sink_dev_type is dtsx");
} else if (strcmp(value, "dtsx_p2") == 0) {
hdmi_desc->dtshd_fmt.is_support = true;
hdmi_desc->dtshd_fmt.dts_vsdb_byte3 = DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1 | DTSX_SINK_SUPPORTS_P2;
dtsx_set_sink_dev_type(DTSX_SINK_SUPPORTS_MA | DTSX_SINK_SUPPORTS_P1 | DTSX_SINK_SUPPORTS_P2);
AM_LOGI("dtsx_sink_dev_type is dtsx");
} else {
AM_LOGE("unknown kvpairs");
}
goto exit;
}
//add for roku
ret = str_parms_get_str(parms,"av_noisegate", value, sizeof(value));
if (ret >= 0 ) {
struct component_noise_gate *noise_gate = get_noise_gate_instance(adev);
ret = sscanf(value, "%d %f", &noise_gate->aml_ng_enable, &noise_gate->aml_ng_level);
AM_LOGI(" AV noise gate:%s, noise gate level:%f db", noise_gate->aml_ng_enable? "enable" : "disable" , noise_gate->aml_ng_level);
goto exit;
}
//For ott support soundbar project, using Enable Soundbar Mode UI to switch soundbar or OTT mode.
ret = str_parms_get_int(parms, "hal_param_soundbar_mode", &val);
if (ret >= 0) {
bool enable = (val != 0) ? true : false;
AM_LOGI("enable %d adev->enable_soundbar_mode %d\n", enable, adev->enable_soundbar_mode);
if (adev->enable_soundbar_mode != enable) {
adev->enable_soundbar_mode = enable;
if (adev->enable_soundbar_mode) {
adev->default_alsa_ch = aml_audio_get_default_alsa_output_ch();
} else {
adev->default_alsa_ch = 2;
}
if (!enable) {
/* enable STB and disable SBR mode*/
adev_update_active_outport(adev, (audio_devices_t)AUDIO_DEVICE_OUT_HDMI);
set_output_device_mute(adev, (audio_devices_t)AUDIO_DEVICE_OUT_HDMI, false, 0);
set_output_device_mute(adev, (audio_devices_t)AUDIO_DEVICE_OUT_SPEAKER, true, 0);
set_output_device_mute(adev, (audio_devices_t)AML_AUDIO_DEVICE_OUT_EXTERNAL_SPEAKER, true, 0);
convert_stb_to_sbr(adev, false);
} else {
/* enable SBR and disable STB mode*/
adev_update_active_outport(adev, (audio_devices_t)AML_AUDIO_DEVICE_OUT_EXTERNAL_SPEAKER);
set_output_device_mute(adev, (audio_devices_t)AUDIO_DEVICE_OUT_HDMI, true, 0);
set_output_device_mute(adev, (audio_devices_t)AUDIO_DEVICE_OUT_SPEAKER, false, 0);
convert_stb_to_sbr(adev, true);
}
aml_audio_pcm_out_close(adev);
if (ms12->dolby_ms12_init_flags) {
set_ms12_full_dap_disable(&adev->ms12, !adev->enable_soundbar_mode);
}
// only s7d(S905X5M) soundbar use the conflict alsa device.
// adev->is_alsa_device_conflict = adev->board_config.sbr_spk_ott_hbr_same_tdm;
AM_LOGI(" enable_soundbar_mode = %d device status at %s\n", enable, adev->enable_soundbar_mode ? "SBR-Speaker" : "PureOTT-HDMI");
}
goto exit;
}
ret = str_parms_get_str(parms,"tuner_type", value , sizeof(value));
if (ret >= 0) {
AM_LOGI("tuner_type=%s", value);
char var[PROPERTY_VALUE_MAX] = "ro_vendor_platform_digitaltv_standards";
aml_audio_property_set(var, value);
adev->dvb_standards = adev_get_dvb_standards();
/* If ms12 has been initialized, dvb_standards needs to be re-initialized to take effect */
if (ms12->dolby_ms12_init_flags) {
adev_ms12_cleanup(&adev->hw_device);
adev_ms12_prepare(&adev->hw_device);
AM_LOGI(" dvb_standards update, need reset ms12");
}
goto exit;
}
ret = str_parms_get_str(parms, "sink_atoms_capability", value, sizeof(value));
if (ret >= 0) {
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
if (strcmp(value, "true") == 0) {
hdmi_desc->ddp_fmt.atmos_supported = true;
AM_LOGI("atoms enable");
} else {
hdmi_desc->ddp_fmt.atmos_supported = false;
AM_LOGI("atmos disable");
}
goto exit;
}
ret = str_parms_get_int (parms, "hal_param_spdif_output_enable", &val);
if (ret >= 0 ) {
AM_LOGI("[%s:%d] set spdif output enable:%d", __func__, __LINE__, val);
if (val == 0) {
do_output_device_routing(adev, AUDIO_DEVICE_OUT_SPDIF, false);
} else {
do_output_device_routing(adev, AUDIO_DEVICE_OUT_SPDIF, true);
}
adev->spdif_enable = (val == 0) ? false : true;
goto exit;
}
ret = str_parms_get_int(parms, "audio_type", &val);
if (ret >= 0) {
adev->audio_type = val;
AM_LOGI("audio_type: %d", adev->audio_type);
goto exit;
}
ret = str_parms_get_int(parms, "hdmi_is_passthrough_active", &val);
if (ret >= 0 ) {
adev->hdmi_is_pth_active = val;
AM_LOGI("hdmi_is_passthrough_active: %d", adev->hdmi_is_pth_active);
goto exit;
}
ret = str_parms_get_int (parms, "capability:routing", &val);
if (ret >= 0) {
adev->routing = val;
AM_LOGI("capability:routing = %#x", adev->routing);
goto exit;
}
ret = str_parms_get_int (parms, "capability:format", &val);
if (ret >= 0) {
adev->output_config.format = val;
AM_LOGI("capability:format = %#x", adev->output_config.format = val);
goto exit;
}
ret = str_parms_get_int (parms, "capability:channels", &val);
if (ret >= 0) {
adev->output_config.channel_mask = val;
AM_LOGI("capability:channel_mask = %#x", adev->output_config.channel_mask);
goto exit;
}
ret = str_parms_get_int (parms, "capability:sampling_rate", &val);
if (ret >= 0) {
adev->output_config.sample_rate = val;
AM_LOGI("capability:sample_rate = %d", adev->output_config.sample_rate);
goto exit;
}
ret = str_parms_get_int (parms, "ChannelReverse", &val);
if (ret >= 0) {
adev->FactoryChannelReverse = val;
AM_LOGI("ChannelReverse = %d", adev->FactoryChannelReverse);
goto exit;
}
ret = str_parms_get_str (parms, "set_ARC_hdmi", value, sizeof (value) );
if (ret >= 0) {
set_arc_hdmi(dev, value, AUDIO_HAL_CHAR_MAX_LEN);
goto exit;
}
ret = str_parms_get_str (parms, "set_ARC_format", value, sizeof (value) );
if (ret >= 0) {
set_arc_format(dev, value, AUDIO_HAL_CHAR_MAX_LEN);
goto exit;
}
ret = set_tv_source_switch_parameters(dev, parms);
if (ret >= 0) {
AM_LOGI("get TV source param(kv: %s)", kvpairs);
goto exit;
}
// HDMI plug in and UI [Sound Output Device] set to "ARC" will recieve speaker_mute = 1
ret = str_parms_get_int(parms, "speaker_mute", &val);
if (ret >= 0) {
if (val) {
aml_audio_command_delay_mute(adev->aml_command_worker, AUDIO_DEVICE_OUT_SPEAKER, 0);
} else {
aml_audio_command_delay_unmute(adev->aml_command_worker, AUDIO_DEVICE_OUT_SPEAKER, 32);
}
adev->speaker_mute = val;
AM_LOGI("set speaker_mute status: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "usb_spk_mute", &val);
if (ret >= 0) {
#ifdef SUPPORT_EXTERNAL_OUTPUT
if (adev->usb_handle) {
aml_set_external_device_mute(adev->usb_handle, val);
AM_LOGI("set usb_spk_mute status: %d", val);
}
#endif
goto exit;
}
ret = str_parms_get_int(parms, "bt_mute", &val);
if (ret >= 0) {
#ifdef SUPPORT_EXTERNAL_OUTPUT
if (adev->bt_handle) {
aml_set_external_device_mute(adev->bt_handle, val);
AM_LOGI("set bt_mute status: %d", val);
}
#endif
goto exit;
}
ret = str_parms_get_int(parms, "headphone_mute", &val);
if (ret >= 0) {
set_output_device_mute(adev, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, val, false);
AM_LOGI("set headphone_mute status: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "ARC eARC mute", &val);
if (ret >= 0) {
set_output_device_mute(adev, AUDIO_DEVICE_OUT_HDMI_ARC, val, false);
AM_LOGI("set ARC eARC mute status: %d", val);
goto exit;
}
ret = str_parms_get_str(parms, "decoder_mute", value, sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0) {
if (adev->decoder_mute) {
AM_LOGI("decoder is muted, no need to set");
goto exit;
}
adev->decoder_mute = true;
adev_update_decoder_mute_state(dev);
} else if (strncmp(value, "false", 5) == 0) {
if (!adev->decoder_mute) {
AM_LOGI("decoder is unmuted, no need to set");
goto exit;
}
adev->decoder_mute = false;
adev_update_decoder_mute_state(dev);
} else {
AM_LOGE("unsupport decoder_mute value: %s", value);
}
AM_LOGI("decoder_mute set to %d", adev->decoder_mute);
goto exit;
}
// This API can only be used for HDMI,AV and ATV source
ret = str_parms_get_str(parms, "hdmi_av_mute", value, sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0) {
adev->patch_mute = true;
} else if (strncmp(value, "false", 5) == 0) {
adev->patch_mute = false;
} else {
AM_LOGE("unsupport hdmi_av_mute value: %s", value);
}
AM_LOGI("hdmi_av_mute set to %d", adev->patch_mute);
goto exit;
}
// This API can only be used for HDMI,AV,ATV and DTV source
ret = str_parms_get_str(parms, "parental_control_av_mute", value, sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0) {
adev->patch_mute = true;
adev->dtv_mute = true;
} else if (strncmp(value, "false", 5) == 0) {
adev->patch_mute = false;
adev->dtv_mute = false;
} else {
AM_LOGE("unsupport parental_control_av_mute value: %s", value);
}
AM_LOGI("parental_control_av_mute set to %d", adev->patch_mute);
goto exit;
}
// used for get first apts for A/V sync
ret = str_parms_get_str (parms, "first_apts", value, sizeof (value) );
if (ret >= 0) {
unsigned int first_apts = atoi (value);
AM_LOGI("audio set first apts 0x%x", first_apts);
adev->first_apts = first_apts;
adev->first_apts_flag = true;
adev->frame_trigger_thred = 0;
goto exit;
}
//This API is used to set audio only flag in av source without video
//This API should be called after av patch has been created
ret = str_parms_get_int(parms, "av_audio_only", &val);
if (ret >= 0) {
struct aml_audio_patch * audio_patch = get_dev_patch_by_source(adev, SRC_LINEIN);
if (NULL == audio_patch) {
AM_LOGE("AV audio_patch is NULL, can't set av_audio_only");
goto exit;
} else {
audio_patch->av_audio_only = val;
AM_LOGI("AV source set av_audio_only to %d", val);
}
goto exit;
}
ret = str_parms_get_str (parms, "is_netflix", value, sizeof (value) );
if (ret >= 0) {
if (strcmp(value, "true") == 0) {
adev->is_netflix_hide = false;
adev->is_netflix = 1;
} else {
if (adev->is_netflix)
adev->is_netflix_hide = true;
adev->is_netflix = 0;
adev->gap_passthrough_state = GAP_PASSTHROUGH_STATE_IDLE;
adev->gap_offset = 0;
}
AM_LOGI("is_netflix %d(%s) hide:%d", adev->is_netflix, value, adev->is_netflix_hide);
set_ms12_encoder_chmod_locking(&(adev->ms12), adev->is_netflix);
set_ms12_acmod2ch_lock(&(adev->ms12), !adev->is_netflix);
goto exit;
}
ret = str_parms_get_str(parms, "Effect_enable", value, sizeof(value));
if (ret >= 0) {
set_aed_master_volume_mute(&adev->alsa_mixer, true);
if (strncmp(value, "DAP_ON", 6) == 0) {
adev->effect_ctrl.dap_enable = 1;
set_ms12_dap_disable_by_UI(&adev->ms12, 0);
//adev->is_ui_force_dap_disable = false;
} else if (strncmp(value, "DAP_OFF", 7) == 0) {
adev->effect_ctrl.dap_enable = 0;
set_ms12_dap_disable_by_UI(&adev->ms12, 1);
//adev->is_ui_force_dap_disable = true;
} else if (strncmp(value, "VX_ON", 5) == 0) {
#ifdef DTS_VX_V4_ENABLE
if (0 == VirtualX_setparameter(&adev->native_postprocess, VIRTUALX4_PARAM_ENABLE, 1, EFFECT_CMD_SET_PARAM))
#else
if (1 == VirtualX_getparameter(&adev->native_postprocess, DTS_PARAM_VX_ENABLE_I32)
&& 0 == VirtualX_getparameter(&adev->native_postprocess, DTS_PARAM_TSX_PROCESS_DISCARD_I32) )
#endif
{
adev->effect_ctrl.vx_enable = true;
#ifdef DTS_VX_V4_ENABLE
/*
* 1. VIRTUALX4_PARAM_ENABLE: UI toggle for feature enablement
* 2. PARAM_VX_ENABLE_I32: dts vx4 module parameter, value set in ini file
* 3. dts x multichannel output: configurable only when both are enabled
*/
if (1 == VirtualX_getparameter(&adev->native_postprocess, PARAM_VX_ENABLE_I32)) {
adev->effect_ctrl.virtualXlib1_state = 1;
dca_set_out_ch_internal(0);//If vx4 enable successful,config dts x output auto
}
#else
dca_set_out_ch_internal(0);//If vx2 is enabled,config output auto
#endif
} else {
adev->effect_ctrl.vx_enable = false;
dca_set_out_ch_internal(2);//If vx4 enable failed or vx2 is disable, set config output stero
}
} else if (strncmp(value, "VX_OFF", 6) == 0) {
adev->effect_ctrl.vx_enable = false;
#ifdef DTS_VX_V4_ENABLE
VirtualX_setparameter(&adev->native_postprocess,VIRTUALX4_PARAM_ENABLE,0,EFFECT_CMD_SET_PARAM);
#endif
dca_set_out_ch_internal(2);//set vx4 or vx2 disabled,set config output stero
}
if (adev->effect_ctrl.dap_enable && adev->effect_ctrl.vx_enable) {
adev->effect_ctrl.effect_mode = EFFECT_MODE_AUTO;
}
else if (adev->effect_ctrl.dap_enable && !adev->effect_ctrl.vx_enable) {
if (is_TV(adev) && MS12_CONFIG_Y != adev->dolby_ms12_audio_config) {
adev->effect_ctrl.effect_mode = EFFECT_MODE_DAP;
} else {
adev->effect_ctrl.effect_mode = EFFECT_MODE_OFF;
}
}
else if (!adev->effect_ctrl.dap_enable && adev->effect_ctrl.vx_enable) {
adev->effect_ctrl.effect_mode = EFFECT_MODE_VX;
}
else if (!adev->effect_ctrl.dap_enable && !adev->effect_ctrl.vx_enable) {
adev->effect_ctrl.effect_mode = EFFECT_MODE_OFF;
}
adev->native_postprocess.effect_ctrl.effect_mode = adev->effect_ctrl.effect_mode;
aml_audio_sleep(64000);
set_aed_master_volume_mute(&adev->alsa_mixer, false);
ALOGD("set dolby DAP enable %d DTS VX enable %d effect_mode %d ms12_config %d is_TV %d", adev->effect_ctrl.dap_enable,
adev->effect_ctrl.vx_enable, adev->effect_ctrl.effect_mode, adev->dolby_ms12_audio_config, is_TV(adev));
goto exit;
}
/*use dolby_lib_type_last to check ms12 type, because durig playing DTS file,
this type will be changed to dcv*/
if (eDolbyMS12Lib == adev->dolby_lib_type_last) {
ret = str_parms_get_int(parms, "continuous_audio_mode", &val);
if (ret >= 0) {
int disable_continuous = !val;
// if exit netflix, we need disable atmos lock
if (disable_continuous) {
adev->atoms_lock_flag = false;
set_ms12_atmos_lock(&(adev->ms12), adev->atoms_lock_flag);
AM_LOGI("exit netflix, set atmos lock as 0");
}
{
bool acmod2ch_lock = !val;
//when in netflix, we should always keep ddp5.1, exit netflix we can output ddp2ch
set_ms12_encoder_chmod_locking(&(adev->ms12), !acmod2ch_lock);
set_ms12_acmod2ch_lock(&(adev->ms12), acmod2ch_lock);
}
AM_LOGI("ignore the continuous_audio_mode!");
adev->is_netflix = val;
goto exit;
/*Dead Code, didn't use now*/
/*AM_LOGI("continuous_audio_mode set to %d", val);
char buf[PROPERTY_VALUE_MAX] = {0};
char *str = aml_audio_property_get_str(DISABLE_CONTINUOUS_OUTPUT, buf, NULL);
if (str != NULL) {
sscanf(buf, "%d", &disable_continuous);
AM_LOGI("%s[%s] disable_continuous %d", DISABLE_CONTINUOUS_OUTPUT, buf, disable_continuous);
}
pthread_mutex_lock(&adev->lock);
if (continuous_mode(adev) && disable_continuous) {
// If the Netflix application is terminated, your platform must disable Atmos locking.
// For more information, see When to enable/disable Atmos lock.
// The following Netflix application state transition scenarios apply (state definitions described in Always Ready):
// Running->Not Running
// Not Running->Running
// Hidden->Visible
// Visible->Hidden
// Application crash / abnormal shutdown
if (eDolbyMS12Lib == adev->dolby_lib_type) {
adev->atoms_lock_flag = 0;
dolby_ms12_set_atmos_lock_flag(adev->atoms_lock_flag);
}
if (dolby_stream_active(adev) || hwsync_lpcm_active(adev)) {
AM_LOGI("later release MS12 when direct stream active");
adev->need_remove_conti_mode = true;
} else {
AM_LOGI("Dolby MS12 is at continuous output mode, here go to end it!");
bool set_ms12_non_continuous = true;
get_dolby_ms12_cleanup(&adev->ms12, set_ms12_non_continuous);
//ALOGI("[%s:%d] get_dolby_ms12_cleanup\n", __FUNCTION__, __LINE__);
adev->exiting_ms12 = 1;
clock_gettime(CLOCK_MONOTONIC, &adev->ms12_exiting_start);
if (adev->active_outputs[STREAM_PCM_NORMAL] != NULL)
usecase_change_validate_l(adev->active_outputs[STREAM_PCM_NORMAL], true);
//continuous_stream_do_standby(adev);
}
} else {
if ((!disable_continuous) && !continuous_mode(adev)) {
adev->mix_init_flag = false;
adev->continuous_audio_mode = 1;
}
}
pthread_mutex_unlock(&adev->lock);
goto exit;*/
}
ret = str_parms_get_int(parms, "hdmi_dolby_atmos_lock", &val);
if (ret >= 0) {
AM_LOGI("hdmi_dolby_atmos_lock set to %d", val);
adev->atoms_lock_flag = val ? true : false;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
// Note: some guide from www.netflix.com
// In the Netflix application, enable/disable Atmos locking
// when you receive the signal from the Netflix application.
pthread_mutex_lock(&adev->lock);
// enable/disable atoms lock
set_ms12_atmos_lock(&(adev->ms12), adev->atoms_lock_flag);
AM_LOGI("set adev->atoms_lock_flag = %d", adev->atoms_lock_flag);
pthread_mutex_unlock(&adev->lock);
}
goto exit;
}
//for the local playback, IEC61937 format!
//temp to disable the Dolby MS12 continuous
//if local playback on Always continuous is completed,
//we should remove this function!
ret = str_parms_get_int(parms, "is_ms12_continuous", &val);
if (ret >= 0) {
AM_LOGI("is_ms12_continuous set to %d", val);
goto exit;
}
/*after enable MS12 continuous mode, the audio delay is big, we
need use this API to compensate some delay to video*/
ret = str_parms_get_int(parms, "compensate_video_enable", &val);
if (ret >= 0) {
AM_LOGI("compensate_video_enable set to %d", val);
adev->compensate_video_enable = val;
//aml_audio_compensate_video_delay(val);
goto exit;
}
/*enable force ddp output for ms12 v2*/
ret = str_parms_get_int(parms, "hal_param_force_ddp", &val);
if (ret >= 0) {
AM_LOGI("hal_param_force_ddp set to %d", val);
if (adev->ms12_force_ddp_out != val) {
adev->ms12_force_ddp_out = val;
update_sink_format_after_hotplug(adev);
}
goto exit;
}
} else if (eDolbyDcvLib == adev->dolby_lib_type_last) {
ret = str_parms_get_int(parms, "continuous_audio_mode", &val);
if (ret >= 0) {
AM_LOGI("ignore the continuous_audio_mode!");
adev->is_netflix = val;
goto exit;
}
}
ret = str_parms_get_str(parms, "SOURCE_GAIN", value, sizeof(value));
if (ret >= 0) {
float fAtvGainDb = 0, fDtvGainDb = 0, fHdmiGainDb = 0, fAvGainDb = 0, fMediaGainDb = 0, fArcGainDb = 0;
sscanf(value,"%f %f %f %f %f %f", &fAtvGainDb, &fDtvGainDb, &fHdmiGainDb, &fAvGainDb, &fMediaGainDb, &fArcGainDb);
AM_LOGI("audio source gain: atv:%f, dtv:%f, hdmiin:%f, av:%f, media:%f, arcin:%f",
fAtvGainDb, fDtvGainDb, fHdmiGainDb, fAvGainDb, fMediaGainDb, fArcGainDb);
set_inport_gain(adev, INPORT_ATV, DbToAmpl(fAtvGainDb));
set_inport_gain(adev, INPORT_TUNER, DbToAmpl(fDtvGainDb));
set_inport_gain(adev, INPORT_HDMIIN, DbToAmpl(fHdmiGainDb));
set_inport_gain(adev, INPORT_LINEIN, DbToAmpl(fAvGainDb));
set_inport_gain(adev, INPORT_MEDIA, DbToAmpl(fMediaGainDb));
set_inport_gain(adev, INPORT_ARCIN, DbToAmpl(fArcGainDb));
goto exit;
}
ret = str_parms_get_str(parms, "SOURCE_MUTE", value, sizeof(value));
if (ret >= 0) {
sscanf(value,"%d", &adev->source_mute);
AM_LOGI("set audio source mute: %s", adev->source_mute?"mute":"unmute");
goto exit;
}
ret = str_parms_get_str(parms, "POST_GAIN", value, sizeof(value));
if (ret >= 0) {
sscanf(value,"%f %f %f", &adev->eq_data.p_gain.speaker, &adev->eq_data.p_gain.spdif_arc,
&adev->eq_data.p_gain.headphone);
AM_LOGI("audio device gain: speaker:%f, spdif_arc:%f, headphone:%f",
adev->eq_data.p_gain.speaker, adev->eq_data.p_gain.spdif_arc,
adev->eq_data.p_gain.headphone);
goto exit;
}
ret = str_parms_get_str(parms, "port_delay", value, sizeof(value));
if (ret >= 0) {
char *port_name;
int port_delay = 0;
char *delimiter = ",";
char *remaining_string = value;
port_name = strtok_r(value, delimiter, &remaining_string);
port_delay = (atoi)(strtok_r(NULL, delimiter, &remaining_string));
aml_audio_set_port_delay(port_name, port_delay);
goto exit;
}
#ifdef USE_EQ_DRC
ret = str_parms_get_str(parms,"EQ_PARAM", value, sizeof(value));
if (ret >= 0) {
if (strcmp(value, adev->eq_param) != 0) {
char type = 'P';
unsigned int Fc = 0;
double G = 0;
double Q = 0;
unsigned int eq_type = 0;
char *remaining_string = value;
char *token = NULL;
unsigned int eq_band_id = 0;
memset(adev->eq_param, 0, sizeof(adev->eq_param));
strncpy(adev->eq_param, value, sizeof(adev->eq_param));
adev->eq_param[sizeof(adev->eq_param) - 1] = '\0';
set_aed_master_volume_mute(&adev->alsa_mixer, true);
set_dac_digital_volume_mute(&adev->alsa_mixer, true);
aml_audio_sleep(32*1000);
token = strtok_r(value, ",", &remaining_string);
while (token != NULL) {
ret = sscanf(token, "%c:%d:%lf:%lf", &type, &Fc, &G, &Q);
if (ret != 4) {
break;
} else {
token = strtok_r(NULL, ",", &remaining_string);
if (type == 'P') {
eq_type = FILTER_TYPE_BANDPASS;
} else {
//other eq_type
}
if (((adev->eq_hw_band_data[eq_band_id].eq_type != eq_type) ||
(adev->eq_hw_band_data[eq_band_id].Fc != Fc) ||
(adev->eq_hw_band_data[eq_band_id].G != G) ||
(adev->eq_hw_band_data[eq_band_id].Q != Q)) &&
(eq_band_id < EQ_HW_BAND_MAX_NUM)) {// AED_EQ_HPF_BAND_NUM only uses for HPF
adev->eq_hw_band_data[eq_band_id].eq_type = eq_type;
adev->eq_hw_band_data[eq_band_id].Fc = Fc;
adev->eq_hw_band_data[eq_band_id].G = G;
adev->eq_hw_band_data[eq_band_id].Q = Q;
ALOGI("audio AED EQ PARAM, band num:%d, type:%c, Fc:%d, G:%lf, Q:%lf", eq_band_id, type, Fc, G, Q);
setpar_eq(G, Q, Fc, eq_type, eq_band_id);
}
eq_band_id++;
}
}
aml_audio_sleep(32*1000);
set_dac_digital_volume_mute(&adev->alsa_mixer, false);
set_aed_master_volume_mute(&adev->alsa_mixer, false);
}
goto exit;
}
/* cmd format: hpf=1:200 */
ret = str_parms_get_str(parms,"hpf", value, sizeof(value));
if (ret >= 0) {
int hpf_enable = 0;
unsigned int Fc = 0;
sscanf(value, "%d:%d", &hpf_enable, &Fc);
if (adev->hpf_enable != hpf_enable) {
double G = 0;
double Q = 1;
set_aed_master_volume_mute(&adev->alsa_mixer, true);
aml_audio_sleep(32*1000);
ALOGI("audio AED EQ HPF, band num:%d, frequency:%d Hz, hpf enable:%d\n", EQ_HW_BAND_MAX_NUM, Fc, hpf_enable);
if (hpf_enable == 1) {
setpar_eq(G, Q, Fc, FILTER_TYPE_HPF, EQ_HW_BAND_MAX_NUM);
} else {
setpar_eq(G, Q, Fc, FILTER_TYPE_BANDPASS, EQ_HW_BAND_MAX_NUM);
}
aml_audio_sleep(32*1000);
set_aed_master_volume_mute(&adev->alsa_mixer, false);
adev->hpf_enable = hpf_enable;
}
goto exit;
}
ret = str_parms_get_str(parms,"mb_drc",value,sizeof(value));
if (ret >= 0) {
sscanf(value, "%u %u %u %u %f %f",&adev->Drc_data.band_id,&adev->Drc_data.attrack_time,&adev->Drc_data.release_time,
&adev->Drc_data.estimate_time,&adev->Drc_data.K,&adev->Drc_data.threshold);
setmb_drc(adev->Drc_data.band_id,adev->Drc_data.attrack_time,adev->Drc_data.release_time,adev->Drc_data.estimate_time,
adev->Drc_data.K,adev->Drc_data.threshold);
goto exit;
}
ret = str_parms_get_str(parms,"fb_drc",value,sizeof(value));
if (ret >= 0) {
sscanf(value, "%u %u %u %u %f %f %f",&adev->Drc_data.band_id,&adev->Drc_data.attrack_time,&adev->Drc_data.release_time,
&adev->Drc_data.estimate_time,&adev->Drc_data.K,&adev->Drc_data.threshold,&adev->Drc_data.offset);
setfb_drc(adev->Drc_data.band_id,adev->Drc_data.attrack_time,adev->Drc_data.release_time,
adev->Drc_data.estimate_time, adev->Drc_data.K, adev->Drc_data.threshold, adev->Drc_data.offset);
goto exit;
}
ret = str_parms_get_str(parms,"csfilter_drc",value,sizeof(value));
if (ret >= 0) {
sscanf(value, "%u %u",&adev->Drc_data.band_id,&adev->Drc_data.fc);
setcsfilter_drc(adev->Drc_data.band_id,adev->Drc_data.fc);
goto exit;
}
ret = str_parms_get_int(parms, "eq_enable", &val);
if (ret >= 0) {
if (val != adev->eq_enable) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AED_EQ_ENABLE, val);
adev->eq_enable = val;
AM_LOGI("audio AED EQ enable: %s", val?"enable":"disable");
}
goto exit;
}
ret = str_parms_get_int(parms, "multi_drc_enable", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AED_MULTI_DRC_ENABLE, val);
AM_LOGI("audio AED Multi-band DRC enable: %s", val?"enable":"disable");
goto exit;
}
ret = str_parms_get_int(parms, "fullband_drc_enable", &val);
if (ret >= 0) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AED_FULL_DRC_ENABLE, val);
AM_LOGI("audio AED Full-band DRC enable: %s", val?"enable":"disable");
goto exit;
}
#endif
/*AUDIO AI*/
if (adev->aml_ai_audio_type != eAmlAiAudioUnsupported) {
ret = str_parms_get_str(parms, "aiaq_enable", value, sizeof(value));
if (ret >= 0) {
int enable = 0;
sscanf(value, "%d", &enable);;
enable = enable ? 1 : 0;
if (false == aml_ai_audio_query_module(AML_AI_TYPE_AIAQ) && enable == 1) {
AmlAiConfig ai_config;
ai_config.samplerate = 48000;
ai_config.channels = 8;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/* For ms12 callback, the audio format must 8 or 10 channel, float */
ai_config.format = AUDIO_FORMAT_PCM_FLOAT;
} else {
ai_config.format = (16 == adev->audio_process_bitwidth)? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_32_BIT;
}
aml_ai_audio_open_module(ai_config, ai_config.channels, AML_AI_TYPE_AIAQ, &adev->alsa_mixer);
}
ALOGI("AI_AQ enable=%d",enable);
aml_ai_audio_set_enable(AML_AI_TYPE_AIAQ, enable);
goto exit;
}
ret = str_parms_get_int(parms, "audio_enhancement_enable", &val);
if (ret >= 0) {
ALOGI("audio_enhancement: %s\n", (val == 1) ? "enable":"disable");
if (false == aml_ai_audio_query_module(AML_AI_TYPE_ENHANCEMENT) && val == 1) {
AmlAiConfig ai_config;
ai_config.samplerate = 48000;
ai_config.channels = 8;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/* For ms12 callback, the audio format must 8 or 10 channel, float */
ai_config.format = AUDIO_FORMAT_PCM_FLOAT;
} else {
ai_config.format = (16 == adev->audio_process_bitwidth)? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_32_BIT;
}
aml_ai_audio_open_module(ai_config, ai_config.channels, AML_AI_TYPE_ENHANCEMENT, NULL);
}
aml_ai_audio_set_enable(AML_AI_TYPE_ENHANCEMENT, val);
goto exit;
}
/* audio enhancement control */
ret = str_parms_get_int(parms, "audio_enhancement_gain", &val);
if (ret >= 0) {
if (val < MIN_AUDIO_ENHANCEMENT_GAIN) {
val = MIN_AUDIO_ENHANCEMENT_GAIN;
} else if (val > MAX_AUDIO_ENHANCEMENT_GAIN) {
val = MAX_AUDIO_ENHANCEMENT_GAIN;
}
AmlAiValues values;
values.value = val;
if (aml_ai_audio_get_enable(AML_AI_TYPE_ENHANCEMENT)) {
aml_ai_audio_set_param(AML_AI_TYPE_ENHANCEMENT, ENHANCEMENT_GAIN, values);
ALOGI("audio_enhancement level (low/middle/high): %d", val);
} else {
ALOGI("audio_enhancement_gain in disable state");
}
goto exit;
}
ret = str_parms_get_str(parms, "audio_pcm_record_enable", value, sizeof(value));
if (ret >= 0) {
int record_enable = 0;
int delay_in_ms = 0;
sscanf(value,"%d,%d", &record_enable, &delay_in_ms);
if (false == aml_ai_audio_query_module(AML_AI_TYPE_ALOOP) && record_enable == 1) {
AmlAiConfig ai_config;
ai_config.samplerate = 48000;
ai_config.channels = 8;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/* For ms12 callback, the audio format must 8 or 10 channel, float */
ai_config.format = AUDIO_FORMAT_PCM_FLOAT;
} else {
ai_config.format = (16 == adev->audio_process_bitwidth)? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_32_BIT;
}
aml_ai_audio_open_module(ai_config, ai_config.channels, AML_AI_TYPE_ALOOP, NULL);
}
if (true == aml_ai_audio_query_module(AML_AI_TYPE_ALOOP)) {
aml_ai_audio_set_enable(AML_AI_TYPE_ALOOP, record_enable);
delay_in_ms = (delay_in_ms > MAX_DELAY_TIME) ? MAX_DELAY_TIME : delay_in_ms;
delay_in_ms = (record_enable == 0) ? 0 : delay_in_ms;
AmlAiValues values;
values.value = delay_in_ms;
aml_ai_audio_set_param(AML_AI_TYPE_ALOOP, ALOOP_RECORD_DELAY, values);
ALOGD("audio_pcm_record_enable: %s, audio delay %dms\n", (record_enable == 1) ? "enable":"disable", delay_in_ms);
}
goto exit;
}
ret = str_parms_get_int(parms, "audio_vocal_isolate_enable", &val);
if (ret >= 0) {
ALOGI("audio_vocal_isolate_enable: %s\n", (val == 1) ? "enable":"disable");
if (false == aml_ai_audio_query_module(AML_AI_TYPE_VOCAL_ISOLATE) && val == 1) {
AmlAiConfig ai_config;
ai_config.samplerate = 48000;
ai_config.channels = 8;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/* For ms12 callback, the audio format must 8 or 10 channel, float */
ai_config.format = AUDIO_FORMAT_PCM_FLOAT;
} else {
ai_config.format = (16 == adev->audio_process_bitwidth)? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_32_BIT;
}
aml_ai_audio_open_module(ai_config, ai_config.channels, AML_AI_TYPE_VOCAL_ISOLATE, NULL);
}
if (true == aml_ai_audio_query_module(AML_AI_TYPE_VOCAL_ISOLATE)) {
aml_ai_audio_set_enable(AML_AI_TYPE_VOCAL_ISOLATE, val);
adev->ai_external_isolate_latency = (val == 1) ? 150 : 0;
}
goto exit;
}
ret = str_parms_get_str(parms, "audio_vocal_gain", value, sizeof(value));
if (ret >= 0) {
float vocal_gain = 0;
sscanf(value,"%f", &vocal_gain);
ALOGI("[%s] set vocal gain = %f", __func__, vocal_gain);
AmlAiValues values;
values.value_f = vocal_gain;
if (aml_ai_audio_get_enable(AML_AI_TYPE_VOCAL_ISOLATE)) {
aml_ai_audio_set_param(AML_AI_TYPE_VOCAL_ISOLATE, VOCAL_ISOLATE_GAIN, values);
} else {
ALOGI("audio_vocal_isolate in disable state");
}
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_music_gain", &val);
if (ret >= 0) {
ALOGV("music gain: %ddB\n", val/100);
AmlAiValues values;
values.value = val;
aml_ai_audio_set_param(AML_AI_TYPE_VOCAL_ISOLATE, VOCAL_ISOLATE_VOLUME_GAIN, values);
aml_ai_audio_set_param(AML_AI_TYPE_LOUDNESS, LOUDNESS_MUSIC_VOLUME, values);
goto exit;
}
ret = str_parms_get_int(parms, "audio_loudness_enable", &val);
if (ret >= 0) {
ALOGI("audio_loudness_enable: %s\n", (val == 1) ? "enable":"disable");
if (false == aml_ai_audio_query_module(AML_AI_TYPE_LOUDNESS) && val == 1) {
AmlAiConfig ai_config;
ai_config.samplerate = 48000;
ai_config.channels = 8;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/* For ms12 callback, the audio format must 8 or 10 channel, float */
ai_config.format = AUDIO_FORMAT_PCM_FLOAT;
} else {
ai_config.format = (16 == adev->audio_process_bitwidth)? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_32_BIT;
}
aml_ai_audio_open_module(ai_config, ai_config.channels, AML_AI_TYPE_LOUDNESS, NULL);
}
if (true == aml_ai_audio_query_module(AML_AI_TYPE_LOUDNESS)) {
aml_ai_audio_set_enable(AML_AI_TYPE_LOUDNESS, val);
}
goto exit;
}
ret = str_parms_get_str(parms, "audio_loudness_gain", value, sizeof(value));
if (ret >= 0) {
float loudness_gain = 0;
sscanf(value,"%f", &loudness_gain);
ALOGD("[%s] set loudness gain = %f", __func__, loudness_gain);
AmlAiValues values;
values.value = loudness_gain;
if (aml_ai_audio_get_enable(AML_AI_TYPE_LOUDNESS)) {
aml_ai_audio_set_param(AML_AI_TYPE_LOUDNESS, LOUDNESS_GAIN, values);
} else {
ALOGI("audio_loudness in disable state");
}
goto exit;
}
}
ret = str_parms_get_str(parms, "DTS_POST_GAIN", value, sizeof(value));
if (ret >= 0) {
sscanf(value,"%f", &adev->dts_post_gain);
AM_LOGI("audio dts post gain: %f",adev->dts_post_gain);
goto exit;
}
ret = str_parms_get_int(parms, "EQ_MODE", &val);
#ifdef USE_EQ_DRC
if (ret >= 0) {
if (eq_mode_set(&adev->eq_data, val) < 0)
AM_LOGE("eq_mode_set failed");
goto exit;
}
#endif
#ifdef USE_DTV
/* dvb cmd deal with start */
{
ret = str_parms_get_int(parms, "hal_param_dtv_patch_cmd", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_CONTROL, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_ad_mix_enable", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_ENABLE ,val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_media_sync_id", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_MEDIA_SYNC_ID ,val);
goto exit;
}
ret = str_parms_get_int(parms, "dual_decoder_advol_level", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_VOL_LEVEL, val);
goto exit;
}
ret = str_parms_get_float(parms, "hal_param_dtv_ad_gain_level", &val_f);
if (ret >= 0) {
if (val_f < -96.0) {
val_f = -96.0;
} else if (val_f > 10.0) {
val_f = 10.0;
}
int advol_level = DbToAmpl(val_f) * 100;
AM_LOGI("ad gain level set to %f db, %d vol\n", val_f, advol_level);
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_VOL_LEVEL, advol_level);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dual_dec_mix_level", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_MIX_LEVEL, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_security_mem_level", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_SECURITY_MEM_LEVEL, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_synctype", &val);
if (ret >= 0) {
adev->synctype = val;
AM_LOGI("adev->synctype set to %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_audio_output_mode", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_OUTPUT_MODE, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_demux_id", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_DEMUX_INFO, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_pid", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_PID, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_fmt", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_FMT, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_audio_fmt", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_FMT, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_audio_id", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_PID, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_sub_audio_fmt", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_FMT, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_sub_audio_pid", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_AD_PID, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_has_dtv_video", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_HAS_VIDEO, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_tv_mute", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_MUTE, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_media_presentation_id", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_MEDIA_PRESENTATION_ID, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_media_first_lang", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_MEDIA_FIRST_LANG, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_media_second_lang", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_MEDIA_SECOND_LANG, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_audio_volume", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_VOLUME, val);
goto exit;
}
ret = str_parms_get_int(parms, "hal_param_dtv_decoder_id", &val);
if (ret >= 0) {
dtv_patch_handle_event(dev, AUDIO_DTV_PATCH_CMD_SET_DECODER_ID, val);
goto exit;
}
}
#endif
/* dvb cmd deal with end */
ret = str_parms_get_str(parms, "sound_track", value, sizeof(value));
if (ret > 0) {
int mode = atoi(value);
AM_LOGI("sound_track mode %d",mode);
adev->sound_track_mode = mode;
goto exit;
}
ret = str_parms_get_str(parms, "has_video", value, sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0) {
adev->is_has_video = true;
} else if (strncmp(value, "false", 5) == 0) {
adev->is_has_video = false;
} else {
adev->is_has_video = true;
AM_LOGE("unsupport value %s choose is_has_video(default) %d", value, adev->is_has_video);
}
AM_LOGI("is_has_video set to %d", adev->is_has_video);
}
ret = str_parms_get_str(parms, "reconfigA2dp", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("A2DP reconfigA2dp out_device=%x", adev->out_device);
goto exit;
}
ret = str_parms_get_str(parms, "hfp_set_sampling_rate", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: hfp_set_sampling_rate. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "hfp_volume", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: hfp_volume. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "bt_headset_name", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HA: bt_headset_name. Abort function and return 0.");
//goto exit;
}
ret = str_parms_get_str(parms, "rotation", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: rotation. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "bt_headset_nrec", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: bt_headset_nrec. Abort function and return 0.");
//goto exit;
}
ret = str_parms_get_str(parms, "bt_wbs", value, sizeof(value));
if (ret >= 0) {
AM_LOGI("Amlogic_HAL: bt_wbs=%s.", value);
if (strncmp(value, "on", 2) == 0)
adev->bt_wbs = true;
else
adev->bt_wbs = false;
goto exit;
}
ret = str_parms_get_str(parms, "hfp_enable", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: hfp_enable. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "HACSetting", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: HACSetting. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "tty_mode", value, sizeof(value));
if (ret >= 0) {
AM_LOGE ("Amlogic_HAL: tty_mode. Abort function and return 0.");
goto exit;
}
ret = str_parms_get_str(parms, "show-meminfo", value, sizeof(value));
if (ret >= 0) {
unsigned int level = (unsigned int)atoi(value);
AM_LOGE ("Amlogic_HAL: ShowMem info level:%d.", level);
aml_audio_debug_malloc_showinfo(level);
goto exit;
}
#ifndef NO_AUDIO_CAP
/*add interface to audio capture*/
ret = str_parms_get_str(parms, "cap_buffer", value, sizeof(value));
if (ret >= 0) {
int size = 0;
char * remaining_string = value;
char *name = strtok_r(value, ",", &remaining_string);
AM_LOGI("cap_buffer %s", value);
#ifndef NO_AUDIO_CAP
if (name) {
size = atoi(name + strlen(name) + 1);
if (size > 0) {
pthread_mutex_lock(&adev->cap_handle_lock);
if (adev->capture_handle) {
aml_audio_capture_out_close(adev->capture_handle);
adev->capture_handle = NULL;
}
aml_audio_capture_out_open(&adev->capture_handle, name, size);
AM_LOGI("IpcBuffer_created %p (%s, %d)", adev->capture_handle, name, size);
pthread_mutex_unlock(&adev->cap_handle_lock);
ret = 0;
goto exit;
} else if (size == 0) {
pthread_mutex_lock(&adev->cap_handle_lock);
if (adev->capture_handle) {
aml_audio_capture_out_close(adev->capture_handle);
adev->capture_handle = NULL;
}
pthread_mutex_unlock(&adev->cap_handle_lock);
ret = 0;
goto exit;
}
}
ret = -EINVAL;
#endif
goto exit;
}
ret = str_parms_get_str(parms, "cap_delay", value, sizeof(value));
if (ret >= 0) {
adev->cap_delay = atoi(value);
AM_LOGI("cap_delay=%d",adev->cap_delay);
goto exit;
}
#endif
#ifdef SUPPORT_EXTERNAL_OUTPUT
/*
* add interface to bluetooth and usb spk
* hal_param set cmd=name,enable
* hal_param set device=plughw:Device,1
* hal_param set device=bluealsa:DEV=BF:FE:4D:6E:B0:92,1
*/
ret = str_parms_get_str(parms, "device", value, sizeof(value));
if (ret >= 0) {
int enable = 0;
char * remaining_string = value;
char *name = strtok_r(value, ",", &remaining_string);
AM_LOGI("external device %s", value);
if (strncmp(name, "plughw", 6) == 0) {
enable = atoi(name + strlen(name) + 1);
if (enable > 0) {
pthread_mutex_lock(&adev->external_handle_lock);
aml_external_device_open(USB_TYPE, name);
pthread_mutex_unlock(&adev->external_handle_lock);
ret = 0;
goto exit;
} else if (enable == 0) {
pthread_mutex_lock(&adev->external_handle_lock);
aml_external_device_close(adev->usb_handle);
adev->usb_handle = NULL;
pthread_mutex_unlock(&adev->external_handle_lock);
ret = 0;
goto exit;
}
} else if (strncmp(name, "bluealsa", 8) == 0) {
enable = atoi(name + strlen(name) + 1);
if (enable > 0) {
pthread_mutex_lock(&adev->external_handle_lock);
aml_external_device_open(BT_TYPE, name);
pthread_mutex_unlock(&adev->external_handle_lock);
ret = 0;
goto exit;
} else if (enable == 0) {
pthread_mutex_lock(&adev->external_handle_lock);
aml_external_device_close(adev->bt_handle);
adev->bt_handle = NULL;
pthread_mutex_unlock(&adev->external_handle_lock);
ret = 0;
goto exit;
}
}
ret = -EINVAL;
goto exit;
}
#endif
ret = str_parms_get_str(parms, "hal_param_dialogue_enhancement", value, sizeof(value));
if (ret >= 0) {
switch (atoi(value)) {
case DIALOGUE_ENHANCEMENT_OFF:
adev->ms12.ac4_de = 0;
adev->ms12.dap_de_enable = 0;
adev->ms12.dap_de_amount = 0;
break;
case DIALOGUE_ENHANCEMENT_LOW:
adev->ms12.ac4_de = 4;
adev->ms12.dap_de_enable = 1;
adev->ms12.dap_de_amount = 4;
break;
case DIALOGUE_ENHANCEMENT_MEDIUM:
adev->ms12.ac4_de = 8;
adev->ms12.dap_de_enable = 1;
adev->ms12.dap_de_amount = 8;
break;
case DIALOGUE_ENHANCEMENT_HIGH:
adev->ms12.ac4_de = 12;
adev->ms12.dap_de_enable = 1;
adev->ms12.dap_de_amount = 12;
break;
default:
ALOGE("%s(), not the expected Dialogue Enhancement lever, set Dialogue Enhancement off", __FUNCTION__);
adev->ms12.ac4_de = 0;
adev->ms12.dap_de_enable = 0;
adev->ms12.dap_de_amount = 0;
break;
}
char parm[64] = "";
AM_LOGI("dap_init_mode=%d ms12_config:%d(0:Y,1:X,2:Z) ac4_de=%d dap_de=%d,%d",
adev->dolby_ms12_dap_init_mode, adev->dolby_ms12_audio_config, adev->ms12.ac4_de, adev->ms12.dap_de_enable, adev->ms12.dap_de_amount);
//STB and TV has different way to set dap de, TV use runtime, STB call decoder setting API
if (is_TV(adev) && adev->dolby_ms12_audio_config != MS12_CONFIG_Y) {
sprintf(parm,"ms12_runtime=-dap_dialogue_enhancer %d,%d", adev->ms12.dap_de_enable, adev->ms12.dap_de_amount);
if (strlen(parm) > 0) {
aml_ms12_update_runtime_params(&(adev->ms12), parm);
}
}
goto exit;
}
ret = str_parms_get_str(parms, "hal_param_dmx_mode", value, sizeof(value));
if (ret >= 0) {
switch (atoi(value)) {
case SOUND_DMX_MODE_SURROUND:
adev->downmix_type = 0;
break;
case SOUND_DMX_MODE_STEREO:
adev->downmix_type = 1;
break;
default:
ALOGE("%s(), not the expected dmx mode, set dmx mode surround", __FUNCTION__);
adev->downmix_type = 0;
break;
}
ALOGI("Amlogic_HAL - %s: set MS12 Downmix modes :%d.", __FUNCTION__, adev->downmix_type);
char parm[32] = "";
sprintf(parm, "%s %d", "-dmx", adev->downmix_type);
pthread_mutex_lock(&adev->lock);
if (strlen(parm) > 0) {
aml_ms12_update_runtime_params(&(adev->ms12), parm);
}
pthread_mutex_unlock(&adev->lock);
goto exit;
}
ret = str_parms_get_str(parms, "picture_mode", value, sizeof(value));
if (ret >= 0) {
if (strncmp(value, "PQ_MODE_STANDARD", 16) == 0) {
set_dev_pic_mode(PQ_STANDARD);
} else if (strncmp(value, "PQ_MODE_GAME", 12) == 0) {
set_dev_pic_mode(PQ_GAME);
} else {
set_dev_pic_mode(PQ_STANDARD);
AM_LOGE("unsupport value %s choose pic mode (default) standard", value);
}
AM_LOGI("set pic mode to: %d", get_dev_pic_mode());
goto exit;
}
ret = str_parms_get_str(parms, "prim_mixgain", value, sizeof(value));
if (ret >= 0) {
char parm[40] = "";
int gain = atoi(value);
if ((gain <= 0) && (gain >= -96)) {
sprintf(parm, "-sys_prim_mixgain %d,200,0", gain << 7);
pthread_mutex_lock(&adev->lock);
aml_ms12_update_runtime_params(&(adev->ms12), parm);
pthread_mutex_unlock(&adev->lock);
ret = 0;
goto exit;
}
ret = -EINVAL;
goto exit;
}
ret = str_parms_get_str(parms, "apps_mixgain", value, sizeof(value));
if (ret >= 0) {
char parm[40] = "";
int gain = atoi(value);
if ((gain <= 0) && (gain >= -96)) {
int after_master_gain;
adev->ms12.apps_mixgain = gain << 7;
after_master_gain = 2560.0f * log10((adev->master_mute) ? 0.0f : adev->master_volume);
after_master_gain += adev->ms12.apps_mixgain;
if (after_master_gain > 0) {
after_master_gain = 0;
} else if (after_master_gain < (-96 << 7)) {
after_master_gain = -96 << 7;
}
sprintf(parm, "-sys_apps_mixgain %d,200,0", after_master_gain);
pthread_mutex_lock(&adev->lock);
aml_ms12_update_runtime_params(&(adev->ms12), parm);
pthread_mutex_unlock(&adev->lock);
ret = 0;
goto exit;
}
ret = -EINVAL;
goto exit;
}
ret = str_parms_get_str(parms, "syss_mixgain", value, sizeof(value));
if (ret >= 0) {
char parm[40] = "";
int gain = atoi(value);
if ((gain <= 0) && (gain >= -96)) {
int after_master_gain;
adev->ms12.syss_mixgain = gain << 7;
after_master_gain = 2560.0f * log10((adev->master_mute) ? 0.0f : adev->master_volume);
after_master_gain += adev->ms12.syss_mixgain;
if (after_master_gain > 0) {
after_master_gain = 0;
} else if (after_master_gain < (-96 << 7)) {
after_master_gain = -96 << 7;
}
sprintf(parm, "-sys_syss_mixgain %d,200,0", after_master_gain);
pthread_mutex_lock(&adev->lock);
aml_ms12_update_runtime_params(&(adev->ms12), parm);
pthread_mutex_unlock(&adev->lock);
ret = 0;
goto exit;
}
ret = -EINVAL;
goto exit;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
ret = str_parms_get_str(parms, "vol_ease", value, sizeof(value));
if (ret >= 0) {// vol_ease only apply to main stream
int gain, duration, shape;// shape(0: linear, 1: in cube, 2: out cube)
AM_LOGI("vol_ease: value= %s", value);
if (adev->master_volume < FLOAT_ZERO) {
ret = 0;
goto exit;
}
if (sscanf(value, "%d,%d,%d", &gain, &duration, &shape) == 3) {
set_vol_ease_by_decoder_id(0, gain, duration, shape);
ret = 0;
goto exit;
}
}
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
ret = str_parms_get_str(parms, "stream_vol_ease", value, sizeof(value));
if (ret >= 0) {// vol_ease only apply to main stream
int gain, duration, shape;// shape(0: linear, 1: in cube, 2: out cube)
int decoder_id = 0;
AM_LOGI("vol_ease: value= %s", value);
if (adev->master_volume < FLOAT_ZERO) {
ret = 0;
goto exit;
}
if (sscanf(value, "%d,%d,%d,%d", &decoder_id, &gain, &duration, &shape) == 4) {
set_vol_ease_by_decoder_id(decoder_id, gain, duration, shape);
ret = 0;
goto exit;
}
}
}
//ms12_runtime is supported when MS12 exists or Dolby dap exists
if (eDolbyMS12Lib == adev->dolby_lib_type || adev->ms12.dap_only_enable) {
ret = str_parms_get_str(parms, "ms12_runtime", value, sizeof(value));
if (ret >= 0) {
char *parm = strstr(kvpairs, "=");
pthread_mutex_lock(&adev->lock);
if (parm) {
if (strcmp(parm+1, "-atmos_lock 1") == 0) {
adev->atoms_lock_flag = 1;
} else if (strcmp(parm+1, "-atmos_lock 0") == 0) {
adev->atoms_lock_flag = 0;
} else if (strstr(parm+1, "-drc")) {
aml_audio_set_drc_control(parm+1, &adev->decoder_drc_control);
//dap drc default same with decoder drc
adev->dap_drc_control = adev->decoder_drc_control;
}
if (strstr(parm+1, "-dap_drc")) {
aml_audio_set_drc_control(strstr(parm+1, "-dap_drc"), &adev->dap_drc_control);
}
if (strstr(parm+1, "-dmx")) {
adev->downmix_type = atoi(parm+5);//refer to enum: AM_DownMixMode_t
AM_LOGE("%s, downmix_type %d", parm+5, adev->downmix_type);
}
if (strstr(parm+1, "-ac4_de")) {
int ac4_de = atoi(parm+8);
adev->ms12.ac4_de = ac4_de;
AM_LOGI("ac4_de=%d", ac4_de);
} else if (strstr(parm+1, "-dap_surround_virtualizer")) {
char *param_value = (adev->dolby_ms12_audio_config != MS12_CONFIG_Z) ? \
"-dap_surround_virtualizer 0,0" : parm + 1;
aml_ms12_update_runtime_params(&(adev->ms12), param_value);
} else if (strstr(parm+1, "-dap_surround_decoder_enable")) {
char *param_value = (adev->dolby_ms12_audio_config != MS12_CONFIG_Z) ? \
"-dap_surround_decoder_enable 0" : parm + 1;
aml_ms12_update_runtime_params(&(adev->ms12), param_value);
} else if (strstr(parm+1, "-dap_dialogue_enhancer") || strstr(parm+1, "-dap_leveler")) {
AM_LOGI("dap_init_mode=%d dolby_ms12_audio_config:%d(0:Y,1:X,2:Z)", adev->dolby_ms12_dap_init_mode, adev->dolby_ms12_audio_config);
if ((adev->dolby_ms12_dap_init_mode == 1) && (adev->dolby_ms12_audio_config == MS12_CONFIG_X)) {
//for OTT config X, dap is in codec graph
if (strstr(parm+1, "-dap_dialogue_enhancer")) {
adev->ms12.dap_de_enable = atoi(parm+24);
adev->ms12.dap_de_amount = atoi(parm+26);
AM_LOGI("de_enable:%d de_amount:%d", adev->ms12.dap_de_enable, adev->ms12.dap_de_amount);
} else {
adev->ms12.dap_leveler[0] = atoi(parm+14);
adev->ms12.dap_leveler[1] = atoi(parm+16);
AM_LOGI("dap_leveler:%d,%d", adev->ms12.dap_leveler[0], adev->ms12.dap_leveler[1]);
}
} else {
//for TV/Soundbar config X/Z
aml_ms12_update_runtime_params(&(adev->ms12), parm+1);
}
} else {
aml_ms12_update_runtime_params(&(adev->ms12), parm+1);
audio_continuous_standby_reset(ms12->continuous_standby_handle);
}
}
pthread_mutex_unlock(&adev->lock);
goto exit;
}
}
ret = str_parms_get_str(parms, "dmx_mode", value, sizeof(value));
if (ret >= 0) {
char *parm = strstr(kvpairs, "=");
pthread_mutex_lock(&adev->lock);
if (parm) {
if (strstr(parm+1, "-dmx")) {
adev->downmix_type = atoi(parm+5);//refer to enum: AM_DownMixMode_t
AM_LOGD("%s, downmix_type %d", parm+5, adev->downmix_type);
if (eDolbyMS12Lib == adev->dolby_lib_type) {
aml_ms12_update_runtime_params(&(adev->ms12), parm+1);
}
}
}
pthread_mutex_unlock(&adev->lock);
goto exit;
}
ret = str_parms_get_str(parms, "drc_mode", value, sizeof(value));
if (ret >= 0) {
char *parm = strstr(kvpairs, "=");
pthread_mutex_lock(&adev->lock);
if (parm) {
if (strstr(parm+1, "-drc")) {
aml_audio_set_drc_control(parm+1, &adev->decoder_drc_control);
//dap drc default same with decoder drc
adev->dap_drc_control = adev->decoder_drc_control;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if (strstr(parm+1, "-dap_drc")) {
aml_audio_set_drc_control(strstr(parm+1, "-dap_drc"), &adev->dap_drc_control);
}
//aml_ms12_update_runtime_params(&(adev->ms12), parm+1);
adev->drc_mode_ui_config = true;
ALOGI("UI set drc_mode_ui_config = %d",adev->drc_mode_ui_config);
dynamic_set_dap_drc_parameters(&adev->ms12, adev->decoder_drc_control);
dynamic_set_dolby_ms12_drc_parameters(&adev->ms12, adev->dap_drc_control );
}
}
pthread_mutex_unlock(&adev->lock);
goto exit;
}
ret = str_parms_get_str(parms, "bypass_dap", value, sizeof(value));
if (ret >= 0) {
sscanf(value,"%d %f", &adev->ms12.UI_disable_dap, &adev->ms12.dap_bypassgain);
set_ms12_dap_disable_by_UI(&adev->ms12, adev->ms12.UI_disable_dap);
AM_LOGD("UI_disable_dap is %d and dap_bypassgain is %f",adev->ms12.UI_disable_dap, adev->ms12.dap_bypassgain);
goto exit;
}
ret = str_parms_get_str(parms, "tuning_binary", value, sizeof(value));
if (ret >= 0 && strlen(value)) {
adev->is_ms12_tuning_dat = is_ms12_tuning_dat_in_dut(value);
if (adev->is_ms12_tuning_dat) {
if (sizeof(adev->ms12_tuning_dat_path) >= strlen(value) + 1) {
memset(adev->ms12_tuning_dat_path, 0, sizeof(adev->ms12_tuning_dat_path));
memcpy(adev->ms12_tuning_dat_path, value, strlen(value));
char tuning_data_runtime_param[AUDIO_HAL_CHAR_MAX_LEN + 12] = {};
if (-1 != snprintf(tuning_data_runtime_param, sizeof(tuning_data_runtime_param), "-dap_tuning %s", adev->ms12_tuning_dat_path)) {
AM_LOGI("tuning_data_runtime_param=%s", tuning_data_runtime_param);
if ((eDolbyMS12Lib == adev->dolby_lib_type) && (adev->dolby_ms12_audio_config != MS12_CONFIG_Y)) {
aml_ms12_update_runtime_params(&(adev->ms12), tuning_data_runtime_param);
audio_continuous_standby_reset(ms12->continuous_standby_handle);
} else {
AM_LOGW("dolby_lib_type:%d ms12_config:%d, can not set ms12 tuning data", adev->dolby_lib_type, adev->dolby_ms12_audio_config);
}
} else {
AM_LOGE("Set dap tuning data fail");
}
} else {
AM_LOGE("The len of value is %zu, it is too long to set", strlen(value) + 1);
}
} else {
AM_LOGW("The file in the %s path does not exist", value);
}
goto exit;
}
ret = str_parms_get_str(parms, "Dolby_Config_Type", value, sizeof(value));
if (ret >= 0 && strlen(value)) {
adev->dolby_ms12_audio_config = (strcasestr(value, "Z") != NULL) ? MS12_CONFIG_Z : \
(strcasestr(value, "X") != NULL) ? MS12_CONFIG_X : MS12_CONFIG_Y;
ALOGI("[%s:%d],use dolby config %d ", __func__, __LINE__, adev->dolby_ms12_audio_config);
goto exit;
}
ret = str_parms_get_str(parms, "VX_SET_DTS_Mode", value, sizeof(value));
if (ret >= 0) {
int dts_decoder_output_mode = atoi(value);
if (dts_decoder_output_mode > 6 || dts_decoder_output_mode < 0)
goto exit;
if (dts_decoder_output_mode == 2)
adev->native_postprocess.vx_force_stereo = 1;
else
adev->native_postprocess.vx_force_stereo = 0;
dca_set_out_ch_internal(dts_decoder_output_mode);
AM_LOGD("set dts decoder output mode to %d", dts_decoder_output_mode);
goto exit;
}
ret = str_parms_get_str(parms, "pts_gap", value, sizeof(value));
if (ret >= 0) {
unsigned long long offset, duration;
if (sscanf(value, "%llu,%lld", &offset, &duration) == 2) {
AM_LOGI("pts_gap %llu %lld", offset, duration);
adev->gap_offset = offset;
adev->gap_ignore_pts = false;
adev->gap_passthrough_state = GAP_PASSTHROUGH_STATE_SET;
//to test
//dolby_ms12_set_pts_gap(offset, duration);
ret = 0;
goto exit;
}
ret = -EINVAL;
goto exit;
}
#ifndef NO_AUDIO_CAP
ret = str_parms_get_int(parms, "audioinject", &val);
if (ret >= 0) {
if (val == 1) {
adev->injection_enable = 1;
// injection must be used with capture together
pthread_mutex_lock(&adev->cap_handle_lock);
if (!adev->capture_handle) {
AM_LOGE("injection must be used with capture together!!! please check !!!");
}
pthread_mutex_unlock(&adev->cap_handle_lock);
//set PCM output to close spdif handle
set_hdmi_format(adev, PCM);
AM_LOGI("close pcm out");
aml_audio_pcm_out_close(adev);
} else {
adev->injection_enable = 0;
}
goto exit;
}
#endif
ret = str_parms_get_int(parms, "low_latency", &val);
if (ret >= 0 ) {
adev->low_latency_mode = val;
if (adev->ms12.dolby_ms12_enable) {
set_ms12_full_dap_disable(&adev->ms12, adev->low_latency_mode);
pthread_mutex_lock(&adev->ms12.lock);
if (adev->low_latency_mode) {
set_ms12_alsa_limit_frame(&adev->ms12, MS12_ALSA_LOW_LIMIT_FRAME);
} else {
set_ms12_alsa_limit_frame(&adev->ms12, MS12_ALSA_DEFAULT_LIMIT_FRAME);
}
pthread_mutex_unlock(&adev->ms12.lock);
}
goto exit;
}
ret = str_parms_get_int(parms, "gst_pause", &val);
if (ret >= 0) {
AM_LOGE ("TODO: gst_pause: This feature is currently unavailable and needs to be added");
goto exit;
}
ret = str_parms_get_int(parms, "enable_scaletempo", &val);
if (ret >= 0) {
if (val == 1)
adev->user_setting_scaletempo = true;
else
adev->user_setting_scaletempo = false;
AM_LOGI("audio enable_scaletempo: %s", adev->user_setting_scaletempo? "already enable as default": "disable");
goto exit;
}
ret = str_parms_get_str(parms,"OutputDestination", value , sizeof(value));
if (ret >= 0) {
AM_LOGI("OutputDestination=%s", value);
adev_update_active_outport(adev, (audio_devices_t)atoi(value));
goto exit;
}
ret = str_parms_get_int(parms, "spdif_protection_mode", &val);
if (ret >= 0 ) {
if (val == SPDIF_PROTECTION_MODE_NEVER) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_ENABLE);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_B_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_ENABLE);
} else if (val == SPDIF_PROTECTION_MODE_ONCE || val == SPDIF_PROTECTION_MODE_NONE) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_DISABLE);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_B_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_DISABLE);
} else if (val == SPDIF_PROTECTION_MODE_CP_NO) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_CP_ENABLE);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_B_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_CP_ENABLE);
} else if (val == SPDIF_PROTECTION_MODE_CP_YES) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_CP_DISABLE);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_B_OUT_CHANNEL_STATUS, SPDIF_PROTECTION_CP_DISABLE);
}
AM_LOGI("spdif_protection_mode:%d", val);
goto exit;
}
//this function is used for TTS
//1. when TTS starts to play, the volumes of other streams become small
// the calling order shold be a."audio_focus_enable=ture" b."audio_focus_volume=***"
//2. when TTS stops to play, the volumes of other streams resume
// the calling order shold be "audio_focus_enable=false"
ret = str_parms_get_int(parms, "audio_focus_enable", &val);
if (ret >= 0) {
if (val == 1) {
adev->audio_focus_enable = true;
}
else {
adev->audio_focus_enable = false;
}
AM_LOGI("audio focus %s", adev->audio_focus_enable? "enable": "disable");
goto exit;
}
ret = str_parms_get_int(parms, "audio_focus_volume", &val);
if (ret >= 0) {
if (adev->audio_focus_enable) {
if (val == 100) { // 0 ~ 100: 100 means recover volume
adev->audio_focus_ratio = 1.0;
aml_audio_focus(adev, false);
AM_LOGI("audio_foucus recover volume");
} else {
adev->audio_focus_volume = val;
adev->audio_focus_ratio = adev->audio_focus_volume / 100.0L;
aml_audio_focus(adev, true);
AM_LOGI("audio_focus_volume is %d", adev->audio_focus_volume);
}
}
goto exit;
}
ret = str_parms_get_int(parms,"master_volume", &val);
if (ret >= 0) {
adev_set_master_volume(dev, (float)val/100.0);
AM_LOGI("master_volume is %f", (float)val/100.0);
goto exit;
}
ret = str_parms_get_str(parms, "patch_set_decoder_id", value, sizeof(value));
if (ret >= 0) {
int patch_id = 0;
int decoder_id = 0;
char *delimiter = ",";
char *remaining_string = value;
patch_id = (atoi)(strtok_r(value, delimiter, &remaining_string));
decoder_id = (atoi)(strtok_r(NULL, delimiter, &remaining_string));
patch_mgr_set_decoder_id(adev->patch_manager, patch_id, decoder_id);
goto exit;
}
//example: set_volume_by_id=1,0.5
ret = str_parms_get_str(parms, "set_volume_by_id", value, sizeof(value));
if (ret >= 0) {
int decoder_id = 0;
float decoder_volume = 0;
char *delimiter = ",";
char *remaining_string = value;
decoder_id = (atoi)(strtok_r(value, delimiter, &remaining_string));
decoder_volume = (atof)(strtok_r(NULL, delimiter, &remaining_string));
set_volume_by_decoder_id(decoder_id, decoder_volume, decoder_volume);
goto exit;
}
//example:hal_param set set_output_device_by_id=1,0 [id,device]
ret = str_parms_get_str(parms, "set_output_device_by_id", value, sizeof(value));
if (ret >= 0) {
int decoder_id = 0;
char *delimiter = ",";
int device = AML_OUTPUT_DEVICE_DEFAULT;
char *remaining_string = value;
decoder_id = (atoi)(strtok_r(value, delimiter, &remaining_string));
device = (atoi)(strtok_r(NULL, delimiter, &remaining_string));
aml_set_output_device_by_id(decoder_id, device);
goto exit;
}
/* karaoke parameters
format: karaoke_control=cmd,type,value
such as: "karaoke_control=switch,0,0" means that set USB mic on */
ret = str_parms_get_str(parms, "karaoke_control", value, sizeof(value));
if (ret >= 0) {
karaoke_manager_command(adev->kara_manager, value, sizeof(value));
goto exit;
}
ret = str_parms_get_int(parms, "karaoke_vol", &val);
if (ret >= 0 ) {
float volume = val / 100.0;
adev_set_karaoke_vol(adev, volume);
AM_LOGI("set karaoke_vol:%d", val);
goto exit;
}
ret = str_parms_get_int(parms, "dtsx_drc_enable", &val);
if (ret >= 0 ) {
adev->dtsx_drc_enable = val? true : false;
dtsx_set_drc_state(adev->dtsx_drc_enable);
ALOGI("[%s:%d] set dtsx drc state to:%s", __func__, __LINE__, adev->dtsx_drc_enable?"ON" : "OFF");
goto exit;
}
ret = str_parms_get_int(parms, "dtsx_spk_loudness", &val);
if (ret >= 0 ) {
if (val == 1) {
dtsx_spk_loudness_enable(true);
} else {
dtsx_spk_loudness_enable(false);
}
ALOGI("[%s:%d] dtsx_spk_loudness:%d", __func__, __LINE__, val);
goto exit;
}
ret = str_parms_get_int(parms, "dtsx_spk_loudness_target", &val);
if (ret >= 0 ) {
dtsx_spk_loudness_target(val);
ALOGI("[%s:%d] dtsx_spk_loudness_target:%d", __func__, __LINE__, val);
goto exit;
}
ret = str_parms_get_int(parms, "dtsx_type1_relable_enable", &val);
if (ret >= 0) {
dtsx_enable_type1_relable(val);
AM_LOGI("dtsx_type1_relable_enable: %d", val);
goto exit;
}
ret = str_parms_get_str(parms,"remove_audio_effect", value , sizeof(value));
if (ret >= 0) {
char sound_effect_support_list[AUDIO_HAL_CHAR_MAX_LEN] = {0};
aml_report_audio_support_list_to_ui(adev, sound_effect_support_list, AUDIO_HAL_CHAR_MAX_LEN);
if (strncmp(value, "all", strlen(value)) == 0) {
ALOGI("adev->native_postprocess.num_postprocessors=%d\n",adev->native_postprocess.num_postprocessors);
while (adev->native_postprocess.num_postprocessors > 0) {
effect_handle_t effect = adev->native_postprocess.postprocessors[adev->native_postprocess.num_postprocessors-1];
adev_remove_device_effect(dev, 0, effect);
}
AM_LOGI("remove %s sound effect", value);
} else {
effect_descriptor_t tmpdesc_in_queue;
for (int i = 0; i < adev->native_postprocess.num_postprocessors; i++) {
(*adev->native_postprocess.postprocessors[i])->get_descriptor(adev->native_postprocess.postprocessors[i], &tmpdesc_in_queue);
if (strncmp (tmpdesc_in_queue.name, value, strlen(tmpdesc_in_queue.name)) == 0) {
adev_remove_device_effect(dev, 0, adev->native_postprocess.postprocessors[i]);
AM_LOGI("support %s effect, remove %s sound effect", sound_effect_support_list, value);
break;
}
}
AM_LOGW("Not support %s effect, remove sound effect failed", value);
}
goto exit;
}
ret = str_parms_get_int(parms, "vpts_mt_offset", &val);
if (ret >= 0) {
adev->hbbtvsync_info.vpts_mt_offset = val;//ms
AM_LOGI("vpts_mt_offset: %d", val);
goto exit;
}
ret = str_parms_get_int(parms, "va_offset", &val);
if (ret >= 0) {
adev->hbbtvsync_info.va_offset = val;//ms
AM_LOGI("va_offset: %d", val);
goto exit;
}
ret = set_mpegh_parameters(adev, parms);
if (ret >= 0) {
AM_LOGI("set mpeg param, kv: %s", kvpairs);
goto exit;
}
ret = str_parms_get_int(parms, "dtsx_support", &val);
if (ret >= 0 ) {
ALOGI("[%s:%d] dtsx_support:%d", __func__, __LINE__, val);
//The platform does not support dts, but dts x lib in the DTS_X_LIB_PATH_A
//need close the open dts lib.
if (!val && adev->dts_lib_type == eDTSXLib) {
adev->dts_lib_type = detect_dts_lib_type((bool)val);
}
goto exit;
}
#ifdef BUILD_LINUX
ret = str_parms_get_str(parms, "setenv", value, sizeof(value));
if (ret >= 0) {
char *remaining_string;
char *var = strtok_r(value, ",", &remaining_string);
if (var) {
char *val = var + strlen(var) + 1;
if (strlen(val) > 0) {
aml_audio_property_set(var, val);
} else {
aml_audio_property_unset(var);
AM_LOGI("env %s unset", var);
#ifdef DIAG_LOG
if (strcmp(var, "AV_PROGRESSION") == 0) {
pthread_mutex_lock(&adev->diag_log_lock);
diag_log_close(adev);
pthread_mutex_unlock(&adev->diag_log_lock);
}
#endif
}
}
goto exit;
}
#endif
ret = str_parms_get_int(parms, "system_early_suspend", &val);
if (ret >= 0)
{
if (adev->is_early_suspend != val) {
AM_LOGI("system_early_suspend: %d", val);
adev->is_early_suspend = val;
}
goto exit;
}
int len = 2048; //todo
//len = str_parms_get_str_len(parms, "applyxmlaction");
if (0 < len)
{
char *xmlbuf = aml_audio_calloc(1, (len + 1));
if (NULL == xmlbuf) {
AM_LOGE("aml_audio_calloc: %d, fail!", ret);
goto exit;
}
ret = str_parms_get_str(parms, "applyxmlaction", xmlbuf, (len + 1));
if (ret >= 0) {
ret = aml_mpegh_applyxmlaction(adev, xmlbuf, (len + 1));
AM_LOGI("applyxmlaction: %d", ret);
}
aml_audio_free(xmlbuf);
xmlbuf = NULL;
goto exit;
}
exit:
str_parms_destroy (parms);
/* always success to pass VTS */
return 0;
}
static void adev_get_cec_control_object(struct aml_audio_device *adev, char *temp_buf)
{
int cec_control_tv = 0;
if (!is_TV(adev) && adev->dolby_lib_type != eDolbyMS12Lib) {
enum AML_SPDIF_FORMAT format = AML_STEREO_PCM;
enum AML_SRC_TO_HDMITX spdif_index = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_SRC_TO_HDMI);
if (spdif_index == AML_SPDIF_A_TO_HDMITX) {
format = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_FORMAT);
} else if (spdif_index == AML_SPDIF_B_TO_HDMITX) {
format = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_B_FORMAT);
} else {
format = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_I2S2HDMI_FORMAT);
}
cec_control_tv = (format == AML_STEREO_PCM) ? 0 : 1;
}
sprintf (temp_buf, "hal_param_cec_control_tv=%d", cec_control_tv);
if (adev->debug_flag) {
ALOGD("[%s:%d] hal_param_cec_control_tv:%d", __func__, __LINE__, cec_control_tv);
}
}
/*Based on the existing library resources to determine whether to support the decoding of this format*/
static bool aml_get_codec_support_runtime(struct aml_audio_device *adev, char* aformat)
{
audio_format_t audio_format = convert_audio_str_2_audio_formt(aformat);
if (audio_format == AUDIO_FORMAT_PCM) {
return true;
} else {
if ((adev->dolby_lib_type == eDolbyMS12Lib && is_dolby_ms12_support_compression_format(audio_format)) ||
(adev->dts_decode_enable && is_dts_format(audio_format)) ||
(adev->dolby_lib_type == eDolbyDcvLib && adev->dolby_decode_enable && is_dcv_dec_support(audio_format))) {
return true;
}
}
return false;
}
static char * adev_get_parameters (const struct audio_hw_device *dev,
const char *keys)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
char temp_buf[AUDIO_HAL_CHAR_MAX_LEN + 32] = {0};
if (!strcmp (keys, AUDIO_PARAMETER_HW_AV_SYNC) ) {
ALOGI ("get hw_av_sync id\n");
return strdup ("hw_av_sync=12345678");
}
// ALOGI ("adev_get_parameters keys: %s \n", keys);
if (strstr (keys, "getenv=") ) {
char *pret=getenv(&keys[7]);
if (NULL != pret) {
ALOGI ("ret: val %s,key %s \n", pret, &keys[7]);
return strdup (pret);
} else {
return strdup ("find not");
}
} else if (strstr (keys, AUDIO_PARAMETER_HW_AV_EAC3_SYNC) ) {
return strdup ("HwAvSyncEAC3Supported=true");
} else if (strstr (keys, "hdmi_format") ) {
sprintf (temp_buf, "hdmi_format=%d", adev->hdmi_format);
return strdup (temp_buf);
} else if (strstr (keys, "digital_output_format") ) {
if (adev->hdmi_format == PCM) {
return strdup ("digital_output_format=pcm");
} else if (adev->hdmi_format == DD) {
return strdup ("digital_output_format=dd");
} else if (adev->hdmi_format == DDP) {
return strdup ("digital_output_format=ddp");
} else if (adev->hdmi_format == AUTO) {
return strdup ("digital_output_format=auto");
} else if (adev->hdmi_format == BYPASS) {
return strdup ("digital_output_format=bypass");
}
} else if (strstr (keys, "spdif_format") ) {
sprintf (temp_buf, "spdif_format=%d", adev->spdif_format);
return strdup (temp_buf);
} else if (strstr (keys, "hdmi_is_passthrough_active") ) {
sprintf (temp_buf, "hdmi_is_passthrough_active=%d", adev->hdmi_is_pth_active);
return strdup (temp_buf);
} else if (strstr (keys, "disable_pcm_mixing") ) {
sprintf (temp_buf, "disable_pcm_mixing=%d", adev->disable_pcm_mixing);
return strdup (temp_buf);
} else if (strstr (keys, "hdmi_encodings") ) {
struct format_desc *fmtdesc = NULL;
bool dd = false, ddp = false;
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
// query dd support
fmtdesc = &hdmi_desc->dd_fmt;
if (fmtdesc && fmtdesc->fmt == AML_HDMI_FORMAT_AC3)
dd = fmtdesc->is_support;
// query ddp support
fmtdesc = &hdmi_desc->ddp_fmt;
if (fmtdesc && fmtdesc->fmt == AML_HDMI_FORMAT_DDP)
ddp = fmtdesc->is_support;
sprintf (temp_buf, "hdmi_encodings=%s", "pcm;");
if (ddp)
sprintf (temp_buf, "ac3;eac3;");
else if (dd)
sprintf (temp_buf, "ac3;");
return strdup (temp_buf);
} else if (strstr (keys, "is_passthrough_active") ) {
bool active = false;
pthread_mutex_lock (&adev->lock);
// "is_passthrough_active" is used by amnuplayer to check is current audio hal have passthrough instance(DD/DD+/DTS)
// if already have one passthrough instance, it will not invoke another one.
// While in HDMI plug off/in test case. Player will query "is_passthrough_active" before adev->usecase_masks finally settle
// So Player will have a chance get a middle-term value, which is wrong.
// now only one passthrough instance, do not check
/* if (adev->usecase_masks & RAW_USECASE_MASK)
active = true;
else if (adev->audio_patch && (adev->audio_patch->aformat == AUDIO_FORMAT_E_AC3 \
|| adev->audio_patch->aformat == AUDIO_FORMAT_AC3) ) {
active = true;
} */
pthread_mutex_unlock (&adev->lock);
sprintf (temp_buf, "is_passthrough_active=%d",active);
return strdup (temp_buf);
} else if (strstr(keys, "hal_param_cec_control_tv")) {
adev_get_cec_control_object(adev, temp_buf);
return strdup (temp_buf);
} else if (!strcmp(keys, "SOURCE_GAIN")) {
sprintf(temp_buf, "source_gain = %f %f %f %f %f", adev->eq_data.s_gain.atv, adev->eq_data.s_gain.dtv,
adev->eq_data.s_gain.hdmi, adev->eq_data.s_gain.av, adev->eq_data.s_gain.media);
ALOGD("source_gain = %f %f %f %f %f", adev->eq_data.s_gain.atv, adev->eq_data.s_gain.dtv,
adev->eq_data.s_gain.hdmi, adev->eq_data.s_gain.av, adev->eq_data.s_gain.media);
return strdup(temp_buf);
} else if (!strcmp(keys, "POST_GAIN")) {
sprintf(temp_buf, "post_gain = %f %f %f", adev->eq_data.p_gain.speaker, adev->eq_data.p_gain.spdif_arc,
adev->eq_data.p_gain.headphone);
return strdup(temp_buf);
} else if (strstr(keys, "dolby_ms12_enable")) {
int ms12_enable = (eDolbyMS12Lib == adev->dolby_lib_type_last);
ALOGI("ms12_enable :%d", ms12_enable);
sprintf(temp_buf, "dolby_ms12_enable=%d", ms12_enable);
return strdup(temp_buf);
} else if (strstr(keys, "isReconfigA2dpSupported")) {
return strdup("isReconfigA2dpSupported=1");
} else if (!strcmp(keys, "SOURCE_MUTE")) {
sprintf(temp_buf, "source_mute = %d", adev->source_mute);
return strdup(temp_buf);
} else if (strstr (keys, "stream_dra_channel") ) {
struct aml_audio_patch *dtv_patch = get_dev_patch_by_source(adev, SRC_DTV);
if (dtv_patch) {
if (dtv_patch->dtv_NchOriginal > 8 || dtv_patch->dtv_NchOriginal < 1) {
sprintf (temp_buf, "0.0");
} else {
sprintf(temp_buf, "channel_num=%d.%d", dtv_patch->dtv_NchOriginal,dtv_patch->dtv_lfepresent);
ALOGD ("temp_buf=%s\n", temp_buf);
}
} else {
sprintf (temp_buf, "0.0");
}
return strdup(temp_buf);
}
else if (strstr(keys, "HDMI Switch")) {
sprintf(temp_buf, "HDMI Switch=%d", (OUTPORT_HDMI == adev->active_outport || is_HDMI_connected(adev) == 1));
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
} else if (!strcmp(keys, "decoder_mute")) {
sprintf(temp_buf, "decoder_mute = %d", adev->decoder_mute);
return strdup(temp_buf);
} else if (!strcmp(keys, "parental_control_av_mute")) {
sprintf(temp_buf, "parental_control_av_mute = %d", adev->patch_mute);
return strdup(temp_buf);
}
else if (strstr (keys, "diaglogue_enhancement") ) {
sprintf(temp_buf, "diaglogue_enhancement=%d", adev->ms12.ac4_de);
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
}
else if (strstr(keys, "audioindicator")) {
get_audio_indicator(adev, temp_buf);
return strdup(temp_buf);
}
else if (strstr (keys, "hal_param_get_earctx_attend_type") ) {
int type = aml_audio_earctx_get_type(adev);
sprintf(temp_buf, "hal_param_get_earctx_attend_type=%d", type);
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
}
else if (strstr (keys, "hal_param_get_earctx_cds") ) {
char cds[AUDIO_HAL_CHAR_MAX_LEN] = {0};
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
earctx_fetch_cds(&adev->alsa_mixer, cds, 0, hdmi_desc);
sprintf(temp_buf, "hal_param_get_earctx_cds=%s", cds);
return strdup(temp_buf);
}
else if (strstr(keys, "audio_information")) {
if ((0 >= adev->audio_hal_info.channel_number) || (0 >= adev->audio_hal_info.sample_rate)) {
adev->audio_hal_info.sample_rate = 48000;
adev->audio_hal_info.channel_number = 2;
}
snprintf(temp_buf, sizeof(temp_buf), "audio_information=%d,%d", adev->audio_hal_info.sample_rate, adev->audio_hal_info.channel_number);
return strdup(temp_buf);
}
#ifdef USE_DTV
else if (strstr (keys, "hal_param_dtv_demuxidbase") ) {
sprintf(temp_buf, "hal_param_dtv_demuxidbase=%d", dtv_get_demuxidbase());
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
} else if (strstr (keys, "ac4_active_pres_id")) {
int active_id_offset = -1;
struct aml_stream_out *aml_out = NULL;
bool is_ac4_stream_present = false;
for (int i = 0 ; i < STREAM_NUM_MAX; i++) {
aml_out = adev->active_outputs[i];
if (aml_out && AUDIO_FORMAT_AC4 == aml_out->hal_internal_format) {
AM_LOGI("aml_out:%p format:%x", aml_out, aml_out->hal_internal_format);
is_ac4_stream_present = true;
break;
}
}
if ((eDolbyMS12Lib == adev->dolby_lib_type) && is_ac4_stream_present) {
//should use the dolby_ms12_get_ac4_active_presentation() before or after get_dolby_ms12_cleanup()
pthread_mutex_lock(&adev->ms12.lock);
if (adev->ms12.dolby_ms12_enable && (0 == get_aml_ac4_active_presentation((struct audio_stream_out *)aml_out, &active_id_offset))) {
ALOGI ("get_aml_ac4_active_presentation index is %d", active_id_offset);
}
pthread_mutex_unlock(&adev->ms12.lock);
}
sprintf(temp_buf, "ac4_active_pres_id=%d", active_id_offset);
return strdup(temp_buf);
} else if (strstr (keys, "hal_param_dtv_es_pts_dts_flag")) {
int flag = dtv_path_get_es_pts_dts_flag(0);
sprintf(temp_buf, "hal_param_dtv_es_pts_dts_flag=%d", flag);
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
}
#endif
else if (strstr (keys, "codecsupport")) {
//return strdup(aml_get_codec_support((char*)(&keys[strlen("codecsupport") + 1]))? "true": "false");
return strdup(aml_get_codec_support_runtime(adev, (char*)(&keys[strlen("codecsupport") + 1]))? "true": "false");
} else if (strstr(keys, "dolby_lib_type")) {
ALOGI("dolby_lib_type: %d", adev->dolby_lib_type);
sprintf(temp_buf, "dolby_lib_type=%d", adev->dolby_lib_type);
return strdup(temp_buf);
} else if (!strncmp(keys, "audio_info=", 11)) {
int audio_info_type = 0;
keys += 11;
sscanf(keys ,"%d", &audio_info_type);
switch (audio_info_type) {
case AUDIO_PARAMTYPE_DEC_STATUS:
sprintf (temp_buf,"audio_information_d=%d", adev->audio_hal_info.is_decoding);
break;
case AUDIO_PARAMTYPE_PTS:
//AUDIO_PARAMTYPE_PTS
break;
case AUDIO_PARAMTYPE_SAMPLE_RATE:
sprintf (temp_buf,"audio_information_s=%d", adev->audio_hal_info.sample_rate);
break;
case AUDIO_PARAMTYPE_CHANNEL_MODE:
sprintf (temp_buf,"audio_information_c=%d", adev->audio_hal_info.channel_number);
break;
case AUDIO_PARAMTYPE_DIGITAL_AUDIO_INFO:
//AUDIO_PARAMTYPE_DIGITAL_AUDIO_INFO;
break;
default:
break;
}
ALOGI("temp_buf %s", temp_buf);
return strdup(temp_buf);
} else if (strstr (keys, "get_sink_capability")) {
return strdup(get_sink_all_capability(adev, temp_buf));
} else if (strstr(keys, "get_device_capability")) { //only get the ms12 atmos enable or not
if (eDolbyMS12Lib == adev->dolby_lib_type && adev->dolby_ms12_audio_config == MS12_CONFIG_Z && is_dap_surround_virtualizer_support()) {
sprintf(temp_buf,"device_cap=ddp_atmos");
} else {
sprintf(temp_buf,"device_cap=none");
}
return strdup(temp_buf);
} else if (strstr (keys, "get_audio_playing_status")) {
sprintf(temp_buf, "audio_playing_status=%d", aml_is_audio_playing(adev));
return strdup(temp_buf);
} else if (strstr (keys, "get_output_port_information")) {
return strdup(get_output_port_information(adev, temp_buf));
} else if (strstr(keys, "audio_server_status")) {
sprintf(temp_buf, "ready");
ALOGI("audio_server_status: %s", temp_buf);
return strdup(temp_buf);
} else if (strstr (keys, "get_audio_support_info")) {
char effect_list[AUDIO_HAL_CHAR_MAX_LEN] = {0};
aml_report_audio_support_list_to_ui(adev, effect_list, AUDIO_HAL_CHAR_MAX_LEN);
sprintf(temp_buf, "audio_support_info: %s", effect_list);
ALOGI("audio_support_list_to_ui: %s", temp_buf);
return strdup(temp_buf);
} else if (strstr (keys, "speaker_mute")) {
bool is_muted = is_output_device_muted(adev, AUDIO_DEVICE_OUT_SPEAKER, false);
sprintf (temp_buf, "speaker_mute=%d", is_muted);
return strdup (temp_buf);
} else if (strstr (keys, "headphone_mute")) {
bool is_muted = is_output_device_muted(adev, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, false);
sprintf (temp_buf, "headphone_mute=%d", is_muted);
return strdup (temp_buf);
} else if (strstr (keys, "Audio spdif mute")) {
bool is_muted = is_output_device_muted(adev, AUDIO_DEVICE_OUT_SPDIF, false);
sprintf (temp_buf, "Audio spdif mute=%d", is_muted);
return strdup (temp_buf);
} else if (strstr (keys, "ARC eARC mute")) {
bool is_muted = is_output_device_muted(adev, AUDIO_DEVICE_OUT_HDMI_ARC, false);
sprintf (temp_buf, "ARC eARC mute=%d", is_muted);
return strdup (temp_buf);
} else if (strstr (keys, "usb_spk_mute")) {
#ifdef SUPPORT_EXTERNAL_OUTPUT
if (adev->usb_handle) {
bool is_muted = aml_get_external_device_mute(adev->usb_handle);
sprintf (temp_buf, "usb_spk_mute=%d", is_muted);
return strdup (temp_buf);
}
#endif
} else if (strstr (keys, "bt_mute")) {
#ifdef SUPPORT_EXTERNAL_OUTPUT
if (adev->usb_handle) {
bool is_muted = aml_get_external_device_mute(adev->bt_handle);
sprintf (temp_buf, "bt_mute=%d", is_muted);
return strdup (temp_buf);
}
#endif
} else if (strstr (keys, "Master volume")) {
int master_volume = 0;
if (is_STB(adev)) {
int dac_vol[2] = {0};
aml_mixer_ctrl_get_array(&adev->alsa_mixer, AML_MIXER_ID_DAC_DIGITAL_VOLUME, &dac_vol, 2);
master_volume = dac_vol[0];
} else {
master_volume = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_AMP_MASTER_VOLUME);
}
sprintf (temp_buf, "Master volume=%d", master_volume);
return strdup (temp_buf);
} else if (strstr (keys, "Audio hdmi-out mute")) {
bool is_muted = is_output_device_muted(adev, AUDIO_DEVICE_OUT_HDMI, false);
sprintf (temp_buf, "Audio hdmi-out mute=%d", is_muted);
return strdup (temp_buf);
} else if (strstr(keys, "get_cap_info=")) {
/*Only for box to get the aud_cap node's info
e.g. PCM max_channels=2 sampling_rates=32000|44100|48000*/
int tmp = get_hdmi_cap_info(adev, &keys[13], temp_buf, AUDIO_HAL_CHAR_MAX_LEN);
if (tmp < 0) {
strcat(temp_buf, "This format is invalid");
}
ALOGI("ret: val %s,key %s \n", temp_buf, &keys[13]);
return strdup (temp_buf);
} else if (strstr(keys, "hdmi_current_video_latency")) {
int v_ltcy = audio_patch_get_current_video_latency(adev);
AM_LOGI("v_ltcy(%d)", v_ltcy);
sprintf (temp_buf, "hdmi_current_video_latency=%d", v_ltcy);
return strdup (temp_buf);
} else if (strstr(keys, "hdmi_current_audio_latency")) {
int a_ltcy = audio_patch_get_current_audio_latency(adev);
AM_LOGI("a_ltcy(%d)", a_ltcy);
sprintf (temp_buf, "hdmi_current_audio_latency=%d", a_ltcy);
return strdup (temp_buf);
} else if (strstr(keys, "hdmi_audio_output_compensated")) {
sprintf (temp_buf, "hdmi_audio_output_compensated=%d", 1);
return strdup (temp_buf);
} else if (strstr (keys, "mpegh_audiosceneconfig") ) {
char *xmlbuf = aml_mpegh_getxmlsceneinfo(adev);
if (NULL != xmlbuf) {
int xmlsize = strlen(xmlbuf);
AM_LOGI("[size:%d]", xmlsize);
return strdup (xmlbuf);
}
return strdup ("");
} else if (strstr(keys, "is_dolby_atmos")) {
snprintf(temp_buf, AUDIO_HAL_CHAR_MAX_LEN, "is_dolby_atmos=%d", adev->audio_hal_info.is_dolby_atmos);
ALOGD("temp_buf %s", temp_buf);
return strdup(temp_buf);
} else if (strstr(keys, "mpegh_persistency_ctx")) {
if (adev->mpegh_ui_persistencemem) {
aml_mpegh_base64_encode(adev->mpegh_ui_persistencemem, adev->mpegh_ui_persistencememsize, adev);
//aml_dump_audio_bitstreams("/data/vendor/audiohal/get_mpegh_persistency_ctx.xml", adev->mpegh_base64_encode_mem, BASE64_BUFSIZE);
return strdup (adev->mpegh_base64_encode_mem);
}
} else if (strstr(keys, "hal_param_get_earc_status")) {
if (is_STB(adev)) {
AM_LOGE("STB is not support earc!");
} else {
int earc_status = -1;
earc_status = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_EARC_TX_ATTENDED_TYPE);
sprintf(temp_buf, "hal_param_get_earc_status=%d", earc_status);
return strdup(temp_buf);
}
} else if (adev->aml_ai_audio_type != eAmlAiAudioUnsupported) {
char *parm = NULL;
AmlAiValues ret_values;
parm = strstr(keys, "ai_audio_result");
if (parm) {
ret_values = aml_ai_audio_get_param(AML_AI_TYPE_AIAQ, AIAQ_RESULT);
sprintf(temp_buf, "aq_tuning:ai_audio_result=%d", ret_values.value);
return strdup(temp_buf);
}
parm = strstr(keys, "audio_enhancement_enable");
if (parm) {
ret_values = aml_ai_audio_get_param(AML_AI_TYPE_ENHANCEMENT, ENHANCEMENT_ENABLE);
sprintf(temp_buf, "audio_enhancement_enable=%d", ret_values.value);
return strdup(temp_buf);
}
parm = strstr(keys, "audio_enhancement_gain");
if (parm) {
ret_values = aml_ai_audio_get_param(AML_AI_TYPE_ENHANCEMENT, ENHANCEMENT_GAIN);
sprintf(temp_buf, "audio_enhancement_gain=%d", ret_values.value);
return strdup(temp_buf);
}
parm = strstr(keys, "audio_vocal_isolate_enable");
if (parm) {
ret_values = aml_ai_audio_get_param(AML_AI_TYPE_VOCAL_ISOLATE, VOCAL_ISOLATE_ENABLE);
sprintf(temp_buf, "audio_vocal_isolate_enable=%d", ret_values.value);
return strdup(temp_buf);
}
parm = strstr(keys, "audio_vocal_gain");
if (parm) {
ret_values = aml_ai_audio_get_param(AML_AI_TYPE_VOCAL_ISOLATE, VOCAL_ISOLATE_GAIN);
sprintf(temp_buf, "audio_vocal_gain=%f", ret_values.value_f);
return strdup(temp_buf);
}
} else if (strstr(keys, "HDMIIN audio samplerate")) {
int stable = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_HDMI_IN_AUDIO_STABLE);
if (!stable) {
AM_LOGW("HDMI in audio is not stable, get samplerate after stable");
return strdup("");
}
int cur_sr = aml_mixer_ctrl_get_int (&adev->alsa_mixer, AML_MIXER_ID_HDMI_IN_SAMPLERATE);
switch (cur_sr) {
case 0:
cur_sr = 0;
break;
case 1:
cur_sr = 32000;
break;
case 2:
cur_sr = 44100;
break;
case 3:
cur_sr = 48000;
break;
case 4:
cur_sr = 88200;
break;
case 5:
cur_sr = 96000;
break;
case 6:
cur_sr = 176400;
break;
case 7:
cur_sr = 192000;
break;
default:
cur_sr = 48000;
break;
}
sprintf (temp_buf, "HDMIIN audio samplerate=%d", cur_sr);
return strdup (temp_buf);
} else if (strstr(keys, "HDMIIN Audio Type")) {
int cur_type = LPCM;
if (get_hdmiin_audio_mode(&adev->alsa_mixer) != HDMIIN_MODE_I2S) {
cur_type = aml_mixer_ctrl_get_int (&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_TYPE);
} else if (aml_mixer_ctrl_get_int (&adev->alsa_mixer, AML_MIXER_ID_AUDIO_PCPD_MONITOR_ENABLE)) {
cur_type = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_PCPD_MONITOR_DATA_TYPE);
}
sprintf (temp_buf, "HDMIIN Audio Type=%d", cur_type);
return strdup (temp_buf);
} else if (strstr(keys, "Audiopatch State")) {
int hdmi_state = 0;
struct aml_stream_out *aml_out = NULL;
for (int i = 0 ; i < STREAM_NUM_MAX; i++) {
aml_out = adev->active_outputs[i];
if (aml_out && HDMI_SRC == aml_out->source_type) {
hdmi_state = 1;
}
}
sprintf (temp_buf, "Audiopatch State=%d", hdmi_state);
return strdup (temp_buf);
} else {
ALOGE("%s() keys %s is not supported! TODO!\n", __func__, keys);
}
return strdup("");
}
static int adev_init_check (const struct audio_hw_device *dev __unused)
{
return 0;
}
static int adev_set_voice_volume (struct audio_hw_device *dev __unused, float volume __unused)
{
return 0;
}
static int adev_set_master_volume (struct audio_hw_device *dev, float volume)
{
if ((volume > 1.0) || (volume < 0.0) || (dev == NULL)) {
return -EINVAL;
}
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
int i;
pthread_mutex_lock (&adev->lock);
adev->master_volume = volume;
if (adev->master_mute)
adev->master_mute = false;
for (i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
struct audio_stream_out *out = (struct audio_stream_out *)aml_out;
if (aml_out) {
out_set_volume_l(out, aml_out->volume_l_org, aml_out->volume_r_org);
}
}
ALOGI("%s() volume = %f, active_outport = %d", __FUNCTION__, volume, adev->active_outport);
pthread_mutex_unlock (&adev->lock);
return 0;
}
int adev_update_decoder_mute_state (struct audio_hw_device *dev)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
int i;
if (dev == NULL) {
return -EINVAL;
}
AM_LOGI("decoder_mute:%d", adev->decoder_mute);
pthread_mutex_lock (&adev->lock);
for (i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out && aml_out->stream_type != STREAM_AUX_INPUT) {
out_set_stream_mute((struct audio_stream_out *)aml_out, adev->decoder_mute, true);
}
}
pthread_mutex_unlock (&adev->lock);
return 0;
}
static int adev_get_master_volume (struct audio_hw_device *dev,
float *volume)
{
if ((volume == NULL) || (dev == NULL)) {
return -EINVAL;
}
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
*volume = adev->master_volume;
ALOGI("%s() volume = %f", __FUNCTION__, *volume);
return 0;
}
static int adev_set_master_mute (struct audio_hw_device *dev, bool muted)
{
if (dev == NULL) {
return -EINVAL;
}
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
ALOGI("%s, %d -> %d", __FUNCTION__, adev->master_mute, muted);
if (adev->master_mute != muted) {
if (muted) { //mute
adev_get_master_volume (dev, &adev->record_volume_before_mute); //Record the volume before mute
adev_set_master_volume (dev, 0); //set mute
} else {
adev_set_master_volume (dev, adev->record_volume_before_mute); //unmute
}
adev->master_mute = muted;
}
return 0;
}
static int adev_get_master_mute (struct audio_hw_device *dev, bool *muted)
{
if ((muted == NULL) || (dev == NULL)) {
return -EINVAL;
}
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
*muted = adev->master_mute;
ALOGI("%s() muted = %d", __FUNCTION__, *muted);
return 0;
}
static int adev_set_mode (struct audio_hw_device *dev, audio_mode_t mode)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
ALOGD ("%s(%p, %d)", __FUNCTION__, dev, mode);
pthread_mutex_lock (&adev->lock);
if (adev->mode != mode) {
adev->mode = mode;
select_mode (adev);
}
pthread_mutex_unlock (&adev->lock);
return 0;
}
static int adev_set_mic_mute (struct audio_hw_device *dev, bool state)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
adev->mic_mute = state;
return 0;
}
static int adev_get_mic_mute (const struct audio_hw_device *dev, bool *state)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
*state = adev->mic_mute;
return 0;
}
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev __unused,
const struct audio_config *config)
{
int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
size_t size;
ALOGD("%s: enter: channel_mask(%#x) rate(%d) format(%#x)", __func__,
config->channel_mask, config->sample_rate, config->format);
if (check_input_parameters(config->sample_rate, config->format, channel_count, AUDIO_DEVICE_NONE) != 0) {
return -EINVAL;
}
size = get_input_buffer_size(0, config->sample_rate, config->format, channel_count);
ALOGD("%s: exit: buffer_size = %zu", __func__, size);
return size;
}
static int adev_open_input_stream(struct audio_hw_device *dev,
audio_io_handle_t handle __unused,
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_in **stream_in,
audio_input_flags_t flags __unused,
const char *address __unused,
audio_source_t source)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_stream_in *in;
int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
int ret = 0;
AM_LOGD("%s: enter: devices(%#x) channel_mask(%#x) rate(%d) format(%#x) source(%d)", __func__,
devices, config->channel_mask, config->sample_rate, config->format, source);
if ((ret = check_input_parameters(config->sample_rate, config->format, channel_count, devices)) != 0) {
if (-ENOSYS == ret) {
AM_LOGE(" check_input_parameters input config Not Supported, and set to Default config(48000,2,PCM_16_BIT)");
config->sample_rate = DEFAULT_OUT_SAMPLING_RATE;
config->format = AUDIO_FORMAT_PCM_16_BIT;
config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
} else {
return -EINVAL;
}
}
in = (struct aml_stream_in *)aml_audio_calloc(1, sizeof(struct aml_stream_in));
if (!in) {
AM_LOGE(" calloc fail, return!!!");
return -ENOMEM;
}
in->device = devices & ~AUDIO_DEVICE_BIT_IN;
in->input_type = audio_input_get_type(in->device);
if (channel_count == 1)
// in fact, this value should be AUDIO_CHANNEL_OUT_BACK_LEFT(16u) according to VTS codes,
// but the macroname can be confusing, so I'd like to set this value to
// AUDIO_CHANNEL_IN_FRONT(16u) instead of AUDIO_CHANNEL_OUT_BACK_LEFT.
config->channel_mask = AUDIO_CHANNEL_IN_FRONT;
else if (channel_count == 2) {
config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
if (in->device & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
config->channel_mask = AUDIO_CHANNEL_IN_2POINT0POINT2;
}
}
if (in->device & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
if (access(AML_AEC_LIB_PATH, R_OK) != 0) {
AM_LOGE("libAudioSignalProcess.so not found.");
goto err;
}
}
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.set_gain = in_set_gain;
in->stream.read = in_read;
in->stream.get_capture_position = in_get_capture_position;
in->stream.get_input_frames_lost = in_get_input_frames_lost;
in->stream.get_active_microphones = in_get_active_microphones;
in->stream.common.add_audio_effect = in_add_audio_effect;
in->stream.common.remove_audio_effect = in_remove_audio_effect;
in->unique_ID = adev->next_unique_ID;
adev->next_unique_ID++;
in->dev = adev;
in->standby = 1;
in->requested_rate = config->sample_rate;
in->hal_channel_mask = config->channel_mask;
in->hal_format = config->format;
in->config.channels = channel_count;
in->source = source;
if (source == AUDIO_SOURCE_ECHO_REFERENCE) {
in->config.rate = PLAYBACK_CODEC_SAMPLING_RATE;
} else if (source == AUDIO_SOURCE_MIXER_OUT || source == AUDIO_SOURCE_ENCODER_OUT) {
audio_unit_acquire_port((audio_unit *)adev->mixer_unit, &in->capture_port, AUDIO_PORT_OUTPUT);
}
if (source == AUDIO_SOURCE_ENCODER_OUT) {
aml_encoder_config_prepare(&adev->encoder_ctx.enc_cfg);
if ((ret = aml_encoder_init(&adev->encoder_ctx.enc_handle, &adev->encoder_ctx.enc_cfg)) == 0) {
aml_enc_info_t enc_info = {0};
aml_encoder_getinfo(adev->encoder_ctx.enc_handle, AML_ENC_FRAME_SIZE, &enc_info);
adev->encoder_ctx.frame_len = enc_info.frame_size;
AM_LOGD("aml_encoder_init ok, frame_len %d", enc_info.frame_size);
} else
AM_LOGE("aml_encoder_init fail, ret: %d", ret);
}
switch (config->format) {
case AUDIO_FORMAT_PCM_16_BIT:
in->config.format = PCM_FORMAT_S16_LE;
break;
case AUDIO_FORMAT_PCM_32_BIT:
in->config.format = PCM_FORMAT_S32_LE;
break;
default:
break;
}
in->buffer = aml_audio_malloc(in->config.period_size * audio_stream_in_frame_size(&in->stream));
if (!in->buffer) {
ret = -ENOMEM;
ALOGE(" malloc fail, goto err!!!");
goto err;
}
memset(in->buffer, 0, in->config.period_size * audio_stream_in_frame_size(&in->stream));
if (in->device & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
adev->aml_aec = aec_create(AML_AEC_MIC_CHANNEL_NUM, pcm_config_aec);
}
*stream_in = &in->stream;
ALOGD("%s: exit", __func__);
return 0;
err:
#ifndef ANDROID_BUILD
if (in->resampler) {
release_resampler(in->resampler);
in->resampler = NULL;
}
#endif
if (in->buffer) {
aml_audio_free(in->buffer);
in->buffer = NULL;
}
aml_audio_free(in);
*stream_in = NULL;
return ret;
}
static void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
#ifndef BUILD_LINUX
if (remoteDeviceOnline()) {
kehwin_adev_close_input_stream(dev,stream);
return;
}
#endif
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_stream_in *in = (struct aml_stream_in *)stream;
ALOGD("%s: enter: dev(%p) stream(%p)", __func__, dev, stream);
in_standby(&stream->common);
#ifndef ANDROID_BUILD
if (in->resampler) {
release_resampler(in->resampler);
in->resampler = NULL;
}
#endif
pthread_mutex_lock (&in->lock);
if (in->buffer) {
aml_audio_free(in->buffer);
in->buffer = NULL;
}
pthread_mutex_unlock (&in->lock);
if (in->proc_buf) {
aml_audio_free(in->proc_buf);
}
if (in->ref_buf) {
aml_audio_free(in->ref_buf);
}
if (in->device & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
aec_destroy(adev->aml_aec);
}
if (in->source == AUDIO_SOURCE_MIXER_OUT || in->source == AUDIO_SOURCE_ENCODER_OUT) {
aml_port_command(in->capture_port, AUDIO_PORT_FLUSH, NULL);
audio_unit_release_port(adev->mixer_unit, aml_port_get_ID(in->capture_port), AUDIO_PORT_OUTPUT);
}
if (in->source == AUDIO_SOURCE_ENCODER_OUT) {
aml_encoder_release(adev->encoder_ctx.enc_handle);
}
aml_audio_free(stream);
ALOGD("%s: exit", __func__);
return;
}
/* must be called with hw device and output stream mutexes locked */
int do_output_standby_l(struct audio_stream *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = aml_out->dev;
ALOGI("[%s:%d] stream stream_type:%s", __func__, __LINE__,
streamtype2Str(aml_out->stream_type));
if (adev->dolby_lib_type != eDolbyMS12Lib) {
aml_audio_spdif_output_close_all();
}
aml_out->standby= 1;
if (true == aml_out->pause_status) {
aml_out->pause_status = false;//clear pause status
/* Avoid the inconsistent pause status between ms12 and audiohal, causes ms12 unable to resume */
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if ((adev->ms12.dolby_ms12_enable == true) &&
(aml_out->stream_type == STREAM_MAIN_INPUT)) {
dolby_ms12_main_resume((struct audio_stream_out *)stream);
}
}
}
return 0;
}
int out_standby_new(struct audio_stream *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
int status;
ALOGD("%s: enter", __func__);
aml_audio_trace_int("out_standby_new", 1);
if (aml_out->standby) {
ALOGI("already standby, do nothing");
aml_audio_trace_int("out_standby_new", 0);
return 0;
}
#if 0 // close this part, put the sleep to ms12 dolby_ms12_main_flush().
if (continuous_mode(aml_out->dev)
&& (aml_out->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC)) {
//1.audio easing duration is 32ms,
//2.one loop for schedule_run cost about 32ms(contains the hardware costing),
//3.if [pause, flush] too short, means it need more time to do audio easing
//so, the delay time for 32ms(pause is completed after audio easing is done) is enough.
//aml_audio_sleep(64000);
}
#endif
pthread_mutex_lock (&aml_out->dev->lock);
pthread_mutex_lock (&aml_out->lock);
status = do_output_standby_l(stream);
pthread_mutex_unlock (&aml_out->lock);
pthread_mutex_unlock (&aml_out->dev->lock);
ALOGI("%s exit", __func__);
aml_audio_trace_int("out_standby_new", 0);
return status;
}
audio_format_t get_output_format (struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = aml_out->dev;
audio_format_t output_format = aml_out->hal_internal_format;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
output_format = adev->sink_format;
} else if (eDolbyDcvLib == adev->dolby_lib_type) {
if (adev->hdmi_format > 0) {
if (adev->dual_spdif_support) {
if (aml_out->hal_internal_format == AUDIO_FORMAT_E_AC3 && adev->sink_format == AUDIO_FORMAT_E_AC3) {
if (adev->dolby_decode_enable == 1) {
output_format = AUDIO_FORMAT_AC3;
} else {
output_format = AUDIO_FORMAT_E_AC3;
}
} else {
output_format = adev->sink_format;
}
} else {
output_format = adev->sink_format;
}
} else
output_format = adev->sink_format;
}
return output_format;
}
bool is_low_latency_mode(struct aml_audio_device *aml_dev)
{
return aml_dev->low_latency_mode;
}
void update_low_latency_mode(struct aml_audio_device *aml_dev)
{
bool current_low_latency_mode = false;
struct aml_audio_patch * hdmi_patch = get_dev_patch_by_source(aml_dev, SRC_HDMIIN);
// Simultaneously meeting the following three conditions is the low latency mode
// 1.hdmi source
// 2.format is pcm
// 3.pic_mode is PQ_GAME
if (hdmi_patch && PQ_GAME == get_dev_pic_mode() && !hdmi_patch->IEC61937_format) {
current_low_latency_mode = true;
}
if (aml_dev->low_latency_mode != current_low_latency_mode) {
AM_LOGI("low_latency_mode changed from %d to %d, useSubMix %d",
aml_dev->low_latency_mode, current_low_latency_mode, aml_dev->useSubMix);
aml_dev->low_latency_mode_changed = true;
aml_dev->low_latency_mode = current_low_latency_mode;
//reconfig_dev_pic_mode_in(aml_dev, true);
if (aml_dev->useSubMix) {
reconfig_dev_pic_mode_out(true);
}
}
}
int reconfig_encoder(struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
AM_LOGI("dolby_lib_type(%d) is_dts(%d) dts_lib_type(%d) active_outport(%d)",
adev->dolby_lib_type, is_dts_format(aml_out->hal_internal_format),
get_dts_lib_type(), adev->active_outport);
if (eDolbyMS12Lib == adev->dolby_lib_type) {
audiohal_send_msg_2_ms12(&adev->ms12, MS12_MESG_TYPE_RESET_MS12_ENCODER);
} else if (is_dts_format(aml_out->hal_internal_format) &&
get_dts_lib_type() == eDTSXLib) {
//The dtsx lib will determine the output format based on the sink device's capability
//and whether it is passthrough. So when sink device's capability or passthrough option
//change, we need reset decoder to reconfig encoder.
if (dtsx_is_need_reset_decoder(stream)) {
aml_decoder_reset_wrapper(stream);
}
}
return 0;
}
//The dtsx lib will determine the output format based on the sink device's capability
//and whether it is passthrough. So when sink device's capability or passthrough option
//change, we need reset decoder.
int aml_decoder_reset_wrapper(struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
int ret = -1;
/*if decoder is init, we close it first*/
if (aml_out->aml_dec) {
aml_decoder_release(aml_out->aml_dec);
aml_out->aml_dec = NULL;
}
aml_audio_spdif_output_close_all();
ret = aml_decoder_init_wrapper(stream);
AM_LOGI("ret=%d", ret);
return ret;
}
int aml_decoder_init_wrapper(struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
int ret = -1;
memset(&aml_out->dec_config, 0, sizeof(aml_dec_config_t));
AM_LOGI("+<in>");
/*prepare the decoder config*/
ret = aml_decoder_config_prepare(stream, aml_out->hal_internal_format, &aml_out->dec_config);
if (ret < 0) {
AM_LOGE("config decoder error");
return ret;
}
ret = aml_decoder_init(&aml_out->aml_dec, aml_out->hal_internal_format, (aml_dec_config_t *)&aml_out->dec_config);
if (ret < 0) {
AM_LOGE("aml_decoder_init failed");
return ret;
}
if (eDolbyMS12Lib != adev->dolby_lib_type) {
pthread_mutex_lock(&adev->lock);
/*coverity[missing_lock]:adev->hw_mixer.start_buf does not need to be lock*/
if (!adev->hw_mixer.start_buf) {
aml_hw_mixer_init(&adev->hw_mixer);
} else {
aml_hw_mixer_reset(&adev->hw_mixer);
}
pthread_mutex_unlock(&adev->lock);
}
AM_LOGI("-<out> init success! ret=%d", ret);
return ret;
}
ssize_t mixer_main_buffer_write(struct audio_stream_out *stream, struct audio_buffer *abuffer)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = NULL;
int return_bytes = 0;
output_function parser_next_func = NULL;
if (!stream) {
AM_LOGE("invalid stream %p", stream);
return -1;
}
if (false == check_audio_buffer_valid(abuffer)) {
AM_LOGE("audio buffer checked fail");
return -1;
}
adev = aml_out->dev;
if (adev->debug_flag > 1) {
AM_LOGI("out:%p bytes:%"PRId32",format:%#x,with_header:%d",
aml_out, abuffer->size, abuffer->format,aml_out->with_header);
AM_LOGI("original_format:%#x, stream_type:%s",
abuffer->original_format, streamtype2Str(aml_out->stream_type));
}
if (get_debug_value(AML_DUMP_AUDIOHAL_ABUFFER)) {
aml_dump_audio_buffer_with_id("input_main.raw", abuffer, aml_out->unique_ID);
}
if (aml_out->standby && (eDolbyMS12Lib == adev->dolby_lib_type_last || !adev->useSubMix)) {
AM_LOGI("standby to unstandby");
aml_out->standby = false;
}
/* here to check if the audio output routing changed. */
if (adev->out_device != aml_out->out_device) {
AM_LOGI("output routing changed, adev_dev:%#x, out_dev:%#x",
adev->out_device, aml_out->out_device);
aml_out->out_device = adev->out_device;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
parser_next_func = aml_audio_ms12_render;
} else {
if (is_dts_format(aml_out->hal_internal_format) && get_dts_lib_type() == eDTSXLib) {
parser_next_func = aml_audio_nonms12_dec_render;
} else {
parser_next_func = aml_audio_nonms12_render;
}
}
//usac aac(xHE-AAC) doesn't need the parser
if ((AUDIO_FORMAT_HE_AAC_V2 == aml_out->hal_format || AUDIO_FORMAT_HE_AAC_V1 == aml_out->hal_format) && MEDIA_SRC == aml_out->source_type) {
return_bytes = parser_next_func(stream, abuffer);
if (return_bytes != abuffer->size) {
AM_LOGE("return err return_bytes(%d) != write_size(%"PRId32"), need check!!", return_bytes, abuffer->size);
}
} else {
return_bytes = audio_parser_process(stream, abuffer, parser_next_func);
}
aml_out->input_bytes_size += return_bytes;
aml_out->last_pts = abuffer->pts;
if (return_bytes > 0) {
size_t frame_size = audio_stream_out_frame_size((const struct audio_stream_out *)stream);
if (frame_size > 0) {
aml_out->frame_write_sum += (size_t)return_bytes / frame_size;
}
clock_gettime(CLOCK_MONOTONIC, &aml_out->timestamp);
aml_out->lasttimestamp.tv_sec = aml_out->timestamp.tv_sec;
aml_out->lasttimestamp.tv_nsec = aml_out->timestamp.tv_nsec;
if (eDolbyMS12Lib == adev->dolby_lib_type && adev->ms12.dolby_ms12_enable) {
unsigned int alsa_latency_frame = adev->ms12.latency_frame;
if (adev->compensate_video_enable) {
alsa_latency_frame = 0;
}
if (frame_size > 0 && aml_out->ms12_dec_handle) {
uint64_t main_bytes_consumed = dolby_ms12_get_main_bytes_consumed(
(struct audio_stream_out *)stream);
aml_out->last_frames_position = main_bytes_consumed / frame_size;
} else {
aml_out->last_frames_position = aml_out->frame_write_sum;
}
if (aml_out->last_frames_position >= alsa_latency_frame) {
aml_out->last_frames_position -= alsa_latency_frame;
}
AM_LOGI_IF(adev->debug_flag > 1,
"main presentation %"PRIu64" frame_write_sum %"PRIu64" alsa_latency %u",
aml_out->last_frames_position, aml_out->frame_write_sum, alsa_latency_frame);
}
}
AM_LOGI_IF(adev->debug_flag, "return %d!", return_bytes);
return return_bytes;
}
ssize_t mixer_aux_buffer_write(struct audio_stream_out *stream, struct audio_buffer *abuffer)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = NULL;
const void *buffer = NULL;
size_t bytes = 0, frame_size = 0, in_frames = 0, bytes_remaining = 0, bytes_written = 0;
int ret = 0, retry = 0;
unsigned int alsa_latency_frame = 0;
bool is_deep_buf = false;
struct timespec ts;
struct audio_buffer abuf;
if (!stream) {
AM_LOGE ("invalid stream %p\n", stream);
return -1;
}
if (false == check_audio_buffer_valid(abuffer)) {
AM_LOGE("audio buffer checked fail");
return -1;
}
adev = aml_out->dev;
buffer = abuffer->buffer;
bytes = abuffer->size;
frame_size = audio_stream_out_frame_size(stream);
in_frames = bytes / frame_size;
bytes_remaining = bytes;
is_deep_buf = (STREAM_DEEP_INPUT == aml_out->stream_type) ? true : false;
memcpy(&abuf, abuffer, sizeof(abuf));
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
AM_LOGI_IF(adev->debug_flag, "[%lld.%9ld -- %d] mixer_aux_buffer_write %zu, is_deep_buf:%d", (long long)ts.tv_sec, ts.tv_nsec, gettid(), bytes, is_deep_buf);
if (adev->debug_flag > 1) {
ALOGD("%s:%d size:%zu, dolby_lib_type:0x%x, frame_size:%zu, is_deep_buf:%d",
__func__, __LINE__, bytes, adev->dolby_lib_type, frame_size, is_deep_buf);
}
if (aml_out->standby) {
ALOGI("%s(), standby to unstandby", __func__);
aml_out->audio_data_handle_state = AUDIO_DATA_HANDLE_START;
aml_out->standby = false;
}
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
aml_dump_audio_bitstreams_with_id("input_aux.raw", buffer, bytes, aml_out->unique_ID);
}
// convert data to 48KHz
if (aml_out->hal_rate != OUTPUT_ALSA_SAMPLERATE ) {
audio_resample_config_t cfg = {
.aformat = aml_out->hal_internal_format,
.channels = aml_out->hal_ch,
.input_sr = aml_out->hal_rate,
.output_sr = OUTPUT_ALSA_SAMPLERATE,
};
ret = aml_audio_resample_process_ex(&aml_out->resample_handle, &cfg, buffer, bytes);
if (ret != 0) {
ALOGE("aml_audio_resample_process_ex failed");
} else {
buffer = aml_out->resample_handle->resample_buffer;
bytes_remaining = aml_out->resample_handle->resample_size;
}
}
if (AUDIO_FORMAT_MPEGH == adev->optical_format) {
struct aml_stream_out *aml_out = NULL;
bool mpegh_stream_exist = false;
for (int i = 0 ; i < STREAM_NUM_MAX; i++) {
aml_out = adev->active_outputs[i];
if (aml_out && is_mpegh_format(aml_out->hal_internal_format)) {
mpegh_stream_exist = true;
break;
}
}
if (mpegh_stream_exist) {
int used_size;
if (0 != aml_system_sound_process(aml_out->aml_dec, abuffer, &used_size)) {
AM_LOGE("aml_system_sound_process fail");
}
}
}/* else */
aml_out->input_bytes_size += bytes;
aml_out->frame_write_sum += in_frames;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
if (adev->a2dp_no_reconfig_ms12 > 0) {
uint64_t curr = aml_audio_get_systime();
if (adev->a2dp_no_reconfig_ms12 <= curr)
adev->a2dp_no_reconfig_ms12 = 0;
}
/* audio zero data detect, and do fade in */
if (adev->is_netflix) {
aml_audio_data_handle(stream, buffer, bytes_remaining);
}
// SUSP-008-TC2 Test Steps 8 pop
if (adev->is_netflix_hide) {
audio_fade_func((void *)buffer, bytes_remaining, 0);
adev->netflix_hide_fadeout_startTime = aml_audio_get_systime()/1000;
adev->is_netflix_hide = false;
ALOGI("%s aux audio fade out. starTime:%"PRIu64"", __func__, adev->netflix_hide_fadeout_startTime);
} else {
if (adev->netflix_hide_fadeout_startTime) {
uint64_t currentTime = aml_audio_get_systime()/1000;
if (currentTime - adev->netflix_hide_fadeout_startTime < 500) {
memset((void *)buffer, 0, bytes_remaining);
} else {
adev->netflix_hide_fadeout_startTime = 0;
ALOGI("%s aux audio memset done. currentTime:%"PRIu64"", __func__, currentTime);
}
}
}
abuf.buffer = buffer;
abuf.size = bytes_remaining;
aml_ms12_loudness_input_control(aml_out, &abuf);
while (bytes_remaining && adev->ms12.dolby_ms12_enable && retry < 20) {
size_t used_size = 0;
if (is_deep_buf) {
if (adev->enable_deep_unit) {
if (aml_port_get_room(aml_out->deep_inport) > bytes_remaining) {
used_size = aml_port_push_buffer(aml_out->deep_inport, &abuf);
}
ret = 0;
//AM_LOGI("deep unit write %zu bytes", used_size);
} else {
ret = dolby_ms12_deep_buffer_process(stream, (char *)buffer + bytes_written, bytes_remaining, &used_size);
}
} else {
ret = dolby_ms12_system_process(stream, (char *)buffer + bytes_written, bytes_remaining, &used_size);
}
if (!ret) {
bytes_remaining -= used_size;
bytes_written += used_size;
retry = 0;
}
retry++;
if (bytes_remaining) {
//usleep(bytes_remaining * 1000000 / frame_size / out_get_sample_rate(&stream->common));
aml_audio_sleep(5000);
}
}
} else {
struct aml_stream_out *out_stream = NULL;
bool dts_stream_exist = false;
for (int i = 0 ; i < STREAM_NUM_MAX; i++) {
out_stream = adev->active_outputs[i];
if (out_stream && is_dts_format(out_stream->hal_internal_format)) {
dts_stream_exist = true;
break;
}
}
if (adev->useSubMix && (!dts_stream_exist || (dts_stream_exist && !adev->hw_mixer.start_buf))) {
bytes_written = out_write_direct_pcm(stream, buffer, bytes_remaining);
} else if (adev->hw_mixer.start_buf) {
//This data is for mixing with DTS. The output format(16 or 32 bits) of DTS is determined by adev->audio_process_bitwidth,
//Therefore, the data format will be converted to the DTS output format here to facilitate subsequent mixing
if (audio_bytes_per_sample(aml_out->hal_format) != adev->audio_process_bitwidth / 8) {
audio_format_t dst_format = (adev->audio_process_bitwidth==32) ? AUDIO_FORMAT_PCM_32_BIT:AUDIO_FORMAT_PCM_16_BIT;
audio_format_t src_format = aml_out->hal_format;
int src_samples = bytes_remaining / audio_bytes_per_sample(src_format);
int need_size = src_samples * audio_bytes_per_sample(dst_format);
if (adev->hw_mixer.tmp_buf_size < need_size) {
AM_LOGI("realloc hw_mixer.tmp_buf from (%d) to (%u)", adev->hw_mixer.tmp_buf_size, need_size);
adev->hw_mixer.tmp_buf = aml_audio_realloc(adev->hw_mixer.tmp_buf, need_size);
if (adev->hw_mixer.tmp_buf == NULL) {
AM_LOGE("realloc for hw_mixer.tmp_buf(%u) failed", need_size);
return bytes;
}
adev->hw_mixer.tmp_buf_size = need_size;
}
memcpy_by_audio_format(adev->hw_mixer.tmp_buf, dst_format, (const void *)buffer, src_format, src_samples);
bytes_written = aml_hw_mixer_write(&adev->hw_mixer, adev->hw_mixer.tmp_buf, need_size);
} else {
bytes_written = aml_hw_mixer_write(&adev->hw_mixer, buffer, bytes_remaining);
}
} else {
AM_LOGE("This case shouldn't have happened, there must be something wrong");
}
}
clock_gettime (CLOCK_MONOTONIC, &aml_out->timestamp);
aml_out->lasttimestamp.tv_sec = aml_out->timestamp.tv_sec;
aml_out->lasttimestamp.tv_nsec = aml_out->timestamp.tv_nsec;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/*
mixer_aux_buffer_write is called when the hw write is called another thread,for example
main write thread or ms12 thread. aux audio is coming from the audioflinger mixed thread.
we need calculate the system buffer latency in ms12 also with the alsa out latency.It is
48K 2 ch 16 bit audio.
*/
alsa_latency_frame = adev->ms12.latency_frame;
int system_latency = 0;
if (is_deep_buf) {
system_latency = dolby_ms12_get_deep_buffer_avail_frames(NULL);
} else {
if (adev->dev_ms12_version == eDolbyMS12_V282) {
system_latency = dolby_ms12_get_system_buffer_avail(NULL);
} else {
system_latency = dolby_ms12_get_system_buffer_avail(NULL) / frame_size;
}
}
if (adev->compensate_video_enable) {
alsa_latency_frame = 0;
}
aml_out->last_frames_position = aml_out->frame_write_sum - system_latency;
if (aml_out->last_frames_position >= alsa_latency_frame) {
aml_out->last_frames_position -= alsa_latency_frame;
}
if (adev->debug_flag > 1) {
AM_LOGI("is_deep_buf:%d stream audio presentation %"PRIu64" latency_frame %d.ms12 system latency_frame %d,total frame=%" PRId64 " %" PRId64 " ms",
is_deep_buf, aml_out->last_frames_position, alsa_latency_frame, system_latency,aml_out->frame_write_sum, aml_out->frame_write_sum/48);
}
}
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
AM_LOGI_IF(adev->debug_flag && !is_deep_buf, "[%lld.%9ld] write %zu bytes, system level %zu ms", (long long)ts.tv_sec, ts.tv_nsec, bytes, dolby_ms12_get_system_buffer_avail(NULL) / frame_size / 48);
AM_LOGI_IF(adev->debug_flag && is_deep_buf, "[%lld.%9ld] write %zu bytes, deep_buffer level %d ms", (long long)ts.tv_sec, ts.tv_nsec, bytes, dolby_ms12_get_deep_buffer_avail_frames(NULL) / 48);
return bytes;
}
ssize_t mixer_ad_buffer_write (struct audio_stream_out *stream, struct audio_buffer *abuffer)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = NULL;
int32_t return_size = 0;
struct audio_buffer abuffer_out = {0};
if (!stream) {
AM_LOGE ("invalid stream %p\n", stream);
return -1;
}
if (false == check_audio_buffer_valid(abuffer)) {
AM_LOGE("audio buffer checked fail");
return -1;
}
adev = aml_out->dev;
memcpy(&abuffer_out, abuffer, sizeof(abuffer_out));
AM_LOGI_IF(adev->debug_flag, "out %p, useSubMix:%d, db_lib:%d, original_format:0x%x, format:0x%x, stream_type:0x%x, in %"PRId32, aml_out,
adev->useSubMix, adev->dolby_lib_type, abuffer->original_format, abuffer->format, aml_out->stream_type, abuffer->size);
if (aml_out->standby) {
aml_out->standby = false;
}
return_size = audio_parser_process(stream, abuffer, aml_audio_ad_render);
AM_LOGI_IF(adev->debug_flag, "out %p, return %d!\n", aml_out, return_size);
return return_size;
}
ssize_t mixer_app_buffer_write(struct audio_stream_out *stream, struct audio_buffer *abuffer)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = NULL;
int ret = 0;
int buffer_frames = 0;
const void *buffer = NULL;
size_t bytes = 0, frame_size = 0, bytes_remaining = 0, bytes_written = 0;
int retry = 100;
if (!stream) {
AM_LOGE ("invalid stream %p\n", stream);
return -1;
}
if (false == check_audio_buffer_valid(abuffer)) {
AM_LOGE("audio buffer checked fail");
return -1;
}
adev = aml_out->dev;
buffer = abuffer->buffer;
bytes = abuffer->size;
frame_size = audio_stream_out_frame_size(stream);
bytes_remaining = bytes;
if (adev->debug_flag) {
ALOGD("[%s:%d] size:%zu, frame_size:%zu", __func__, __LINE__, bytes, frame_size);
}
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
aml_dump_audio_bitstreams_with_id("input_app.raw", buffer, bytes, aml_out->unique_ID);
}
if (eDolbyMS12Lib != adev->dolby_lib_type) {
if (!adev->useSubMix) {
AM_LOGW("Submix is disable now, app write isn't supported");
return bytes;
}
AM_LOGI_IF(adev->debug_flag, "dolby_lib_type:%d, is not ms12, app write to nonms12", adev->dolby_lib_type);
int return_bytes = out_write_direct_pcm(stream, buffer, bytes);
return return_bytes;
}
if (is_bypass_dolbyms12(stream)) {
ALOGW("[%s:%d] is_bypass_dolbyms12, not support app write", __func__, __LINE__);
return -1;
}
// convert data to 48KHz
if (aml_out->hal_rate != OUTPUT_ALSA_SAMPLERATE ) {
audio_resample_config_t cfg = {
.aformat = aml_out->hal_internal_format,
.channels = aml_out->hal_ch,
.input_sr = aml_out->hal_rate,
.output_sr = OUTPUT_ALSA_SAMPLERATE,
};
ret = aml_audio_resample_process_ex(&aml_out->resample_handle, &cfg, buffer, bytes);
if (ret != 0) {
ALOGE("aml_audio_resample_process_ex failed");
} else {
buffer = aml_out->resample_handle->resample_buffer;
bytes_remaining = aml_out->resample_handle->resample_size;
}
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
aml_ms12_loudness_input_control(aml_out, abuffer);
}
if (aml_out->flags & AUDIO_OUTPUT_FLAG_FAST) { //video drop data in miracast case,audio does it also.
if (dolby_ms12_get_app_buffer_frame(&buffer_frames) / 48 > 45) {
AM_LOGI_IF(adev->debug_flag, "fast mode write %zu bytes, app level %d ms", bytes, buffer_frames / 48);
return bytes;
}
}
while (bytes_remaining && adev->ms12.dolby_ms12_enable && retry > 0) {
size_t used_size = 0;
ret = dolby_ms12_app_process(stream, (char *)buffer + bytes_written, bytes_remaining, &used_size);
if (!ret) {
bytes_remaining -= used_size;
bytes_written += used_size;
}
retry--;
if (bytes_remaining) {
aml_audio_sleep(3000);
}
}
if (retry <= 10) {
ALOGE("[%s:%d] write retry=%d ", __func__, __LINE__, retry);
}
if (retry == 0 && bytes_remaining != 0) {
ALOGE("[%s:%d] write timeout 60 ms ", __func__, __LINE__);
bytes -= bytes_remaining;
}
if (adev->debug_flag) {
dolby_ms12_get_app_buffer_frame(&buffer_frames);
AM_LOGI("app level %d ms", buffer_frames / 48);
}
return bytes;
}
ssize_t mixer_hwmixer_buffer_write (struct audio_stream_out *stream, struct audio_buffer *abuffer)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = NULL;
int32_t return_size = 0;
if (!stream) {
AM_LOGE ("invalid stream %p\n", stream);
return -1;
}
adev = aml_out->dev;
AM_LOGI_IF(adev->debug_flag, "out %p, format:0x%x, stream_type:0x%x, in %"PRId32, aml_out, abuffer->format, aml_out->stream_type, abuffer->size);
if (aml_out->standby) {
aml_out->standby = false;
}
return_size = aml_audio_hwmixer_out_write(adev, abuffer);
AM_LOGI_IF(adev->debug_flag, "out %p, return %d!\n", aml_out, return_size);
return return_size;
}
/* must be called with hw device mutexes locked */
static int stream_type_validate_l(struct aml_stream_out *aml_out, uint32_t flags)
{
int ret = 0;
struct aml_audio_device *adev = adev_get_handle();
aml_out->flags = flags;
aml_out->enable_scaletempo = false;
/* decide aml_out->stream_type with aml_out->flags */
// hwsync case
if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) && (aml_out->hal_format != AUDIO_FORMAT_IEC61937)) {
if (is_digital_raw_format(aml_out->hal_format) || audio_is_linear_pcm(aml_out->hal_format)) {
aml_out->stream_type = STREAM_MAIN_INPUT;
} else {
ALOGE("No matching stream_type, need check!!");
}
}
// non hwsync cases
if (is_digital_raw_format(aml_out->hal_format)) {
aml_out->stream_type = STREAM_MAIN_INPUT;
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) && audio_is_linear_pcm(aml_out->hal_format)) {
if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
// AAudio case
aml_out->stream_type = STREAM_APP_INPUT;
} else {
//multi-channel LPCM or hi-res LPCM
aml_out->stream_type = STREAM_MAIN_INPUT;
}
} else if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
aml_out->stream_type = STREAM_APP_INPUT;
} else if (flags & AUDIO_OUTPUT_FLAG_FAST) {
if (adev->has_hwmixer) {
aml_out->stream_type = STREAM_HWMIXER_INPUT;
} else {
aml_out->stream_type = STREAM_APP_INPUT;
}
} else if (aml_out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
aml_out->stream_type = STREAM_DEEP_INPUT;
} else {
aml_out->stream_type = STREAM_AUX_INPUT;
}
//ad case
if (flags & AUDIO_OUTPUT_FLAG_AD_STREAM) {
aml_out->stream_type = STREAM_AD_INPUT;
}
/* decide aml_out->write with aml_out->stream_type */
if (adev->dolby_lib_type == eDolbyMS12Lib) {
switch (aml_out->stream_type) {
case STREAM_AUX_INPUT:
case STREAM_DEEP_INPUT:
aml_out->write = mixer_aux_buffer_write;
aml_out->inputPortType = AML_DOLBY_INPUT_SYSTEM;
AM_LOGI("mixer_aux_buffer_write!");
break;
case STREAM_APP_INPUT:
aml_out->write = mixer_app_buffer_write;
aml_out->inputPortType = AML_DOLBY_INPUT_APP;
AM_LOGI("mixer_app_buffer_write!");
break;
case STREAM_AD_INPUT:
aml_out->write = mixer_ad_buffer_write;
aml_out->inputPortType = AML_DOLBY_INPUT_AD;
AM_LOGI("mixer_ad_buffer_write!");
break;
case STREAM_MAIN_INPUT:
aml_out->write = mixer_main_buffer_write;
aml_out->inputPortType = AML_DOLBY_INPUT_MAIN;
aml_out->enable_scaletempo = true;
AM_LOGI("mixer_main_buffer_write!");
break;
case STREAM_HWMIXER_INPUT:
aml_out->write = mixer_hwmixer_buffer_write;
AM_LOGI("mixer_hwmixer_buffer_write!");
break;
default:
AM_LOGE("No matching aml_out->write_func !");
ret = -1;
break;
}
} else {
aml_out->inputPortType = get_input_port_type(&aml_out->audioCfg, aml_out->flags);
switch (aml_out->stream_type) {
case STREAM_AUX_INPUT:
case STREAM_DEEP_INPUT:
aml_out->write = mixer_aux_buffer_write;
AM_LOGI("mixer_aux_buffer_write !");
break;
case STREAM_APP_INPUT:
aml_out->write = mixer_app_buffer_write;
AM_LOGI("mixer_app_buffer_write !");
break;
case STREAM_AD_INPUT:
aml_out->write = mixer_ad_buffer_write;
aml_out->inputPortType = AML_DOLBY_INPUT_AD;
AM_LOGI("mixer_ad_buffer_write!");
break;
case STREAM_MAIN_INPUT:
aml_out->write = mixer_main_buffer_write;
aml_out->enable_scaletempo = true;
AM_LOGI("mixer_main_buffer_write !");
break;
case STREAM_HWMIXER_INPUT:
aml_out->write = mixer_hwmixer_buffer_write;
AM_LOGI("mixer_hwmixer_buffer_write!");
break;
default:
AM_LOGE("No matching aml_out->write_func !");
ret = -1;
break;
}
}
if (HDMI_SRC == aml_out->source_type || HDMI1_SRC == aml_out->source_type || AV_SRC == aml_out->source_type) {
aml_out->enable_scaletempo = false;
}
return ret;
}
static inline int add_active_output_stream(struct aml_audio_device *adev, struct aml_stream_out *aml_out)
{
int i = 0;
pthread_mutex_lock(&adev->active_outputs_lock);
for (i = 0; i < STREAM_NUM_MAX; i++) {
if (adev->active_outputs[i] == NULL) {
adev->active_outputs[i] = aml_out;
AM_LOGI("adev->active_outputs[%d] = aml_out", i);
break;
}
}
if (i == STREAM_NUM_MAX) {
AM_LOGE("adev->active_outputs[] is full");
pthread_mutex_unlock(&adev->active_outputs_lock);
return -1;
}
pthread_mutex_unlock(&adev->active_outputs_lock);
return 0;
}
static inline int remove_active_output_stream(struct aml_audio_device *adev, struct aml_stream_out *aml_out)
{
int i = 0;
pthread_mutex_lock(&adev->active_outputs_lock);
for (i = 0; i < STREAM_NUM_MAX; i++) {
if (adev->active_outputs[i] == aml_out) {
adev->active_outputs[i] = NULL;
AM_LOGI("set adev->active_outputs[%d] = NULL, removed stream", i);
break;
}
}
if (i == STREAM_NUM_MAX) {
AM_LOGE("stream is not in adev->active_outputs[]");
pthread_mutex_unlock(&adev->active_outputs_lock);
return -1;
}
pthread_mutex_unlock(&adev->active_outputs_lock);
return 0;
}
static int set_hdmi_format(struct aml_audio_device *adev, int val)
{
int ret = -1;
if (adev->hdmi_format == val) {
return 0;
}
adev->hdmi_format_updated = 1;
adev->hdmi_format = val;
/* update the DUT's EDID */
ret = update_edid_after_edited_audio_sad((struct audio_hw_device *)adev);
if (ret < 0) {
ALOGE("[%s:%d], An error occurred during updating the DUT's EDID!", __func__, __LINE__);
return ret;
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
get_sink_format(adev);
audiohal_send_msg_2_ms12(&adev->ms12, MS12_MESG_TYPE_RESET_MS12_ENCODER);
if (adev->out_device & AUDIO_DEVICE_OUT_ALL_A2DP) {
adev->a2dp_no_reconfig_ms12 = aml_audio_get_systime() + 2000000;
}
}
ALOGI("update HDMI format: %d\n", adev->hdmi_format);
return 0;
}
static int out_write_pcm_float_to_s32(struct aml_stream_out *aml_out,
const void **write_buf, size_t *write_bytes)
{
if (*write_bytes == 0) {
return 0;
}
const size_t samples = *write_bytes / audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
const size_t out_bytes = samples * audio_bytes_per_sample(AUDIO_FORMAT_PCM_32_BIT);
if (samples == 0 || (*write_bytes % audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT)) != 0) {
return -EINVAL;
}
if (aml_out->float_to_s32_tmp_buf_size < out_bytes) {
void *new_buf = aml_audio_realloc(aml_out->float_to_s32_tmp_buf, out_bytes);
if (new_buf == NULL) {
return -ENOMEM;
}
aml_out->float_to_s32_tmp_buf = new_buf;
aml_out->float_to_s32_tmp_buf_size = out_bytes;
}
memcpy_by_audio_format(aml_out->float_to_s32_tmp_buf, AUDIO_FORMAT_PCM_32_BIT,
*write_buf, AUDIO_FORMAT_PCM_FLOAT, samples);
*write_buf = aml_out->float_to_s32_tmp_buf;
*write_bytes = out_bytes;
return 0;
}
/* out_write entrance: every write goes in here. */
ssize_t out_write_new(struct audio_stream_out *stream,
const void *buffer,
size_t bytes)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = aml_out->dev;
ssize_t return_bytes = 0;
struct audio_buffer abuffer = {0};
size_t pheader_cost = 0;
int pheader_outsize = 0;
uint64_t cur_pts = HWSYNC_PTS_NA;
size_t write_bytes = bytes;
const void *write_buf = buffer;
size_t bytes_cost = 0;
size_t total_bytes = bytes;
bool need_reconfig_encoder = false;
if (aml_out->source_type == MEDIA_SRC && !audio_is_linear_pcm(aml_out->hal_format)) {
aml_set_thread_priority("out_write_new", pthread_self(), adev->out_write_thread_priority);
}
if (NULL == buffer) {
ALOGE("[%s:%d], buffer is NULL, out_stream(%p) position(%zu)", __func__, __LINE__, stream, bytes);
return bytes;
}
#ifdef NO_SERVER
enum digital_format digital_mode = PCM;
enum OUT_PORT output_sel = OUTPORT_SPEAKER;
int ret = 0;
digital_mode = get_digital_mode(&adev->alsa_mixer);
ret = set_hdmi_format(adev, digital_mode);
if (ret < 0) {
ALOGE("[%s:%d], An error occurred during setting hdmi_format !", __func__, __LINE__);
return ret;
}
output_sel = get_output_select(&adev->alsa_mixer);
if (aml_out->is_sink_format_prepared && output_sel != adev->active_outport) {
adev->active_outport = output_sel;
get_sink_format(adev);
} else {
adev->active_outport = output_sel;
}
#endif
/* Confirm whether to enable mch pcm output */
pthread_mutex_lock(&adev->audio_mixer_lock);
if (aml_out->standby && adev->useSubMix) {
if (aml_need_mch_pcm_output(adev)) {
mixer_enable_multich_output(adev->audio_mixer, true);
} else {
/* need to close multi-pcm alsa handle, then npcm can use it. */
mixer_enable_multich_output(adev->audio_mixer, false);
}
}
/* init submix input port when input isn't main */
if (adev->useSubMix && aml_out->inputPortID == -1 && (aml_out->stream_type == STREAM_AUX_INPUT ||
aml_out->stream_type == STREAM_APP_INPUT || aml_out->stream_type == STREAM_DEEP_INPUT)) {
init_mixer_input_port(adev->audio_mixer, &aml_out->audioCfg, aml_out->flags,
on_notify_cbk, aml_out, on_input_avail_cbk, aml_out, 1.0);
AM_LOGI("[Submix]port:%s, inputPortID:%d", mixerInputType2Str(get_input_port_type(&aml_out->audioCfg, aml_out->flags)), aml_out->inputPortID);
}
pthread_mutex_unlock(&adev->audio_mixer_lock);
adev->debug_flag = aml_audio_get_debug_flag();
AM_LOGI_IF(adev->debug_flag, "+<IN>out_stream(%p) position(%zu)", stream, bytes);
/*move it from open function, because when hdmi hot plug, audio service will
* call many times open/close to query the hdmi capability, this will affect the
* sink format
*/
if (!aml_out->is_sink_format_prepared) {
need_reconfig_encoder = true;
aml_out->card = alsa_device_get_card_index();
if (!is_TV(adev)) {
if (is_use_spdifb(aml_out)) {
aml_audio_select_spdif_to_hdmi(AML_SPDIF_B_TO_HDMITX);
aml_out->restore_hdmitx_selection = true;
}
}
aml_out->is_sink_format_prepared = true;
}
/* here to check if the audio HDMI ARC format updated. */
if (is_arc_hdmi_updated(adev)) {
AM_LOGI ("arc format updated, need reconfig output");
need_reconfig_encoder = true;
set_arc_hdmi_updated(adev, false);
}
/* here to check if the digital output format updated. */
if (adev->hdmi_format_updated) {
AM_LOGI("hdmi format updated, need reconfig output, [PCM(0)/SPDIF(4)/AUTO(5)] %d", adev->hdmi_format);
need_reconfig_encoder = true;
adev->hdmi_format_updated = 0;
}
/* here to check if hdmi reconnected. */
if (is_HDMI_reconnected(adev)) {
AM_LOGI("hdmi connect updated, need reconfig output");
need_reconfig_encoder = true;
set_HDMI_reconnected_flag(adev, false);
}
if (need_reconfig_encoder) {
get_sink_format(adev);
reconfig_encoder(stream);
dtsx_set_drc_state(adev->dtsx_drc_enable);
need_reconfig_encoder = false;
}
if (adev->active_outport_changed) {
dtsx_set_drc_state(adev->dtsx_drc_enable);
adev->active_outport_changed = false;
}
aml_audio_trace_int("out_write_new", bytes);
/**
* deal with the device output changes
* pthread_mutex_lock(&aml_out->lock);
* out_device_change_validate_l(aml_out);
* pthread_mutex_unlock(&aml_out->lock);
*/
clock_gettime(CLOCK_MONOTONIC, &adev->last_write_ts);
adev->stream_write_data = true;
pheader_rewrite:
if (true == aml_out->with_header) {
write_bytes = 0;
if (NULL == aml_out->pheader) {
aml_out->pheader = audio_header_info_init();
}
pheader_cost = audio_header_find_frame(aml_out->pheader, (char *)buffer + bytes_cost, total_bytes - bytes_cost, &cur_pts, &pheader_outsize);
if (pheader_outsize > 0)
{
write_bytes = pheader_outsize;
write_buf = aml_out->pheader->hw_sync_body_buf;
}
if ((0 != aml_out->pheader->clip_front) || (0 != aml_out->pheader->clip_back))
{
if (NULL == aml_out->clip_meta)
{
aml_out->clip_meta = hal_clip_meta_init();
}
//AM_LOGI("b_install_clip_callback:%d ms12_dec_handle:%p", aml_out->b_install_clip_callback, aml_out->ms12_dec_handle);
if (eDolbyMS12Lib == adev->dolby_lib_type && !aml_out->b_install_clip_callback && aml_out->ms12_dec_handle) {
if (0 != aml_ms12_decoder_register_callback(&(adev->ms12), aml_out->ms12_dec_handle, MS12_CODEC_CALLBACK_CLIPMETA, ms12_clipmeta, (void *)stream))
{
AM_LOGE("clip callback register fail!");
} else {
AM_LOGI("clip callback register success!");
aml_out->b_install_clip_callback = true;
}
}
AM_LOGI("inbytes:%zu, outbytes:%zu, offset:%"PRIu64", clip_front:%"PRIu64", clip_back:%"PRIu64"", bytes, write_bytes,
aml_out->clip_offset, aml_out->pheader->clip_front, aml_out->pheader->clip_back);
aml_out->clip_offset = aml_out->payload_offset;
hal_set_clip_info(aml_out->clip_meta, aml_out->clip_offset, aml_out->pheader->clip_front, aml_out->pheader->clip_back);
}
aml_out->payload_offset += write_bytes;
if (aml_out->eos != aml_out->pheader->eos) {
if (aml_out->pheader->eos) {
/* in nonms12 render case, there would pop noise when playback ending, as there's no fade out.
* and we use AED mute to avoid noise.
*/
if (adev->dolby_lib_type != eDolbyMS12Lib) {
set_aed_master_volume_mute(&adev->alsa_mixer, true);
} else {
//mute
ease_setting_t vol_setting;
vol_setting.duration = 32;
vol_setting.start_volume = 0;
vol_setting.target_volume = 0;
vol_setting.ease_shape = EaseLinear;
aml_audio_ease_config(aml_out->vol_process_handle, &vol_setting);
}
} else {
if (adev->dolby_lib_type != eDolbyMS12Lib) {
set_aed_master_volume_mute(&adev->alsa_mixer, false);
} else {
//unmute
ease_setting_t vol_setting;
vol_setting.duration = 32;
vol_setting.start_volume = aml_out->volume_l;
vol_setting.target_volume = aml_out->volume_l;
vol_setting.ease_shape = EaseLinear;
aml_audio_ease_config(aml_out->vol_process_handle, &vol_setting);
}
}
aml_out->eos = aml_out->pheader->eos;
}
if (adev->debug_flag > 0) {
ALOGI("[%s:%d], inputsize:%zu, pheader_cost:%zu, cur_pts:0x%"PRIx64" (%"PRIu64" ms), outsize:%d", __func__, __LINE__, bytes, pheader_cost, cur_pts, cur_pts/90, pheader_outsize);
}
if ((cur_pts > 0xffffffff) && (cur_pts != HWSYNC_PTS_NA) && (cur_pts != HWSYNC_PTS_EOS)) {
ALOGE("APTS exeed the max 32bit value");
}
}
if ((NULL != aml_out->avsync_ctx) && (AVSYNC_TYPE_MEDIASYNC == aml_out->avsync_type)) {
if (NULL == aml_out->avsync_ctx->mediasync_ctx) {
AM_LOGE("mediasync_ctx is null error, cleanup avsync_type!");
aml_out->avsync_type = AVSYNC_TYPE_NULL;
} else if (true != aml_out->with_header) { //DTV
pthread_mutex_lock(&(aml_out->avsync_ctx->lock));
cur_pts = aml_out->avsync_ctx->mediasync_ctx->in_apts;
pthread_mutex_unlock(&(aml_out->avsync_ctx->lock));
}
}
if (aml_out->flags & AUDIO_OUTPUT_FLAG_HBBTV && aml_out->avsync_type == AVSYNC_TYPE_MEDIASYNC) {
pthread_mutex_lock(&(aml_out->avsync_ctx->lock));
if (aml_out->avsync_ctx->mediasync_ctx->handle) {
struct mediasync_audio_queue_info audio_queue_info;
audio_queue_info.apts = ((int64_t)cur_pts / 90 + adev->hbbtvsync_info.va_offset + adev->hbbtvsync_info.vpts_mt_offset) * 90;
audio_queue_info.duration = 0;
audio_queue_info.size = write_bytes;
audio_queue_info.isneedupdate = false;
audio_queue_info.isworkingchannel = true;
audio_queue_info.tunit = MEDIASYNC_UNIT_PTS;
mediasync_wrap_queueAudioFrame(aml_out->avsync_ctx->mediasync_ctx->handle, &audio_queue_info);
AM_LOGI_IF(adev->debug_flag, "aout %p, handle %p. cur_pts:0x%"PRIx64", adjust_pts 0x%"PRIx64", va_offset %dms, vpts_mt_offset %dms",
aml_out, aml_out->avsync_ctx->mediasync_ctx->handle, cur_pts, audio_queue_info.apts, adev->hbbtvsync_info.va_offset, adev->hbbtvsync_info.vpts_mt_offset);
cur_pts = audio_queue_info.apts;
}
pthread_mutex_unlock(&(aml_out->avsync_ctx->lock));
}
if (aml_out->hal_input_is_float) {
if (out_write_pcm_float_to_s32(aml_out, &write_buf, &write_bytes) < 0) {
AM_LOGE("out_write_pcm_float_to_s32 error!");
return bytes;
}
}
if (aml_out->write && write_bytes > 0) {
abuffer.buffer = write_buf;
abuffer.size = write_bytes;
abuffer.pts = cur_pts;
abuffer.original_format = aml_out->hal_format;
abuffer.original_sample_rate = aml_out->hal_rate;
abuffer.format = aml_out->hal_format;
abuffer.sample_rate = aml_out->hal_rate;
abuffer.channel = aml_out->hal_ch;
abuffer.channel_mask = aml_out->hal_channel_mask;
if (abuffer.pts != HWSYNC_PTS_NA) {
abuffer.b_pts_valid = true;
}
return_bytes = aml_out->write(stream, &abuffer);
}
/*if the data consume is not complete, it will be send again by audio flinger,
this old data will cause av sync problem after seek.
*/
if (aml_out->with_header && return_bytes > 0) {
/*
if the data is not consumed totally,
we need re-send data again
*/
bytes_cost += pheader_cost;
if (bytes_cost > 0 && total_bytes > bytes_cost) {
/* We need to wait for Google to fix the issue:
* Issue: After pause, there will be residual sound in AF, which will cause NTS fail.
* Now we need to judge whether the current format is DTS */
if (is_dts_format(aml_out->hal_internal_format))
return bytes_cost;
else
goto pheader_rewrite;
}
return_bytes = bytes_cost;
}
aml_audio_trace_int("out_write_new", 0);
if (return_bytes > 0) {
aml_out->total_write_size += return_bytes;
if (aml_out->is_normal_pcm) {
size_t frame_size = audio_stream_out_frame_size(stream);
if (frame_size != 0) {
if (aml_out->stream_type == STREAM_DEEP_INPUT) {
adev->deep_buf_audio_frame_written = aml_out->input_bytes_size / frame_size;
} else {
adev->sys_audio_frame_written = aml_out->input_bytes_size / frame_size;
}
}
}
}
update_audio_format(adev, aml_out);
AM_LOGI_IF(adev->debug_flag, "-<OUT>out_stream(%p) position(%zu) ret %zd, %"PRIu64"", stream, bytes, return_bytes, aml_out->total_write_size);
return return_bytes;
}
int adev_open_output_stream_new(struct audio_hw_device *dev,
audio_io_handle_t handle __unused,
audio_devices_t devices,
audio_output_flags_t flags,
struct audio_config *config,
struct audio_stream_out **stream_out,
const char *address)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_stream_out *aml_out = NULL;
int ret = 0;
AM_LOGD("enter!!");
ret = adev_open_output_stream(dev,
0,
devices,
flags,
config,
stream_out,
address);
if (ret < 0) {
ALOGE("%s(), open stream failed", __func__);
return ret;
}
aml_out = (struct aml_stream_out *)(*stream_out);
memcpy(&aml_out->audioCfg, config, sizeof(struct audio_config));
/* init ease for stream */
if (aml_audio_ease_init(&aml_out->audio_stream_ease) < 0) {
ALOGE("%s aml_audio_ease_init faild\n", __func__);
ret = -EINVAL;
goto AUDIO_EASE_INIT_FAIL;
}
if (is_dts_format(aml_out->hal_internal_format) || is_mpegh_format(aml_out->hal_internal_format)) {
adev->native_postprocess.effect_ctrl.is_dts = true;
if (!adev->useSubMix && adev->dolby_lib_type_last == eDolbyMS12Lib) {
pthread_mutex_lock(&adev->audio_mixer_lock);
/*coverity[lock_evasion]: There will be no competition here*/
adev->useSubMix = true;
ret = initHalSubMixing(MIXER_LPCM, adev, is_TV(adev));//init submix
if (ret < 0)
AM_LOGI("initHalSubMixing fail");
pthread_mutex_unlock(&adev->audio_mixer_lock);
adev->restart_device_flag = false;
}
}
/* init vol process for stream */
if (aml_audio_ease_init(&aml_out->vol_process_handle) < 0) {
AM_LOGE("aml_audio_ease_init vol_process_handle failed\n");
ret = -EINVAL;
goto AUDIO_EASE_INIT_FAIL;
} else {
AM_LOGI("out:%p vol_process_handle:%p init success", aml_out, aml_out->vol_process_handle);
aml_out->vol_process_handle->data_format.format = AUDIO_FORMAT_PCM_FLOAT;
if (aml_out->stream_type != STREAM_AD_INPUT) {
/*In order to avoid the invalid adjustment of mastervol when there is no stream */
AM_LOGI("stream:%p set vol to %f", aml_out, aml_out->volume_l_org);
out_set_volume_l((struct audio_stream_out *)aml_out, aml_out->volume_l_org, aml_out->volume_r_org);
}
}
/*when a new stream is opened here, we can add it to active stream*/
ret = add_active_output_stream(adev, aml_out);
if (ret < 0) {
AM_LOGE("add_active_output_stream failed");
return ret;
}
AM_LOGD("exit, out %p, stream ID:%d, io handle:%d, stream_type:%s card:%d alsa devices:%d",
aml_out, aml_out->unique_ID, handle, streamtype2Str(aml_out->stream_type), aml_out->card, aml_out->device);
return 0;
AUDIO_EASE_INIT_FAIL:
adev_close_output_stream(dev, *stream_out);
return ret;
}
void adev_close_output_stream_new(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
if (dev == NULL || stream == NULL) {
ALOGE("%s: Input parameter error!!!", __func__);
return;
}
AM_LOGI("enter, stream:%p, stream ID:%d, stream_type = %s, format = %x, ch = %d",aml_out,
aml_out->unique_ID, streamtype2Str(aml_out->stream_type), aml_out->hal_internal_format, aml_out->hal_ch);
/* when a stream is closed here, we remove it from active stream */
remove_active_output_stream(adev, aml_out);
/* free vol process resource */
aml_audio_ease_close(aml_out->vol_process_handle);
aml_out->vol_process_handle = NULL;
/* free stream ease resource */
aml_audio_ease_close(aml_out->audio_stream_ease);
aml_out->audio_stream_ease = NULL;
/*Avoid accidentally setting variate when multiple streams
*An unexpected setting of variable values will result in dts foramt without dtsvx sound effects*/
pthread_mutex_lock(&adev->active_outputs_lock);
if (adev->native_postprocess.effect_ctrl.is_dts) {
if (!dts_stream_active(adev)) {
adev->native_postprocess.effect_ctrl.is_dts = false;
}
AM_LOGI("native_postprocess.effect_ctrl.is_dts=%d",adev->native_postprocess.effect_ctrl.is_dts);
}
pthread_mutex_unlock(&adev->active_outputs_lock);
adev_close_output_stream(dev, stream);
/* Confirm whether to disable mch pcm output */
pthread_mutex_lock(&adev->audio_mixer_lock);
if (adev->useSubMix && eDolbyDcvLib == adev->dolby_lib_type) {
if (aml_need_mch_pcm_output(adev)) {
mixer_enable_multich_output(adev->audio_mixer, true);
} else {
/* need to close multi-pcm alsa handle, then npcm can use it. */
mixer_enable_multich_output(adev->audio_mixer, false);
}
}
pthread_mutex_unlock(&adev->audio_mixer_lock);
if (eDolbyMS12Lib == adev->dolby_lib_type) {
/*only enable mc output when it supports multi channel*/
if (aml_need_mch_pcm_output(adev)) {
set_ms12_mch_enable(&(adev->ms12), true);
} else {
set_ms12_mch_enable(&(adev->ms12), false);
}
}
get_sink_format(adev);
AM_LOGI("exit!!");
}
static enum OUT_PORT get_output_dev_for_sinks(enum OUT_PORT *sinks, int num_sinks, enum OUT_PORT sink) {
for (int i=0; i<num_sinks; i++) {
if (sinks[i] == sink) {
return sink;
}
}
return -1;
}
static enum OUT_PORT get_output_dev_for_strategy(struct aml_audio_device *adev, enum OUT_PORT *sinks, int num_sinks)
{
uint32_t sink = -1;
if (sink == -1) {
sink = get_output_dev_for_sinks(sinks, num_sinks, OUTPORT_A2DP);
}
if (sink == -1 && is_arc_connected(adev)) {
sink = get_output_dev_for_sinks(sinks, num_sinks, OUTPORT_HDMI_ARC);
}
if (sink == -1) {
sink = get_output_dev_for_sinks(sinks, num_sinks, OUTPORT_HDMI);
}
if (sink == -1) {
sink = get_output_dev_for_sinks(sinks, num_sinks, OUTPORT_SPEAKER);
}
if (sink == -1) {
sink = OUTPORT_SPEAKER;
}
return sink;
}
static int adev_create_audio_patch(struct audio_hw_device *dev,
unsigned int num_sources,
const struct audio_port_config *sources,
unsigned int num_sinks,
const struct audio_port_config *sinks,
audio_patch_handle_t *handle)
{
struct aml_audio_device *aml_dev = (struct aml_audio_device *)dev;
const struct audio_port_config *src_config = NULL;
const struct audio_port_config *sink_config = NULL;
enum OUT_PORT outport = OUTPORT_SPEAKER;
unsigned int i = 0;
int ret = -1;
if (!sources || !sinks || !handle) {
ALOGE("[%s:%d] null pointer! sources:%p, sinks:%p, handle:%p", __func__, __LINE__, sources, sinks, handle);
return -EINVAL;
}
src_config = sources;
sink_config = sinks;
if ((num_sources != 1) || (num_sinks > 3) || (src_config->type != AUDIO_PORT_TYPE_DEVICE)) {
ALOGE("[%s:%d] unsupport num sources:%d or sinks:%d", __func__, __LINE__, num_sources, num_sinks);
return -EINVAL;
}
AM_LOGI("num_sources:%d, num_sinks:%d, %s(0x%x)->%s(0x%x), AF:%p"
, num_sources, num_sinks
, (src_config->type == AUDIO_PORT_TYPE_MIX) ? "mix" : "device"
, (src_config->type == AUDIO_PORT_TYPE_MIX) ? src_config->ext.mix.handle : src_config->ext.device.type
, (sink_config->type == AUDIO_PORT_TYPE_MIX) ? "mix" : "device"
, (sink_config->type == AUDIO_PORT_TYPE_MIX) ? sink_config->ext.mix.handle : sink_config->ext.device.type
, handle);
android_dev_convert_to_hal_dev(sink_config->ext.device.type, (int *)&outport);
if (num_sinks > 1) {
enum OUT_PORT sink_devs[3] = {OUTPORT_SPEAKER, OUTPORT_SPEAKER, OUTPORT_SPEAKER};
for (int i=0; i<num_sinks; i++) {
android_dev_convert_to_hal_dev(sink_config[i].ext.device.type, (int *)&sink_devs[i]);
if (aml_dev->debug_flag) {
AM_LOGD("sink[%d]:%s", i, outputPort2Str(sink_devs[i]));
}
}
outport = get_output_dev_for_strategy(aml_dev, sink_devs, num_sinks);
} else {
AM_LOGI("one sink, sink:%s", outputPort2Str(outport));
}
/*ignore outport SPEAKER, because LINUX always sets SPEAKER when ARC or HDMI connected*/
if (outport != OUTPORT_SPEAKER || aml_dev->active_outport == OUTPORT_MAX) {
if (outport != OUTPORT_SPDIF) {
aml_audio_output_routing(dev, outport, false);
}
aml_dev->out_device = 0;
for (i = 0; i < num_sinks; i++) {
aml_dev->out_device |= sink_config[i].ext.device.type;
}
}
if (src_config->type == AUDIO_PORT_TYPE_DEVICE) {
ret = patch_mgr_create_patch(aml_dev, num_sources, sources, num_sinks, sinks, handle);
if (ret != 0) {
AM_LOGE("device->device patch: unsupport input dev:%#x.", src_config->ext.device.type);
ret = -EINVAL;
}
}
AM_LOGI("patch handle(%d)", *handle);
return ret;
}
/* Release an audio patch */
static int adev_release_audio_patch(struct audio_hw_device *dev,
audio_patch_handle_t handle)
{
int ret = 0;
AM_LOGI("++ handle(%d)", handle);
if (dev) {
ret = patch_mgr_release_patch((struct aml_audio_device *)dev, handle);
}
AM_LOGI("-- after releasing patch, patch sets will be:");
return ret;
}
static char *adev_dump(const audio_hw_device_t *device, int fd)
{
struct aml_audio_device* aml_dev = (struct aml_audio_device*)device;
const int kNumRetries = 5;
const int kSleepTimeMS = 100;
int retry = kNumRetries;
int size;
char *string = NULL;
int ret = 0;
int buffer_frames = 0;
#ifdef BUILD_LINUX
fd = open("/tmp/haldump", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
if (fd < 0) {
ALOGE("Cannot access /tmp for dump");
return NULL;
}
#endif
aml_dev->debug_flag = aml_audio_get_debug_flag();
dprintf(fd, "AUDIO_HAL_GIT_VERSION %s\n",libVersion_audio_hal);
dprintf(fd, "\n-------------[AML_HAL] primary audio hal[dev:%p]------------------\n", aml_dev);
while (retry > 0 && pthread_mutex_trylock(&aml_dev->lock) != 0) {
usleep(kSleepTimeMS * 1000);
retry--;
}
if (retry > 0) {
aml_dev_dump_latency(aml_dev, fd);
pthread_mutex_unlock(&aml_dev->lock);
} else {
// Couldn't lock
dprintf(fd, "[AML_HAL] Could not obtain aml_dev lock.\n");
}
adev_audio_patches_dump(aml_dev, fd);
dprintf(fd, "\n-------------[AML_HAL] extra info------------------\n");
dprintf(fd, "[AML_HAL] hdmi_format : %10d | active_outport : %s | active_inport: %s\n",
aml_dev->hdmi_format, outputPort2Str(aml_dev->active_outport), inputPort2Str(get_active_inport(aml_dev)));
dprintf(fd, "[AML_HAL] dvb_standards : %d\n", aml_dev->dvb_standards);
dprintf(fd, "[AML_HAL] injection : %d\n", aml_dev->injection_enable);
#ifndef NO_AUDIO_CAP
pthread_mutex_lock(&aml_dev->cap_handle_lock);
dprintf(fd, "[AML_HAL] capture enable : %d\n", aml_dev->capture_handle ? 1 :0);
if (aml_dev->capture_handle) {
aml_audio_capture_out_dump(aml_dev->capture_handle, fd);
}
pthread_mutex_unlock(&aml_dev->cap_handle_lock);
#endif
dprintf(fd, "[AML_HAL] last_stream_write_data : %10d | stream_write_data : %d\n",
aml_dev->last_stream_write_data, aml_dev->stream_write_data);
dprintf(fd, "[AML_HAL] continuous_standby : %10d\n",
audio_continuous_standby_check(aml_dev->ms12.continuous_standby_handle));
audio_continuous_standby_dump(aml_dev->ms12.continuous_standby_handle, fd);
dolby_ms12_get_app_buffer_frame(&buffer_frames);
dprintf(fd, "[AML_HAL] audio_process_bitwidth: %2d bit\n", aml_dev->audio_process_bitwidth);
dprintf(fd, "[AML_HAL] A2DP gain : %10f \n",
aml_dev->sink_gain[OUTPORT_A2DP]);
dprintf(fd, "[AML_HAL] SPEAKER gain : %10f | HDMI gain: %f | USB SPK gain: %f | BT gain: %f\n",
aml_dev->sink_gain[OUTPORT_SPEAKER], aml_dev->sink_gain[OUTPORT_HDMI], aml_dev->sink_gain[OUTPORT_USB_HEADSET], aml_dev->sink_gain[OUTPORT_BT_SCO_HEADSET]);
dprintf(fd, "[AML_HAL] dtv_src gain : %10f | atv gain: %f | hdmi gain: %f | hdmi1 gain: %f | linein: %f | media gain: %f\n",
get_inport_gain(aml_dev, INPORT_TUNER), get_inport_gain(aml_dev, INPORT_ATV), get_inport_gain(aml_dev, INPORT_HDMIIN), get_inport_gain(aml_dev, INPORT_HDMIIN1), get_inport_gain(aml_dev, INPORT_LINEIN), get_inport_gain(aml_dev, INPORT_MEDIA));
dprintf(fd, "[AML_HAL] ms12 main volume : %10f\n", aml_dev->ms12.main_volume);
dprintf(fd, "[AML_HAL] decoder DRC control : %10d\n", aml_dev->decoder_drc_control);
dprintf(fd, "[AML_HAL] DAP DRC control : %10d\n", aml_dev->dap_drc_control);
dprintf(fd, "[AML_HAL] downmix type : %10d\n", aml_dev->downmix_type);
dprintf(fd, "[AML_HAL] UI disable dap : %10d\n", aml_dev->ms12.UI_disable_dap);
dprintf(fd, "[AML_HAL] internal disable dap : %10d\n", aml_dev->ms12.internal_disable_dap);
dprintf(fd, "[AML_HAL] pic mode : %10d\n", get_dev_pic_mode());
dprintf(fd, "[AML_HAL] dap_enable : %10d\n", aml_dev->ms12.dap_enable);
dprintf(fd, "[AML_HAL] ms12.main_input_fmt : %10d\n", aml_dev->ms12.main_input_fmt);
dprintf(fd, "[AML_HAL] dolby_drc_mode : %10d(0:line,1:RF)\n", aml_dev->ms12.dolby_drc_mode);
dprintf(fd, "[AML_HAL]stereo_out_drc_cur_volume : %10f\n", aml_dev->ms12.stereo_out_drc_cur_volume);
dprintf(fd, "[AML_HAL]stereo_out_drc_target_volume: %10f\n", aml_dev->ms12.stereo_out_drc_target_volume);
dprintf(fd, "[AML_HAL]dap_out_drc_cur_volume : %10f\n", aml_dev->ms12.dap_out_drc_cur_volume);
dprintf(fd, "[AML_HAL]dap_out_drc_target_volume : %10f\n", aml_dev->ms12.dap_out_drc_target_volume);
dprintf(fd, "[AML_HAL] ac4_de : %10d\n", aml_dev->ms12.ac4_de);
dprintf(fd, "[AML_HAL] dap_de : %10d,%d\n", aml_dev->ms12.dap_de_enable, aml_dev->ms12.dap_de_amount);
dprintf(fd, "[AML_HAL] dap_leveler : %10d,%d\n", aml_dev->ms12.dap_leveler[0], aml_dev->ms12.dap_leveler[1]);
dprintf(fd, "[AML_HAL] master volume: %10f, mute: %d\n", aml_dev->master_volume, aml_dev->master_mute);
dprintf(fd, "[AML_HAL] ad vol: %10d, mix level: %10d\n", aml_dev->advol_level, aml_dev->mixing_level);
dprintf(fd, "[AML_HAL] sys stream level: %d bytes, app stream level: %d ms\n", dolby_ms12_get_system_buffer_avail(NULL), buffer_frames / 48);
aml_decoder_info_dump(aml_dev, fd);
if (aml_dev->dolby_lib_type == eDolbyMS12Lib) {
dolby_ms12_info_dump(fd);
} else {
mixer_dump(aml_dev, fd);
}
aml_adev_stream_out_dump(aml_dev, fd);
dprintf(fd, "\nAML stream outs:\n");
dprintf(fd, "[AML_HAL] sink format: %#x\n", aml_dev->sink_format);
if (OUTPORT_HDMI_ARC == aml_dev->active_outport) {
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(aml_dev);
bool dd_is_support = hdmi_desc->dd_fmt.is_support;
bool ddp_is_support = hdmi_desc->ddp_fmt.is_support;
bool mat_is_support = hdmi_desc->mat_fmt.is_support;
dprintf(fd, "[AML_HAL] -dd: %d, ddp: %d, mat: %d\n",
dd_is_support, ddp_is_support, mat_is_support);
}
#ifdef AML_MALLOC_DEBUG
aml_audio_debug_malloc_showinfo(MEMINFO_SHOW_PRINT);
#endif
audio_hal_info_dump(aml_dev, fd);
aml_audio_spdif_info_dump(aml_dev, fd);
audio_input_dump(fd);
aml_audio_hwmixer_out_dump(aml_dev, fd);
aml_alsa_device_status_dump(aml_dev, fd);
#ifdef SUPPORT_EXTERNAL_OUTPUT
aml_external_device_status_dump(aml_dev, fd);
#endif
aml_dump_policy_mappings(aml_dev, fd);
aml_alsa_mixer_status_dump(&aml_dev->alsa_mixer, fd);
karaoke_manager_dump(aml_dev->kara_manager, fd);
dprintf(fd, "\n-------------[AML_HAL] MS12 config info------------------\n");
switch (aml_dev->dolby_ms12_audio_config) {
case MS12_CONFIG_Y:
dprintf(fd, "[Ms12 Info] Dolby MS12 Config: %s\n", "Y");
break;
case MS12_CONFIG_X:
dprintf(fd, "[Ms12 Info] Dolby MS12 Config: %s\n", "X");
dprintf(fd, "[Ms12 Info] MS12 tuning config path: %s\n", DOLBY_TUNING_DAT);
break;
case MS12_CONFIG_Z:
dprintf(fd, "[Ms12 Info] Dolby MS12 Config: %s\n", "Z");
dprintf(fd, "[Ms12 Info] MS12 tuning config path: %s\n", DOLBY_TUNING_DAT);
break;
}
//#ifdef ENABLE_BT_A2DP
#ifndef BUILD_LINUX
a2dp_hal_dump(aml_dev, fd);
#endif
#ifdef BUILD_LINUX
size = lseek(fd, 0, SEEK_CUR);
if (size >= 0) {
string = aml_audio_calloc(size + 1, 1);
if (string) {
lseek(fd, 0, SEEK_SET);
ret = read(fd, string, size);
if (ret < 0) {
ALOGE("%s(), fail to read", __func__);
}
}
}
close(fd);
unlink("/tmp/haldump");
#endif
dprintf(fd, "\n-------------[AML_HAL] primary audio hal End---------------------\n");
return string;
}
pthread_mutex_t adev_mutex = PTHREAD_MUTEX_INITIALIZER;
static void * g_adev = NULL;
void *adev_get_handle(void) {
/*coverity[missing_lock]: g_adev does not need to be lock*/
return (void *)g_adev;
}
int adev_ms12_prepare(struct audio_hw_device *dev) {
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
int ret = -1;
audio_format_t aformat = AUDIO_FORMAT_E_AC3;
struct dolby_ms12_desc *ms12 = &(adev->ms12);
AM_LOGD("enter");
if (ms12->dap_only_enable) {
aml_dap_close(ms12);
}
get_sink_format(adev);
adev->ms12.is_continuous_paused = false;
ret = get_the_dolby_ms12_prepared(ms12, aformat, AUDIO_CHANNEL_OUT_STEREO, 48000);
AM_LOGD("exit");
return 0;
}
void adev_ms12_cleanup(struct audio_hw_device *dev) {
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
AM_LOGD("enter");
get_dolby_ms12_cleanup(&adev->ms12, true);
AM_LOGD("exit");
return;
}
static int adev_headphone_limiter_init(struct aml_audio_device *adev) {
int ret = -EXIT_FAILURE;
int headphone_limiter_enable = aml_get_json_int_value("Headphone_Limiter_Enable", 0);
if (!headphone_limiter_enable) {
ALOGI("Not support headpone limiter module");
return ret;
}
int format = adev->audio_process_bitwidth == 32 ? AUDIO_FORMAT_PCM_32_BIT : AUDIO_FORMAT_PCM_16_BIT;
int headphone_limiter_release_time = aml_get_json_int_value("Headphone_Limiter_ReleaseTime", 100);
adev->headphone_limiter = aml_audio_limiter_new(format);
if (adev->headphone_limiter) {
char *headphone_drc_peak_str = aml_get_json_string_value("Headphone_Limiter_Threshold");
if (!headphone_drc_peak_str) {
headphone_drc_peak_str = "-12.0f";
}
char *endptr;
float headphone_drc_peak = strtof(headphone_drc_peak_str, &endptr);
if (endptr == headphone_drc_peak_str) {
AM_LOGE("Invalid Headphone_DRC_Threshold value, use default -12.0f");
headphone_drc_peak = -12.0f;
}
float headphone_drc_peak_gain = DbToAmpl(headphone_drc_peak);
ret = aml_audio_limiter_set(adev->headphone_limiter, headphone_drc_peak_gain, 1, headphone_limiter_release_time, AML_LIMITER_PROC_FRAME, AML_LIMITER_DRC);
if (ret != 0) {
aml_audio_limiter_free(adev->headphone_limiter);
adev->headphone_limiter = NULL;
AM_LOGE("aml_audio_headphone_drc_set fail");
} else {
aml_audio_limiter_enable(adev->headphone_limiter, true);
if (adev->eq_data.p_gain.headphone > DbToAmpl(-headphone_drc_peak)) {
adev->eq_data.p_gain.headphone = DbToAmpl(-headphone_drc_peak);
AM_LOGE("HP post gain < Headphone_Limiter_Threshold with HP limiter, need reset to %fdb",-headphone_drc_peak);
}
AM_LOGI("Enable heaphone drc:%p, limiter peak is %f, release time is %d", adev->headphone_limiter, headphone_drc_peak_gain, headphone_limiter_release_time);
}
}
return ret;
}
static int adev_headphone_limiter_deint(struct aml_audio_device *adev) {
if (adev->headphone_limiter) {
aml_audio_limiter_free(adev->headphone_limiter);
adev->headphone_limiter = NULL;
}
return 0;
}
static int adev_close(hw_device_t *device)
{
struct aml_audio_device *adev = (struct aml_audio_device *)device;
pthread_mutex_lock(&adev_mutex);
ALOGD("%s: count:%d enter", __func__, adev->count);
adev->count--;
if (adev->count > 0) {
pthread_mutex_unlock(&adev_mutex);
return 0;
}
pthread_mutex_destroy(&(adev->llp_lock));
/* destroy thread for communication between Audio Hal and MS12 */
if ((eDolbyMS12Lib == adev->dolby_lib_type)) {
ms12_mesg_thread_destroy(&adev->ms12);
ALOGD("%s, ms12_mesg_thread_destroy finished!\n", __func__);
}
if (eDolbyMS12Lib == adev->dolby_lib_type) {
adev_ms12_cleanup((struct audio_hw_device *)device);
pthread_mutex_destroy(&adev->ms12.bypass_ms12_lock);
}
#ifdef USE_MEDIAINFO
minfo_destroy(adev->minfo_h);
adev->minfo_h = NULL;
#endif
if (adev->effect_buf) {
aml_audio_free(adev->effect_buf);
}
if (adev->spdif_output_buf) {
aml_audio_free(adev->spdif_output_buf);
}
if (adev->hp_output_buf) {
aml_audio_free(adev->hp_output_buf);
}
if (adev->tmp_process_buf) {
aml_audio_free(adev->tmp_process_buf);
}
if (adev->tmp_buffer_8ch) {
aml_audio_free(adev->tmp_buffer_8ch);
adev->tmp_buffer_8ch = NULL;
}
if (adev->low_latency_buf) {
aml_audio_free(adev->low_latency_buf);
adev->low_latency_buf = NULL;
}
ring_buffer_release(&(adev->spk_tuning_rbuf));
ring_buffer_release(&(adev->spdif_rbuf));
#ifdef USE_EQ_DRC
eq_drc_release(&adev->eq_data);
#endif
if (adev->audio_mixer) {
deleteHalSubMixing(adev);
}
pthread_mutex_destroy(&adev->audio_mixer_lock);
if (adev->hw_mixer.start_buf) {
aml_hw_mixer_deinit(&adev->hw_mixer);
}
/** rlease some global shared resources **/
destroy_hdmi_capability_manager(adev);
destroy_hw_resource_mgr(adev);
destroy_patch_manager(adev);
pthread_mutex_destroy(&adev->dtv_patch_lock);
pthread_mutex_destroy(&adev->patch_lock);
pthread_mutex_destroy(&adev->active_outputs_lock);
pthread_mutex_destroy(&adev->low_latency_param_lock);
for (int i = I2S_DEVICE; i < ALSA_DEVICE_CNT; i++) {
pthread_mutex_destroy(&adev->alsa_lock[i]);
}
for (int i = SPDIF_HANDLE_1; i < SPDIF_HANDLE_CNT; i++) {
pthread_mutex_destroy(&adev->spdif_handle_lock[i]);
}
for (int i = 0; i < AUDIO_INPUT_TYPE_CNT; i++) {
pthread_mutex_destroy(&adev->input_handle_lock[i]);
}
pthread_mutex_destroy(&adev->hwmixer_mgr_lock);
aml_audio_delay_deinit();
release_alsa_device();
#ifdef NO_SERVER
adev->audio_debug_thread_exit = true;
if (adev->audio_debug_thread) {
pthread_join(adev->audio_debug_thread, NULL);
}
#endif
if (adev->mpegh_ui_persistencemem) {
aml_audio_free(adev->mpegh_ui_persistencemem);
adev->mpegh_ui_persistencememsize = 0;
adev->mpegh_ui_persistencemem = NULL;
}
if (adev->mpegh_base64_encode_mem) {
aml_audio_free(adev->mpegh_base64_encode_mem);
adev->mpegh_base64_encode_mem = NULL;
}
if (adev->mpegh_audioscenexml) {
aml_audio_free(adev->mpegh_audioscenexml);
adev->mpegh_audioscenexmlsize = 0;
adev->mpegh_audioscenexml = NULL;
}
if (adev->aml_command_worker) {
aml_audio_cmd_worker_thread_destroy(adev);
adev->aml_command_worker = NULL;
}
karaoke_manager_release(adev->kara_manager);
if (adev->enable_deep_unit) {
deep_buffer_unit_deinit(adev->deep_unit);
}
mixer_out_unit_deinit(adev->mixer_unit);
#ifdef SUPPORT_EXTERNAL_OUTPUT
pthread_mutex_destroy(&adev->external_handle_lock);
aml_external_device_port_deinit(adev);
#endif
g_adev = NULL;
audio_effect_unload_interface(&(adev->hw_device));
if (is_STB(adev)) {
aml_audio_uevent_close();
}
close_mixer_handle(&adev->alsa_mixer);
adev_headphone_limiter_deint(adev);
aml_audio_free(device);
pthread_mutex_unlock(&adev_mutex);
aml_audio_debug_malloc_close();
aml_audio_debug_close();
ALOGD("%s: exit", __func__);
return 0;
}
static int adev_set_audio_port_config (struct audio_hw_device *dev, const struct audio_port_config *config)
{
struct aml_audio_device *aml_dev = (struct aml_audio_device *) dev;
enum OUT_PORT outport = OUTPORT_SPEAKER;
enum IN_PORT inport = INPORT_HDMIIN;
float volume = 0.0;
if (config == NULL) {
ALOGE("[%s:%d] audio_port_config is null", __func__, __LINE__);
return -EINVAL;
}
if ((config->config_mask & AUDIO_PORT_CONFIG_GAIN) == 0) {
ALOGE("[%s:%d] config_mask:%#x invalid", __func__, __LINE__, config->config_mask);
return -EINVAL;
}
ALOGI("++[%s:%d] audio_port id:%d, role:%d, type:%d", __func__, __LINE__, config->id, config->role, config->type);
if (config->type == AUDIO_PORT_TYPE_DEVICE) {
if (config->role == AUDIO_PORT_ROLE_SINK) {
android_dev_convert_to_hal_dev(config->ext.device.type, (int *)&outport);
aml_dev->sink_gain[outport] = DbToAmpl(config->gain.values[0] / 100.0);
if (outport == OUTPORT_HDMI_ARC) {
aml_dev->sink_gain[outport] = 1.0;
}
ALOGI(" - set sink device[%#x](outport:%s): volume_dB[%d], gain[%f]",
config->ext.device.type, outputPort2Str(outport),
config->gain.values[0], aml_dev->sink_gain[outport]);
ALOGI(" - now the sink gains are:");
ALOGI("\t- OUTPORT_SPEAKER->gain[%f]", aml_dev->sink_gain[OUTPORT_SPEAKER]);
ALOGI("\t- OUTPORT_HDMI_ARC->gain[%f]", aml_dev->sink_gain[OUTPORT_HDMI_ARC]);
ALOGI("\t- OUTPORT_HEADPHONE->gain[%f]", aml_dev->sink_gain[OUTPORT_HEADPHONE]);
ALOGI("\t- OUTPORT_HDMI->gain[%f]", aml_dev->sink_gain[OUTPORT_HDMI]);
ALOGI("\t- OUTPORT_USB_HEADSET->gain[%f]", aml_dev->sink_gain[OUTPORT_USB_HEADSET]);
ALOGI("\t- OUTPORT_BT_SOC_HEADSET->gain[%f]", aml_dev->sink_gain[OUTPORT_BT_SCO_HEADSET]);
ALOGI("\t- active outport is: %s", outputPort2Str(aml_dev->active_outport));
// sync volume to hwmixer
if (aml_dev->hwmixer_manager) {
hwmixer_mgr_set_volume_by_port(aml_dev->hwmixer_manager, aml_dev->active_outport);
}
} else if (config->role == AUDIO_PORT_ROLE_SOURCE) {
android_dev_convert_to_hal_dev(config->ext.device.type, (int *)&inport);
volume = DbToAmpl(config->gain.values[0] / 100.0);
if (volume < FLOAT_ZERO) {
volume = 0.0;
}
//aml_dev->src_gain[inport] = volume;
set_inport_gain(aml_dev, inport, volume);
ALOGI(" - set src device[%#x](inport:%s): volume db[%d], gain[%f][%f]",
config->ext.device.type, inputPort2Str(inport),
config->gain.values[0], volume, get_inport_gain(aml_dev, inport));
ALOGI(" - now the source gains are:");
ALOGI(" - INPORT_DTV->gain[%f]", get_inport_gain(aml_dev, INPORT_TUNER));
ALOGI(" - INPORT_ATV->gain[%f]", get_inport_gain(aml_dev, INPORT_ATV));
ALOGI(" - INPORT_HDMI->gain[%f]", get_inport_gain(aml_dev, INPORT_HDMIIN));
ALOGI(" - INPORT_HDMI1->gain[%f]", get_inport_gain(aml_dev, INPORT_HDMIIN1));
ALOGI(" - INPORT_AV->gain[%f]", get_inport_gain(aml_dev, INPORT_LINEIN));
ALOGI(" - INPORT_MEDIA->gain[%f]", get_inport_gain(aml_dev, INPORT_MEDIA));
ALOGI(" - set gain for in_port:%s, active inport is:%s", inputPort2Str(inport), inputPort2Str(get_active_inport(aml_dev)));
} else {
ALOGI("[%s:%d] unsupported role:%d type.", __func__, __LINE__, config->role);
}
}
return 0;
}
static int adev_get_dvb_standards ()
{
enum dtv_standards dtv_type = DTV_STANDARDS_EUDVB;
char *tuner_type = getenv("ro_vendor_platform_digitaltv_standards");
if (tuner_type != NULL) {
if (0 == strcmp(tuner_type, "atsc")) {
dtv_type = DTV_STANDARDS_ATSC;
} else if (0 == strcmp(tuner_type, "dtmb")) {
dtv_type = DTV_STANDARDS_DTMB;
} else if (0 == strcmp(tuner_type, "isdb")) {
dtv_type = DTV_STANDARDS_ISDB;
} else if (0 == strcmp(tuner_type, "sdtvb")) {
dtv_type = DTV_STANDARDS_SDTVB;
} else {
dtv_type = DTV_STANDARDS_EUDVB;
}
}
AM_LOGI("dtv tuner_type: %d", dtv_type);
return dtv_type;
}
static int adev_get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
{
struct aml_audio_device *aml_dev = (struct aml_audio_device *) dev;
enum OUT_PORT outport = OUTPORT_SPEAKER;
enum IN_PORT inport = INPORT_HDMIIN;
if (port == NULL) {
ALOGE("[%s:%d] port is null", __func__, __LINE__);
return -EINVAL;
}
ALOGI("++[%s:%d] audio_port_config id:%d, type:%x", __func__, __LINE__, port->active_config.id, port->ext.device.type);
if (port->active_config.id == AUDIO_PORT_ROLE_SOURCE) {
android_dev_convert_to_hal_dev(port->ext.device.type, (int *)&inport);
port->active_config.gain.values[0] = get_inport_gain(aml_dev, inport) * 100;
ALOGI("[%s:%d] device %x, inport %d, gain %d", __func__, __LINE__,
port->ext.device.type, inport, port->active_config.gain.values[0] );
} else if (port->active_config.id == AUDIO_PORT_ROLE_SINK) {
android_dev_convert_to_hal_dev(port->ext.device.type, (int *)&outport);
port->active_config.gain.values[0] = aml_dev->sink_gain[outport] * 100;
ALOGI("[%s:%d] device %x, outport %d, gain %d", __func__, __LINE__,
port->ext.device.type, outport, port->active_config.gain.values[0]);
}
return 0;
}
/* Disable the treblebass attenuation parameter
*{0,4,4,3,1}
* 0 : states;
* 4 : psize;
* 4 : vsize;
* 3 : trebless comand;
* 1 : trebless comand value;
*/
#define SEND_TREBLEBASS_CMD(e) do { \
uint32_t _cmd[] = {0,4,4,3,1}; \
uint32_t _replySize = sizeof(uint32_t); \
uint32_t _replyData = 0; \
(*(e))->command(e, EFFECT_CMD_SET_PARAM, sizeof(_cmd), \
(effect_param_t *)_cmd, &_replySize, &_replyData); \
} while(0)
static int adev_add_device_effect(struct audio_hw_device *dev,
audio_port_handle_t device, effect_handle_t effect)
{
struct aml_audio_device *aml_dev = (struct aml_audio_device *) dev;
int i;
int status = 0;
pthread_mutex_lock (&aml_dev->lock);
if (aml_dev->native_postprocess.num_postprocessors >= MAX_POSTPROCESSORS) {
status = -ENOSYS;
goto exit;
}
/* Check if the effect already exists in the array */
for (i = 0; i < aml_dev->native_postprocess.num_postprocessors; i++) {
if (aml_dev->native_postprocess.postprocessors[i] == effect) {
status = 0;
goto exit;
}
}
effect_descriptor_t tmpdesc;
(*effect)->get_descriptor(effect, &tmpdesc);
int is_virtualx = (0 == strncmp(tmpdesc.name, "VirtualX", strlen("VirtualX")));
int is_hpeq = (0 == strncmp(tmpdesc.name, "Hpeq", strlen("Hpeq")));
int is_treblebass = (0 == strncmp(tmpdesc.name, "TrebleBass", strlen("TrebleBass")));
/* Determine insertion position */
int insert_pos = aml_dev->native_postprocess.num_postprocessors;
if (is_virtualx) {
aml_dev->native_postprocess.libvx_exist = true;
insert_pos = 0; // VirtualX always goes to first position
} else if (is_hpeq) {
// Check if VirtualX exists in current processors
int has_virtualx = 0;
if (aml_dev->native_postprocess.num_postprocessors > 0) {
effect_descriptor_t first_desc;
(*aml_dev->native_postprocess.postprocessors[0])->get_descriptor(aml_dev->native_postprocess.postprocessors[0], &first_desc);
has_virtualx = (0 == strncmp(first_desc.name, "VirtualX", strlen("VirtualX")));
}
insert_pos = has_virtualx ? 1 : 0; // Second position if VirtualX exists, else first
}
/* Shift subsequent elements and insert new effect */
for (i = aml_dev->native_postprocess.num_postprocessors; i > insert_pos; i--) {
aml_dev->native_postprocess.postprocessors[i] = aml_dev->native_postprocess.postprocessors[i-1];
}
aml_dev->native_postprocess.postprocessors[insert_pos] = effect;
aml_dev->native_postprocess.num_postprocessors++;
if (is_hpeq || is_treblebass) {
/* Find companion effect */
effect_handle_t target_soundeffect_handle = NULL;
const char *target_name = is_hpeq ? "TrebleBass" : "Hpeq";
for (int i = 0; i < aml_dev->native_postprocess.num_postprocessors; i++) {
effect_descriptor_t d;
(*aml_dev->native_postprocess.postprocessors[i])->get_descriptor(aml_dev->native_postprocess.postprocessors[i], &d);
if (!strncmp(d.name, target_name, strlen(target_name))) {
target_soundeffect_handle = aml_dev->native_postprocess.postprocessors[i];
break;
}
}
/* Both HPEQ+TrebleBass ,Use HPEQ attenuation only */
if (target_soundeffect_handle) {
SEND_TREBLEBASS_CMD(is_hpeq ? target_soundeffect_handle : effect);
}
/*Since Hpeq and Treblebass attenuates the signal, full band drc is used for signal compensation*/
aml_mixer_ctrl_set_int(&aml_dev->alsa_mixer, AML_MIXER_ID_AED_FULL_DRC_ENABLE, 1);
AM_LOGI("Enable EQ_DRC compensation for %s", tmpdesc.name);
}
AM_LOGD("add audio effect: %s in audio hal, effect_handle: %p, total num of effects: %d active_outport:%d",
tmpdesc.name, effect, aml_dev->native_postprocess.num_postprocessors, aml_dev->active_outport);
exit:
pthread_mutex_unlock (&aml_dev->lock);
return status;
}
static int adev_remove_device_effect(struct audio_hw_device *dev,
audio_port_handle_t device, effect_handle_t effect)
{
int i;
int status = -EINVAL;
bool found = false;
struct aml_audio_device *aml_dev = (struct aml_audio_device *) dev;
pthread_mutex_lock (&aml_dev->lock);
if (aml_dev->native_postprocess.num_postprocessors <= 0) {
status = -ENOSYS;
goto exit;
}
effect_descriptor_t tmpdesc_in_queue;
effect_descriptor_t tmpdesc_remove;
if (!effect) {
ALOGE("%s:Check parameters correctly",__func__);
status = -ENOSYS;
goto exit;
}
(*effect)->get_descriptor(effect, &tmpdesc_remove);
for (i = 0; i < aml_dev->native_postprocess.num_postprocessors; i++) {
(*aml_dev->native_postprocess.postprocessors[i])->get_descriptor(aml_dev->native_postprocess.postprocessors[i], &tmpdesc_in_queue);
if (0 == strcmp(tmpdesc_in_queue.name, tmpdesc_remove.name)) {
aml_dev->native_postprocess.postprocessors[i] = NULL;
status = 0;
found = true;
aml_dev->native_postprocess.num_postprocessors--;
ALOGI("%s, remove audio effect: %s in audio hal",__FUNCTION__, tmpdesc_in_queue.name);
}
if (found) {
aml_dev->native_postprocess.postprocessors[i] = aml_dev->native_postprocess.postprocessors[i + 1];
if (0 == strncmp(tmpdesc_remove.name, "Hpeq", strlen("Hpeq"))) {
aml_mixer_ctrl_set_int(&aml_dev->alsa_mixer, AML_MIXER_ID_AED_FULL_DRC_ENABLE, 0);
}
}
}
if (!found) {
ALOGI("%s %d, not found %s sound effect", __FUNCTION__, __LINE__, tmpdesc_remove.name);
} else {
ALOGI("%s %d, total num of effects: %d", __FUNCTION__, __LINE__, aml_dev->native_postprocess.num_postprocessors);
}
ALOGD("func:%s effect_handle_t:%p, active_outport:%d", __func__, effect, aml_dev->active_outport);
exit:
pthread_mutex_unlock (&aml_dev->lock);
return status;
}
static int aml_audio_focus(struct aml_audio_device *adev, bool on)
{
if (!adev)
return -1;
pthread_mutex_lock (&adev->lock);
for (int i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
struct audio_stream_out *out = (struct audio_stream_out *)aml_out;
if (aml_out && aml_out->stream_type != STREAM_APP_INPUT) {
AM_LOGI("focus on:%d focus_ratio:%f", on , adev->audio_focus_ratio);
out_set_volume_l(out, aml_out->last_volume_l_org, aml_out->last_volume_l_org);
}
}
pthread_mutex_unlock (&adev->lock);
return 0;
}
#ifdef NO_SERVER
#define AUDIO_DEBUG_FIFO_NAME "/data/audio_debug_fifo"
static void *audio_debug_threadloop(void *data)
{
struct aml_audio_device *adev = (struct aml_audio_device *)data;
int fd = 0;
char read_msg[256] = {0};
AM_LOGI("entry. adev: %p", adev);
if (adev == NULL) {
AM_LOGE("adev pointer invalid!");
goto Error;
}
prctl(PR_SET_NAME, (unsigned long)"audio_debug_thread");
fd = open(AUDIO_DEBUG_FIFO_NAME, O_RDONLY|O_NONBLOCK);
if (fd > 0) {
while (!adev->audio_debug_thread_exit) {
memset(read_msg, 0, sizeof(read_msg));
int ret = read(fd, read_msg, sizeof(read_msg));
if (ret > 0) {
AM_LOGI("msg: %s", read_msg);
size_t read_msg_len = strlen(read_msg);
if (read_msg_len > 0 && '\n' == read_msg[read_msg_len-1]) {
read_msg[read_msg_len-1] = '\0';
}
adev->hw_device.set_parameters(&adev->hw_device, read_msg);
} else {
aml_audio_sleep(2000000);
}
}
close(fd);
}
Error:
AM_LOGI("exit");
return ((void *)0);
}
#endif
static int adev_uevent_callback(int uevent_type) {
struct aml_audio_device *adev = adev_get_handle();
AM_LOGI("uevent type=%d", uevent_type);
switch (uevent_type) {
case UEVENT_TYPE_VMODE_CHANGE:
adev->reset_hdmitx_audio = true;
break;
default:
break;
}
return 0;
}
static int adev_ioctl(struct audio_hw_device *dev, int cmd, void *param, int size) {
AM_LOGI("cmd: %d, size: %d", cmd, size);
if (cmd == AUDIO_IOCTL_CMD_TEST) {
int *arg_int = (int *)param;
for (int i = 0; i * 4 < size; i++) {
AM_LOGI("arg[%d]:%d", i, arg_int[i]);
arg_int[i] ++;
}
goto exit;
}
#ifdef L_PROJECT
else if (cmd >= AUDIO_IOCTL_CMD_KCONTROL_START && cmd <= AUDIO_IOCTL_CMD_KCONTROL_END) {
aml_kctl_func(cmd, param, size);
goto exit;
}
#endif
exit:
return 0;
}
static int adev_open(const hw_module_t* module, const char* name, hw_device_t** device)
{
struct aml_audio_device *adev;
aml_audio_debug_malloc_open();
size_t bytes_per_frame = audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT)
* audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
int buffer_size = PLAYBACK_PERIOD_COUNT * DEFAULT_PLAYBACK_PERIOD_SIZE * bytes_per_frame;
int spk_tuning_buf_size = MAX_SPK_EXTRA_LATENCY_MS
* bytes_per_frame * MM_FULL_POWER_SAMPLING_RATE / 1000;
int spdif_ring_buf_size = SPDIF_RBUF_DATA_STORAGE_MS
* bytes_per_frame * MM_FULL_POWER_SAMPLING_RATE / 1000;
int spdif_tuning_latency = aml_audio_get_spdif_tuning_latency();
int card = CARD_AMLOGIC_BOARD;
int ret = 0, i;
audio_effect_t *audio_effect;
bool earctx_mode = true;
ALOGD("%s: enter, ver:%s", __func__, libVersion_audio_hal);
pthread_mutex_lock(&adev_mutex);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) {
ret = -EINVAL;
pthread_mutex_unlock(&adev_mutex);
goto err;
}
if (g_adev != NULL) {
ALOGI("adev exsits ,reuse");
adev = (struct aml_audio_device *)g_adev;
adev->count++;
*device = &adev->hw_device.common;
ALOGI("*device:%p",*device);
pthread_mutex_unlock(&adev_mutex);
goto err;
}
adev = aml_audio_calloc(1, sizeof(struct aml_audio_device));
if (!adev) {
ret = -ENOMEM;
pthread_mutex_unlock(&adev_mutex);
goto err;
}
g_adev = (void *)adev;
adev->count = 1;
pthread_mutex_unlock(&adev_mutex);
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_new;
adev->hw_device.close_output_stream = adev_close_output_stream_new;
adev->hw_device.open_input_stream = adev_open_input_stream;
adev->hw_device.close_input_stream = adev_close_input_stream;
adev->hw_device.create_audio_patch = adev_create_audio_patch;
adev->hw_device.release_audio_patch = adev_release_audio_patch;
adev->hw_device.set_audio_port_config = adev_set_audio_port_config;
adev->hw_device.add_device_effect = adev_add_device_effect;
adev->hw_device.remove_device_effect = adev_remove_device_effect;
adev->hw_device.get_microphones = adev_get_microphones;
adev->hw_device.get_audio_port = adev_get_audio_port;
adev->hw_device.dump = adev_dump;
adev->hw_device.ioctl = adev_ioctl;
adev->hdmi_format = AUTO;
adev->is_hdmi_arc_interact_done = false;
adev->is_netflix_hide = false;
adev->user_setting_scaletempo = true;
adev->next_unique_ID = 0;
adev->audio_focus_ratio = 1.0;
adev->auto_test_debug_flag = false;
adev->sink_configured_dolby_capability = AUDIO_FORMAT_INVALID;
adev->sink_configured_dts_capability = AUDIO_FORMAT_INVALID;
adev->enable_soundbar_mode = false;
adev->is_early_suspend = false;
adev->dtv_mute = false;
pthread_mutex_init(&adev->llp_lock, NULL);
card = alsa_device_get_card_index();
if ((card < 0) || (card > 7)) {
ALOGE("error to get audio card");
ret = -EINVAL;
goto err_adev;
}
adev->card = card;
adev->has_hwmixer = alsa_device_has_hwmixer();
pthread_mutex_init(&adev->hwmixer_mgr_lock, NULL);
/* Set the default route before the PCM stream is opened */
adev->mode = AUDIO_MODE_NORMAL;
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN;
adev->eq_data.card = adev->card;
ret = aml_audio_output_routing(&adev->hw_device, OUTPORT_SPEAKER, false);
if (ret < 0) {
ALOGE("%s() routing failed", __func__);
ret = -EINVAL;
goto err_adev;
}
adev->effect_buf_size = buffer_size;
adev->effect_buf = aml_audio_malloc(buffer_size);
if (adev->effect_buf == NULL) {
ALOGE("malloc effect buffer failed");
ret = -ENOMEM;
goto err_adev;
}
memset(adev->effect_buf, 0, buffer_size);
adev->effect_ctrl.dap_enable = 0;
adev->effect_ctrl.vx_enable = 0;
adev->effect_ctrl.effect_mode = EFFECT_MODE_OFF;
adev->native_postprocess.effect_ctrl.effect_mode = EFFECT_MODE_OFF;
adev->effect_ctrl.virtualXlib1_state = 0;
adev->spdif_output_buf = aml_audio_malloc(buffer_size * 2);
if (adev->spdif_output_buf == NULL) {
ALOGE("no memory for spdif output buffer");
ret = -ENOMEM;
goto err_effect_buf;
}
memset(adev->spdif_output_buf, 0, buffer_size * 2);
adev->hp_output_buf = aml_audio_malloc(buffer_size);
if (adev->hp_output_buf == NULL) {
ALOGE("no memory for hp output buffer");
ret = -ENOMEM;
goto err_spdif_buf;
}
memset(adev->hp_output_buf, 0, buffer_size);
adev->tmp_process_buf_size = buffer_size;
adev->tmp_process_buf = aml_audio_malloc(buffer_size);
if (adev->tmp_process_buf == NULL) {
ALOGE("no memory for usb output buffer");
ret = -ENOMEM;
goto err_hp_buf;
}
memset(adev->tmp_process_buf, 0, buffer_size);
if (0 != aml_audio_config_parser()) {
ALOGE("%s() Audio Config file parsing error\n",__FUNCTION__);
} else {
audio_hal_property_list_loading();
adev->dvb_standards = adev_get_dvb_standards();
}
if (0 != aml_audio_avsync_parser()) {
ALOGE("%s() Audio Config file parsing error\n",__FUNCTION__);
} else {
audio_hal_avsync_latency_loading();
}
/* init speaker tuning buffers */
ret = ring_buffer_init(&(adev->spk_tuning_rbuf), spk_tuning_buf_size);
if (ret < 0) {
ALOGE("Fail to init audio spk_tuning_rbuf!");
goto err_tmp_process_buf;
}
adev->spk_tuning_buf_size = spk_tuning_buf_size;
/* init spdif ring buffers */
ret = ring_buffer_init(&(adev->spdif_rbuf), spdif_ring_buf_size);
if (ret < 0) {
ALOGE("Fail to init audio spdif_rbuf!");
goto err_ringbuf;
}
/* if no latency set by prop, use default one */
if (spdif_tuning_latency == 0) {
spdif_tuning_latency = DEFAULT_SPK_EXTRA_LATENCY_MS;
} else if (spdif_tuning_latency > MAX_SPK_EXTRA_LATENCY_MS) {
spdif_tuning_latency = MAX_SPK_EXTRA_LATENCY_MS;
} else if (spdif_tuning_latency < 0) {
spdif_tuning_latency = 0;
}
// try to detect which dolby lib is readable
adev->dolby_lib_type = detect_dolby_lib_type();
adev->dolby_lib_type_last = adev->dolby_lib_type;
adev->aml_dolby_lib_version = get_aml_dolby_lib_version();
adev->dev_ms12_version = adev->aml_dolby_lib_version->eDevVersion;
ALOGI("%s(), dolby_lib_type: %d, dev_ms12_version: %d", __func__,
adev->dolby_lib_type, adev->dev_ms12_version);
adev->dolby_decode_enable = dolby_lib_decode_enable(adev->dolby_lib_type_last);
bool dts_decoder_support = (aml_get_json_int_value("AML_Dts_Decoder_Support", 1) == 1) ? true : false;
adev->dts_lib_type = detect_dts_lib_type(dts_decoder_support);
if (adev->dts_lib_type == eDTSXLib) {
adev->dts_decode_enable = 1;
adev->dts_x_spk_drc_cut = aml_get_json_int_value("AML_Dts_Speaker_Drc_Cut", 100);
adev->dts_x_spk_drc_boost = aml_get_json_int_value("AML_Dts_Speaker_Drc_Boost", 100);
adev->dts_x_spk_drc_curve = aml_get_json_int_value("AML_Dts_Speaker_Drc_Curve", 2);
adev->dts_x_hdmi_drc_cut = aml_get_json_int_value("AML_Dts_HDMI_Drc_Cut", 100);
adev->dts_x_hdmi_drc_boost = aml_get_json_int_value("AML_Dts_HDMI_Drc_Boost", 100);
adev->dts_x_hdmi_drc_curve = aml_get_json_int_value("AML_Dts_HDMI_Drc_Curve", 2);
adev->dts_x_spdif_drc_cut = aml_get_json_int_value("AML_Dts_Spdif_Drc_Cut", 100);
adev->dts_x_spdif_drc_boost = aml_get_json_int_value("AML_Dts_Spdif_Drc_Boost", 100);
adev->dts_x_spdif_drc_curve = aml_get_json_int_value("AML_Dts_Spdif_Drc_Curve", 2);
adev->dts_x_hp_drc_cut = aml_get_json_int_value("AML_Dts_Headphone_Drc_Cut", 100);
adev->dts_x_hp_drc_boost = aml_get_json_int_value("AML_Dts_Headphone_Drc_Boost", 100);
adev->dts_x_hp_drc_curve = aml_get_json_int_value("AML_Dts_Headphone_Drc_Curve", 2);
if (is_TV(adev)) {
dtsx_spk_loudness_enable(aml_get_json_int_value("AML_Dts_SPK_Loudness_Enable", 1));
dtsx_spk_loudness_target(aml_get_json_int_value("AML_Dts_SPK_Loudness_Target", -20));
} else {
dtsx_spk_loudness_enable(aml_get_json_int_value("AML_Dts_PCM_Loudness_Output_Enable", 1));
dtsx_spk_loudness_target(aml_get_json_int_value("AML_Dts_PCM_Loudness_Output_Target", -20));
}
} else {
adev->dts_decode_enable = dts_lib_decode_enable();
}
adev->mpegh_decode_enable = mpegh_lib_decode_enable();
adev->dtsx_drc_enable = false;
/* convert MS to data buffer length need to cache */
adev->spk_tuning_lvl = (spdif_tuning_latency * bytes_per_frame * MM_FULL_POWER_SAMPLING_RATE) / 1000;
/* end of spker tuning things */
*device = &adev->hw_device.common;
adev->dts_post_gain = 1.0;
for (i = 0; i < OUTPORT_MAX; i++) {
adev->sink_gain[i] = 1.0f;
adev->last_volume[i] = 1.0f;
}
/* get the device Loudness level */
adev->loudness_level = get_loudness_level();
adev->need_remove_conti_mode = false;
adev->audio_process_bitwidth = aml_get_json_int_value("Audio_Process_BitWidth", 16);
adev->dual_spdif_support = aml_get_json_int_value(DUAL_SPDIF,0);
adev->ms12_force_ddp_out = aml_get_json_int_value(FORCE_DDP,0);
adev->ms12_cpu_affinity = aml_get_json_int_value("Ms12_CPU_Affinity", 12);
adev->ms12_thread_priority = aml_get_json_int_value("Ms12_Thread_Priority", 5);
adev->out_write_thread_priority = aml_get_json_int_value("Out_Write_Thread_Priority", 5);
adev->spdif_enable = true;
adev->ms12_dynamic_sleep = aml_audio_property_get_bool("ro.vendor.media.audio.ms12.dynamic_sleep", false);
adev->mpegh_ui_persistencemem = aml_audio_malloc(AML_MPEGH_PERSISTENCE_BUFSIZE);
adev->mpegh_ui_persistencememsize = AML_MPEGH_PERSISTENCE_BUFSIZE;
adev->mpegh_audioscenexml = NULL;
adev->mpegh_audioscenexmlsize = 0;
adev->hdmitx_multi_ch_src = aml_get_json_int_value("HDMITX_Multi_CH_Src_Select", -1);
adev->hdmitx_hbr_src = aml_get_json_int_value("HDMITX_HBR_Src_Select", -1);
adev->hdmitx_src = aml_get_json_int_value("HDMITX_Src_Select", -1);
adev->sbr_spk_ott_hbr_same_tdm = aml_get_json_int_value("SBR_Speaker_OTT_HBR_Same_TDM", 0);
if (adev->hdmitx_src != -1) {
adev->spdif_independent = true;
}
open_mixer_handle(&adev->alsa_mixer);
if (init_audio_hw_resource_mgr(adev, &adev->alsa_mixer) < 0) {
AM_LOGE("ERROR: init_audio_hw_resource_mgr failed");
ret = -EINVAL;
goto err_spdifbuf;
}
/*for ms12 case, we set default continuous mode*/
if (eDolbyMS12Lib == adev->dolby_lib_type) {
adev->is_ms12_tuning_dat = false;
adev->dolby_ms12_dap_init_mode = DAP_NO_PROC;
char* str_val = aml_get_json_string_value(DOLBY_MS12_CONFIG_TYPE);
if (NULL != str_val) {
adev->dolby_ms12_audio_config = (strcasestr(str_val, "Z") != NULL) ? MS12_CONFIG_Z : \
(strcasestr(str_val, "X") != NULL) ? MS12_CONFIG_X : MS12_CONFIG_Y;
if (adev->dolby_ms12_audio_config == MS12_CONFIG_Z || adev->dolby_ms12_audio_config == MS12_CONFIG_X) {
char *tuning_dat_ini_path = aml_audio_cfg_path_get("AMLOG_MS12_TUNING_DATA");
aml_ms12_version_t *ms12_version = get_aml_dolby_lib_version();
const char *default_tuing_data = (eDolbyMS12_V282 == ms12_version->eDevVersion) ? DOLBY_TUNING_DAT_V28 : DOLBY_TUNING_DAT;
const char *path_to_use = tuning_dat_ini_path ? tuning_dat_ini_path : default_tuing_data;
AM_LOGI("ms12_version:%d path_to_use %s tuning_dat_ini_path:%s default_tuing_data:%s",
ms12_version->eDevVersion, path_to_use, tuning_dat_ini_path, default_tuing_data);
adev->is_ms12_tuning_dat = is_ms12_tuning_dat_in_dut(path_to_use);
if (adev->is_ms12_tuning_dat) {
memset(adev->ms12_tuning_dat_path, 0, AUDIO_HAL_CHAR_MAX_LEN);
memcpy(adev->ms12_tuning_dat_path, path_to_use, strlen(path_to_use));
AM_LOGI("use dolby config %d, ms12_tuning_dat=%d, ms12_tuning_dat_path=%s", adev->dolby_ms12_audio_config, adev->is_ms12_tuning_dat, adev->ms12_tuning_dat_path);
}
if (is_STB(adev)) {
//STB set to Dolby config X, no DOLBY_TUNING_DAT, set DAP mode to DAP_CONTENT_PROC for content processing.
//If DOLBY_TUNING_DAT is present, it indicates soundbar product, requiring dap to be mounted at boot.
adev->dolby_ms12_dap_init_mode = adev->is_ms12_tuning_dat ? DAP_CONTENT_PROC_DEVICE_PROC_MODE : DAP_CONTENT_PROC_MODE;
} else {
if (!adev->is_ms12_tuning_dat) {
adev->dolby_ms12_audio_config = MS12_CONFIG_Y;
AM_LOGE("DOLBY_TUNING_DAT is missing, updated dolby config to Y (%s)", str_val);
}
}
}
} else {
adev->dolby_ms12_audio_config = MS12_CONFIG_Y;
ALOGE("dolby config type needs to be configured");
}
}
pthread_mutex_init(&adev->patch_lock, NULL);
pthread_mutex_init(&adev->active_outputs_lock, NULL);
pthread_mutex_init(&adev->dtv_patch_lock, NULL);
pthread_mutex_init(&adev->low_latency_param_lock, NULL);
for (int i = I2S_DEVICE; i < ALSA_DEVICE_CNT; i++) {
pthread_mutex_init(&adev->alsa_lock[i], NULL);
}
for (int i = SPDIF_HANDLE_1; i < SPDIF_HANDLE_CNT; i++) {
pthread_mutex_init(&adev->spdif_handle_lock[i], NULL);
}
for (int i = 0; i < AUDIO_INPUT_TYPE_CNT; i++) {
pthread_mutex_init(&adev->input_handle_lock[i], NULL);
}
//set HDMI EDID
//add "Hdmi_EDID": "18,15,07,50,57,06,01,3e,1e,c0,5f,7e,07,67,04,03,7b,04,5a", to /vendor/etc/halaudio/aml_audio_config.json
//18(decimalism) is the length of EDID,
//SAD(hexadecimal) are 15,07,50(dd);57,06,01(ddp);3e,1e,c0(dts);5f,7e,07(dts-hd);67,04,03(mat);7b,04,5a(mpegh)
//Note:We don't need to set PCM EDID(09,7f,05),since AML_MIXER_ID_HDMIIN_AUDIO_EDID will keep the original PCM EDID
char* str_EDID = aml_get_json_string_value(HDMI_EDID);
if (NULL != str_EDID) {
AM_LOGI("str_EDID=%s", str_EDID);
unsigned int *ptr = (unsigned int *)(adev->Hdmi_EDID);
char *pt = NULL, *tmp = NULL;
int i = 0;
int edid_length = 0;
int array_length = EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE;
pt = strtok_r (str_EDID, ", ", &tmp);
//The format of HDMIIN EDID is:
//4 bytes (default value is 0), 4 bytes (EDID length), SAD,SAD... The length of SAD is 3 bytes
ptr[0] = 0;
while (pt != NULL && i < array_length) {
char *endptr;
if (i == 0) {
AM_LOGI("[%d] edid_length:%d(0x%x)", i , atoi(pt), atoi(pt));
edid_length = atoi (pt);//EDID_len
ptr[1] = edid_length;
}
else {
AM_LOGI("[%d] %ld(0x%lx)", i , strtol (pt, &endptr, 16), strtol (pt, &endptr, 16));
if (TLV_HEADER_SIZE + i - 1 < EDID_ARRAY_MAX_LEN) {
adev->Hdmi_EDID[TLV_HEADER_SIZE + i - 1] = strtol (pt, &endptr, 16);
} else {
AM_LOGE("index(%zd) is greater than or equal to total num(%d)", TLV_HEADER_SIZE + i - 1, EDID_ARRAY_MAX_LEN);
break;
}
}
pt = strtok_r (NULL, ", ", &tmp);
i++;
}
if (edid_length == (i - 1)) {
int suitable_edid_len = (edid_length <= (EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE)) ? (edid_length) : (EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE);
adev->Hdmi_EDID_len = suitable_edid_len;
aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID,
adev->Hdmi_EDID, (suitable_edid_len + TLV_HEADER_SIZE));
} else {
AM_LOGE("The EDID is error in json, edid_length(%d)!=real_len(%d)", edid_length, i - 1);
}
} else {
if (is_TV(adev)) {
char edid_string[3];
if (aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID, edid_string, TLV_HEADER_SIZE)) {
AM_LOGE("Set default EDID fail");
} else {
AM_LOGI("Set default EDID success");
}
}
}
/* Set the earctx mode by the property, only need set false */
earctx_mode = aml_audio_property_get_bool("persist.sys.vendor.earc_settings", true);
if (!earctx_mode) {
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_EARC_TX_EARC_MODE, earctx_mode);
ALOGI("eARC_TX eARC Mode get from property: %d\n", earctx_mode);
}
adev->atoms_lock_flag = false;
if (eDolbyDcvLib == adev->dolby_lib_type) {
adev->dcvlib_bypass_enable = 1;
}
memset(&adev->dts_hd, 0, sizeof(struct dca_dts_dec));
adev->sound_track_mode = 0;
adev->downmix_type = AM_DOWNMIX_LORO;
ALOGI("%s() adev->dolby_lib_type = %d", __FUNCTION__, adev->dolby_lib_type);
adev->audio_type = LPCM;
/* init arc hdmi capability manager */
if (init_hdmi_capability_manager(adev) < 0) {
ALOGE("%s() line:%d error! new hdmi_capability_manager failed", __func__, __LINE__);
ret = -EINVAL;
goto err_spdifbuf;
}
/* init device patch manager */
if (init_patch_manager(adev) < 0) {
ALOGE("%s() line:%d error! new patch_manger failed", __func__, __LINE__);
ret = -EINVAL;
goto err_spdifbuf;
}
#ifdef USE_EQ_DRC
ret = eq_drc_init(&adev->eq_data);
if (ret >= 0) {
if (0 != (ret & NO_AMLOGIC_EFFECT_INI)) {
/*If AMLOGIC_EFFECT_INI file missing, set default EQ_DRC full band params
*#Amlogic_DRC_Param_Generator -M 0 -m 2 -b 0 -a 2 -r 100 -e 10 -k 0 -t 0 -o 10
*/
adev->Drc_data.band_id = 0;
adev->Drc_data.attrack_time = 2;
adev->Drc_data.release_time = 100;
adev->Drc_data.estimate_time = 10;
adev->Drc_data.K = 0;
adev->Drc_data.threshold = 0;
adev->Drc_data.offset = 10;
setfb_drc(adev->Drc_data.band_id,adev->Drc_data.attrack_time,adev->Drc_data.release_time,
adev->Drc_data.estimate_time, adev->Drc_data.K, adev->Drc_data.threshold, adev->Drc_data.offset);
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AED_FULL_DRC_ENABLE, 0);
}
ALOGI("%s() audio source gain: atv:%f, dtv:%f, hdmiin:%f, av:%f, media:%f", __func__,
adev->eq_data.s_gain.atv, adev->eq_data.s_gain.dtv,
adev->eq_data.s_gain.hdmi, adev->eq_data.s_gain.av, adev->eq_data.s_gain.media);
ALOGI("%s() audio device gain: speaker:%f, spdif_arc:%f, headphone:%f", __func__,
adev->eq_data.p_gain.speaker, adev->eq_data.p_gain.spdif_arc,
adev->eq_data.p_gain.headphone);
struct component_noise_gate *noise_gate = get_noise_gate_instance(adev);
aml_noise_gate_set_param(noise_gate, adev->eq_data.noise_gate.aml_ng_enable,
adev->eq_data.noise_gate.aml_ng_level,
adev->eq_data.noise_gate.aml_ng_attack_time,
adev->eq_data.noise_gate.aml_ng_release_time);
}
#else
adev->eq_data.s_gain.atv = 1.0;
adev->eq_data.s_gain.dtv = 1.0;
adev->eq_data.s_gain.hdmi= 1.0;
adev->eq_data.s_gain.av = 1.0;
adev->eq_data.s_gain.media = 1.0;
adev->eq_data.p_gain.speaker= 1.0;
adev->eq_data.p_gain.spdif_arc = 1.0;
adev->eq_data.p_gain.headphone = 1.0;
#endif
adev->aml_ai_audio_type = aml_ai_audio_support();
AM_LOGI("AI %s, type is %d", (adev->aml_ai_audio_type != eAmlAiAudioUnsupported) ? "support": "not support", adev->aml_ai_audio_type);
/* set source volume to audio_hw_resource_mgr, audiohal subsequent processing uses the value in audio_hw_resource_mgr */
set_inport_gain(adev, INPORT_ATV, adev->eq_data.s_gain.atv);
set_inport_gain(adev, INPORT_TUNER, adev->eq_data.s_gain.dtv);
set_inport_gain(adev, INPORT_HDMIIN, adev->eq_data.s_gain.hdmi);
set_inport_gain(adev, INPORT_HDMIIN1, adev->eq_data.s_gain.hdmi);
set_inport_gain(adev, INPORT_LINEIN, adev->eq_data.s_gain.av);
set_inport_gain(adev, INPORT_MEDIA, adev->eq_data.s_gain.media);
if (is_TV(adev) || is_SBR(adev)) {
adev->default_alsa_ch = aml_audio_get_default_alsa_output_ch();
} else {
/* for stb/ott, fixed 2 channels speaker output for alsa*/
adev->default_alsa_ch = 2;
}
adev_headphone_limiter_init(adev);
if (NULL == adev->headphone_limiter && adev->eq_data.p_gain.headphone > 1.0) {
adev->eq_data.p_gain.headphone = 1.0;
AM_LOGE("HP post gain < 0dB without HP limiter, reset to 0dB");
}
//Enable audio delay feature
//If the platform need audio delay feature, please add "Audio_Delay_Max": 1000 to aml_audio_config.json
adev->max_audio_delay = aml_get_json_int_value(AUDIO_DELAY_MAX, 0);
if (adev->max_audio_delay) {
ret = aml_audio_delay_init(adev->max_audio_delay);
if (ret < 0) {
ALOGE("[%s:%d] aml_audio_delay_init fail", __func__, __LINE__);
goto err_spdifbuf;
}
} else {
AM_LOGI("This platform doesn't support audio delay feature");
}
adev->advol_level = 75;
adev->master_volume = 1.0;
// FIXME: current MS12 is not compatible with SUBMIXER, when MS12 lib exists, use ms12 system.
if (eDolbyMS12Lib == adev->dolby_lib_type) {
adev->useSubMix = false;
} else {
adev->useSubMix = true;
}
pthread_mutex_init(&adev->audio_mixer_lock, NULL);
if (adev->useSubMix) {
ret = initHalSubMixing(MIXER_LPCM, adev, is_TV(adev));
if (0 != ret) {
AM_LOGE("initHalSubMixing err, need check!!");
}
adev->restart_device_flag = false;
}
#ifdef USE_MEDIAINFO
adev->minfo_h = minfo_init();
if (!adev->minfo_h) {
ALOGE("Mediainfo init failed");
}
adev->info_rid = -1;
adev->minfo_atmos_flag = -1;
#endif
// adev->debug_flag is set in hw_write()
// however, sometimes function didn't goto hw_write() before encounting error.
// set debug_flag here to see more debug log when debugging.
adev->debug_flag = aml_audio_get_debug_flag();
aml_audio_debug_open(aml_get_json_string_value(DUMP_DEFAULT_PATH));
#ifndef NO_AUDIO_CAP
pthread_mutex_init(&adev->cap_handle_lock, NULL);
#endif
//set default mode to line
aml_audio_set_drc_control("-drc 0 -bs 100 -cs 100", &adev->decoder_drc_control);
aml_audio_set_drc_control("-dap_drc 0 -b 100 -c 100", &adev->dap_drc_control);
adev->drc_mode_ui_config = false;
AM_LOGD("adev->decoder_drc_control(%d),adev->dap_drc_control(%d) adev->drc_mode_ui_config (%d)", adev->decoder_drc_control, adev->dap_drc_control,adev->drc_mode_ui_config);
if (is_STB(adev)) {
aml_audio_uevent_open(adev_uevent_callback);
AM_LOGI("regist uevent callback");
} else {
AM_LOGI("Not STB, do not regist uevent callback");
}
ALOGD("%s adev->dolby_lib_type:%d is_TV:%d", __func__, adev->dolby_lib_type, is_TV(adev));
//set msync log level
log_set_level(AVS_LOG_INFO);
adev->last_stream_write_data = true;
adev->native_postprocess.effect_ctrl.is_dts = false;
adev->tmp_buffer_8ch_size = 1024 * 4 * adev->default_alsa_ch;
/* coverity[TAINTED_SCALAR]: adev->default_alsa_ch may come from getenv, but under control */
adev->tmp_buffer_8ch = aml_audio_malloc(adev->tmp_buffer_8ch_size);
if (!adev->tmp_buffer_8ch) {
AM_LOGE("alloc tmp_buffer_8ch failed");
ret = -ENOMEM;
goto err_spdifbuf;
}
memset(adev->tmp_buffer_8ch, 0, adev->tmp_buffer_8ch_size);
karaoke_manager_init(&adev->kara_manager);
/* create thread for communication between Audio Hal and MS12 */
if ((eDolbyMS12Lib == adev->dolby_lib_type)) {
ret = dolby_ms12_desc_init(adev);
if (0 != ret) {
AM_LOGE("ms12_desc init fail!");
goto Err_MS12_Init;
}
adev->enable_deep_unit = aml_get_json_int_value("DEEP_BUFFER_UNIT_ENABLE", 0);
if (adev->enable_deep_unit) {
deep_buffer_unit_config deep_config;
deep_config.ms12_pointer = &adev->ms12;
deep_buffer_unit_init(&adev->deep_unit, &deep_config);
AM_LOGI("deep_buffer_unit_init success!");
}
}
mixer_out_unit_init(&adev->mixer_unit);
#ifdef SUPPORT_EXTERNAL_OUTPUT
pthread_mutex_init(&adev->external_handle_lock, NULL);
aml_external_device_port_init(adev);
#endif
audio_effect_load_interface(&(adev->hw_device), &audio_effect);
#ifdef __LP64__
adev->hw_device.common.reserved[0] = (uint64_t)audio_effect;
#else
adev->hw_device.common.reserved[0] = (uint32_t)audio_effect;
#endif
#ifdef NO_SERVER
adev->audio_debug_thread_exit = false;
if (pthread_create(&(adev->audio_debug_thread), NULL, &audio_debug_threadloop, (void *)adev)) {
AM_LOGE("create audio debug thread fail!!!");
} else {
AM_LOGI("create audio debug thread success!!!");
}
#endif
if (aml_audio_cmd_worker_thread_create(adev) < 0) {
ALOGE("%s() line:%d error! new aml_audio_cmd_worker_thread_create failed", __func__, __LINE__);
ret = -EINVAL;
goto err_adev;
}
AM_LOGD("exit dual_spdif_support(%d) audio_process_bitwidth(%d)", adev->dual_spdif_support, adev->audio_process_bitwidth);
return 0;
Err_MS12_Init:
aml_audio_free(adev->tmp_buffer_8ch);
err_spdifbuf:
ring_buffer_release(&adev->spdif_rbuf);
err_ringbuf:
ring_buffer_release(&adev->spk_tuning_rbuf);
err_tmp_process_buf:
aml_audio_free(adev->tmp_process_buf);
err_hp_buf:
aml_audio_free(adev->hp_output_buf);
err_spdif_buf:
aml_audio_free(adev->spdif_output_buf);
err_effect_buf:
aml_audio_free(adev->effect_buf);
err_adev:
aml_audio_free(adev);
/*coverity[LOCK_EVASION]: There will be no competition here*/
g_adev = NULL;
*device = NULL;
err:
return ret;
}
static struct hw_module_methods_t 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 = "aml audio HW HAL",
.author = "amlogic, Corp.",
.methods = &hal_module_methods,
},
};