/*!
\copyright Copyright (c) 2017-2023 Qualcomm Technologies International, Ltd.
All Rights Reserved.
Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version
\file kymera_anc.c
\brief Kymera ANC code
*/
#include "kymera_anc.h"
#include "kymera_dsp_clock.h"
#include "kymera_lock.h"
#include "kymera_internal_msg_ids.h"
#include "kymera_tones_prompts.h"
#include "kymera_anc_common.h"
#include <audio_power.h>
#include <vmal.h>
#include <file.h>
#include <cap_id_prim.h>
#include <opmsg_prim.h>
#include "kymera_config.h"
#include "kymera_anc_common.h"
#include "kymera.h"
#include "microphones.h"
#include "kymera_source_sync.h"
#include "kymera_setup.h"
#include "kymera_mic_if.h"
#include "anc_state_manager.h"
#include "av_seids.h"
#ifdef ENABLE_ANC
typedef struct
{
uint8 anc_ff_gain[MAXIMUM_AUDIO_ANC_INSTANCES];
} kymera_anc_data_t;
static kymera_anc_data_t kymera_anc_data;
#define kymeraAnc_GetData() (&kymera_anc_data)
#define ANC_TUNING_SINK_USB_LEFT 0 /*can be any other backend device. PCM used in this tuning graph*/
#define ANC_TUNING_SINK_USB_RIGHT 1
#define ANC_TUNING_SINK_FBMON_LEFT 2 /*reserve slots for FBMON tap. Always connected.*/
#define ANC_TUNING_SINK_FBMON_RIGHT 3
#define ANC_TUNING_SINK_MIC1_LEFT 4 /* must be connected to internal ADC. Analog or digital */
#define ANC_TUNING_SINK_MIC1_RIGHT 5
#define ANC_TUNING_SINK_MIC2_LEFT 6
#define ANC_TUNING_SINK_MIC2_RIGHT 7
#define ANC_TUNING_SOURCE_USB_LEFT 0 /*can be any other backend device. USB used in the tuning graph*/
#define ANC_TUNING_SOURCE_USB_RIGHT 1
#define ANC_TUNING_SOURCE_DAC_LEFT 2 /* must be connected to internal DAC */
#define ANC_TUNING_SOURCE_DAC_RIGHT 3
#define ANC_TUNNING_START_DELAY (200U)
#define ANC_TUNNING_USB_SAMPLING_RATE 48000 /* Only 48KHz supported for anc tuning */
#define ANC_TUNNING_USB_AUDIO_CHANNELS 2 /* Number of mic and speaker channels in the audio data stream */
#define KYMERA_ANC_MIC_SAMPLE_RATE (16000U)
/*! Macro for creating messages */
#define MAKE_KYMERA_MESSAGE(TYPE) \
TYPE##_T *message = PanicUnlessNew(TYPE##_T);
/* Qualcomm-provided downloadable anc tuning capability for QCC517x */
#define CAP_ID_DOWNLOAD_ANC_TUNING_QCC517X (0x40B2)
#ifdef DOWNLOAD_USB_AUDIO
#define EB_CAP_ID_USB_AUDIO_RX CAP_ID_DOWNLOAD_USB_AUDIO_RX
#define EB_CAP_ID_USB_AUDIO_TX CAP_ID_DOWNLOAD_USB_AUDIO_TX
#else
#define EB_CAP_ID_USB_AUDIO_RX CAP_ID_USB_AUDIO_RX
#define EB_CAP_ID_USB_AUDIO_TX CAP_ID_USB_AUDIO_TX
#endif
#ifdef ENABLE_ADAPTIVE_ANC
#define ancConfigIsAdvancedAdaptiveAnc() (AncConfig_IsAdvancedAnc())
#else
#define ancConfigIsAdvancedAdaptiveAnc() (FALSE)
#endif
#define GAIN_20_12_FORMAT_TO_Q2N_SF_FACTOR (43541) /* 2^(20-2)/20*log(2) = 43541 */
#define GAIN_dB_TO_FIXED_POINT(x, num_fractional_bits) ((x << num_fractional_bits))
#define GAIN_dB_TO_Q6_9_FIXED_POINT(x) (GAIN_dB_TO_FIXED_POINT(x, 9))
#define GAIN_dB_TO_Q20_12_FIXED_POINT(x) (GAIN_dB_TO_FIXED_POINT(x, 12))
#define GAIN_20_12_FORMAT_TO_6_9_FORMAT(x) ((int16)(x >> 3))
#define GAIN_20_12_FORMAT_TO_Q2N_SF(x) (x * GAIN_20_12_FORMAT_TO_Q2N_SF_FACTOR)
#define GAIN_Q2N_SF_FORMAT_TO_20_12(x) (x / GAIN_20_12_FORMAT_TO_Q2N_SF_FACTOR)
#define GET_INTEGRAL_PART_FROM_FIXED_POINT(x, num_fractional_bits) (x >> num_fractional_bits)
#define GET_INTEGRAL_PART_FROM_20_12_FORMAT(x) (GET_INTEGRAL_PART_FROM_FIXED_POINT(x, 12))
#define MAX_TARGET_GAIN 255
typedef struct
{
Source mic_in1;
Source mic_in2;
Source fb_mon;
Sink DAC;
}chan_data_t;
static chan_data_t left, right;
#ifdef ENHANCED_ANC_USE_2ND_DAC_ENDPOINT
static Sink eanc_second_dac;
#define SPLITTER_TERMINAL_IN_0 0
#define SPLITTER_TERMINAL_OUT_0 0
#define SPLITTER_TERMINAL_OUT_1 1
/*Splitter is needed for eANC tuning mode to activate second DAC path. ANC tuning capability output is required
for both the ANC instances through a Splitter to DAC EP Left and Right for the Echo Cancellation purpose.*/
static void kymeraAnc_CreateSplitter(void)
{
kymeraTaskData *theKymera = KymeraGetTaskData();
theKymera->output_splitter = (Operator)(VmalOperatorCreate(CAP_ID_SPLITTER));
}
static void kymeraAnc_ConfigureSplitter(void)
{
kymeraTaskData *theKymera = KymeraGetTaskData();
OperatorsSplitterSetWorkingMode(theKymera->output_splitter, splitter_mode_clone_input);
OperatorsSplitterEnableSecondOutput(theKymera->output_splitter, FALSE);
OperatorsSplitterSetDataFormat(theKymera->output_splitter, operator_data_format_pcm);
}
#endif
static kymera_chain_handle_t chain_handle;
static kymera_chain_handle_t KymeraAnc_GetChain(void)
{
return chain_handle;
}
static void kymeraAnc_SetChain(kymera_chain_handle_t chain)
{
chain_handle = chain;
}
static Sink kymeraAnc_MicSink(kymera_chain_handle_t chain_handle, unsigned input_role)
{
return (ChainGetInput(chain_handle, input_role));
}
static uint8_t kymeraAnc_GetRequiredAncMics(void)
{
uint8_t num_mics = 0;
anc_path_enable anc_path = appConfigAncPathEnable();
switch(anc_path)
{
case feed_forward_mode_left_only:
case feed_back_mode_left_only:
num_mics = 1;
break;
case feed_forward_mode:
case feed_back_mode:
case hybrid_mode_left_only:
num_mics = 2;
break;
case hybrid_mode:
num_mics = 4;
break;
default:
break;
}
return num_mics;
}
static void kymeraAnc_GetAncMicConfig(uint16 *mic_ids)
{
anc_path_enable anc_path = appConfigAncPathEnable();
switch(anc_path)
{
case feed_forward_mode:
mic_ids[0] = getAncFeedForwardLeftMic();
mic_ids[1] = getAncFeedForwardRightMic();
break;
case feed_back_mode:
mic_ids[0] = getAncFeedBackLeftMic();
mic_ids[1] = getAncFeedBackRightMic();
break;
case hybrid_mode:
mic_ids[0] = getAncFeedForwardLeftMic();
mic_ids[1] = getAncFeedForwardRightMic();
mic_ids[2] = getAncFeedBackLeftMic();
mic_ids[3] = getAncFeedBackRightMic();
break;
case feed_forward_mode_left_only:
mic_ids[0] = getAncFeedForwardLeftMic();
break;
case feed_back_mode_left_only:
mic_ids[0] = getAncFeedBackLeftMic();
break;
case hybrid_mode_left_only:
mic_ids[0] = getAncFeedForwardLeftMic();
mic_ids[1] = getAncFeedBackLeftMic();
break;
default:
break;
}
}
static void kymeraAnc_GetAncMicSinks(Sink *mic_sinks)
{
anc_path_enable anc_path = appConfigAncPathEnable();
switch(anc_path)
{
case feed_forward_mode_left_only:
case feed_back_mode_left_only:
mic_sinks[0] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_1_IN);
break;
case feed_forward_mode:
case feed_back_mode:
case hybrid_mode_left_only:
mic_sinks[0] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_1_IN);
mic_sinks[1] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_2_IN);
break;
case hybrid_mode:
mic_sinks[0] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_1_IN);
mic_sinks[1] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_2_IN);
mic_sinks[2] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_3_IN);
mic_sinks[3] = kymeraAnc_MicSink(KymeraAnc_GetChain(), EPR_ANC_SPC_4_IN);
break;
default:
break;
}
}
static bool kymeraAnc_MicGetConnectionParameters(uint16 *mic_ids, Sink *mic_sinks, uint8 *num_of_mics, uint32 *sample_rate, Sink *aec_ref_sink)
{
*sample_rate = KYMERA_ANC_MIC_SAMPLE_RATE;
*num_of_mics = kymeraAnc_GetRequiredAncMics();
kymeraAnc_GetAncMicConfig(mic_ids);
kymeraAnc_GetAncMicSinks(mic_sinks);
aec_ref_sink[0] = NULL;
return TRUE;
}
static mic_user_state_t kymeraAnc_GetUserState(void)
{
return mic_user_state_interruptible;
}
static bool kymeraAnc_MicDisconnectIndication(const mic_change_info_t *info)
{
DEBUG_LOG("kymeraAnc_MicDisconnectIndication");
UNUSED(info);
kymera_chain_handle_t chain = KymeraAnc_GetChain();
if(chain != NULL)
{
ChainStop(chain);
}
return TRUE;
}
static void kymeraAnc_MicReconnectedIndication(void)
{
DEBUG_LOG("kymeraAnc_MicReconnectedIndication");
ChainStart(KymeraAnc_GetChain());
}
static const mic_callbacks_t kymeraAnc_Callbacks =
{
.MicGetConnectionParameters = kymeraAnc_MicGetConnectionParameters,
.MicDisconnectIndication = kymeraAnc_MicDisconnectIndication,
.MicReconnectedIndication = kymeraAnc_MicReconnectedIndication,