Skip to content
Snippets Groups Projects
audio_hw.c 445 KiB
Newer Older
 * Copyright (c) 2013-2022, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2013 The Android Open Source Project
 *
 * 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.
 *
 * This file was modified by DTS, Inc. The portions of the
 * code modified by DTS, Inc are copyrighted and
 * licensed separately, as follows:
 *
 * (C) 2014 DTS, Inc.
 *
 * 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.
 * Changes from Qualcomm Innovation Center are provided under the following license:
 * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 * SPDX-License-Identifier: BSD-3-Clause-Clear
 *
 */

#define LOG_TAG "audio_hw_primary"
#define ATRACE_TAG (ATRACE_TAG_AUDIO|ATRACE_TAG_HAL)
/*#define LOG_NDEBUG 0*/
/*#define VERY_VERY_VERBOSE_LOGGING*/
#ifdef VERY_VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif
#include <limits.h>
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/time.h>
#include <stdlib.h>
#include <math.h>
#include <dlfcn.h>
#include <sys/resource.h>
#include <sys/prctl.h>
Aalique Grahame's avatar
Aalique Grahame committed
#include <log/log.h>
#include <cutils/trace.h>
#include <cutils/str_parms.h>
#include <cutils/properties.h>
#include <cutils/atomic.h>
#include <cutils/sched_policy.h>
#include <hardware/audio_effect.h>
#include <hardware/audio_alsaops.h>
#include <system/thread_defs.h>
#include <tinyalsa/asoundlib.h>
Andy Hung's avatar
Andy Hung committed
#include <utils/Timers.h> // systemTime
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_ns.h>
#include <audio_utils/format.h>
#include "audio_hw.h"
#include "audio_perf.h"
#include "platform_api.h"
#include <platform.h>
#include "audio_extn.h"
#include "voice_extn.h"
#include "ip_hdlr_intf.h"
#include "sound/compress_params.h"

#ifdef AUDIO_GKI_ENABLED
#include "sound/audio_compressed_formats.h"
#endif

#include "sound/asound.h"
#include "audio_amplifier.h"

#ifdef DYNAMIC_LOG_ENABLED
#include <log_xml_parser.h>
#define LOG_MASK HAL_MOD_FILE_AUDIO_HW
#include <log_utils.h>
#endif

#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4
/*DIRECT PCM has same buffer sizes as DEEP Buffer*/
#define DIRECT_PCM_NUM_FRAGMENTS 2
#define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
#define VOIP_PLAYBACK_VOLUME_MAX 0x2000
Arun Mirpuri's avatar
Arun Mirpuri committed
#define MMAP_PLAYBACK_VOLUME_MAX 0x2000
#define PCM_PLAYBACK_VOLUME_MAX 0x2000
#define DSD_VOLUME_MIN_DB (-110)
#define AUDIO_IO_PORTS_MAX 32
#define PLAYBACK_GAIN_MAX 1.0f
Aalique Grahame's avatar
Aalique Grahame committed
#define RECORD_GAIN_MIN 0.0f
#define RECORD_GAIN_MAX 1.0f
#define RECORD_VOLUME_CTL_MAX 0x2000

/* treat as unsigned Q1.13 */
#define APP_TYPE_GAIN_DEFAULT         0x2000

#define PROXY_OPEN_RETRY_COUNT           100
#define PROXY_OPEN_WAIT_TIME             20
#define GET_USECASE_AUDIO_PLAYBACK_PRIMARY(db) \
         (db)? USECASE_AUDIO_PLAYBACK_DEEP_BUFFER : \
               USECASE_AUDIO_PLAYBACK_LOW_LATENCY
#define GET_PCM_CONFIG_AUDIO_PLAYBACK_PRIMARY(db) \
         (db)? pcm_config_deep_buffer : pcm_config_low_latency
#define ULL_PERIOD_SIZE (DEFAULT_OUTPUT_SAMPLING_RATE/1000)
#define DEFAULT_VOIP_BUF_DURATION_MS 20
#define DEFAULT_VOIP_BIT_DEPTH_BYTE sizeof(int16_t)
#define DEFAULT_VOIP_SAMP_RATE 48000

#define VOIP_IO_BUF_SIZE(SR, DURATION_MS, BIT_DEPTH) (SR)/1000 * DURATION_MS * BIT_DEPTH

struct pcm_config default_pcm_config_voip_copp = {
    .channels = 1,
    .rate = DEFAULT_VOIP_SAMP_RATE, /* changed when the stream is opened */
    .period_size = VOIP_IO_BUF_SIZE(DEFAULT_VOIP_SAMP_RATE, DEFAULT_VOIP_BUF_DURATION_MS, DEFAULT_VOIP_BIT_DEPTH_BYTE)/2,
    .period_count = 2,
    .format = PCM_FORMAT_S16_LE,
    .avail_min = VOIP_IO_BUF_SIZE(DEFAULT_VOIP_SAMP_RATE, DEFAULT_VOIP_BUF_DURATION_MS, DEFAULT_VOIP_BIT_DEPTH_BYTE)/2,
    .stop_threshold = INT_MAX,
#define MIN_CHANNEL_COUNT                1
#define DEFAULT_CHANNEL_COUNT            2
#define MAX_HIFI_CHANNEL_COUNT           8

Aalique Grahame's avatar
Aalique Grahame committed
#ifndef MAX_TARGET_SPECIFIC_CHANNEL_CNT
#define MAX_CHANNEL_COUNT 1
#else
#define MAX_CHANNEL_COUNT atoi(XSTR(MAX_TARGET_SPECIFIC_CHANNEL_CNT))
#define XSTR(x) STR(x)
#define STR(x) #x
#endif

#define IS_USB_HIFI (MAX_HIFI_CHANNEL_COUNT >= MAX_CHANNEL_COUNT) ? \
                     true : false

#ifdef LINUX_ENABLED
static inline int64_t audio_utils_ns_from_timespec(const struct timespec *ts)
{
	return ts->tv_sec * 1000000000LL + ts->tv_nsec;
}
#endif

static unsigned int configured_low_latency_capture_period_size =
        LOW_LATENCY_CAPTURE_PERIOD_SIZE;

#define MMAP_PERIOD_SIZE (DEFAULT_OUTPUT_SAMPLING_RATE/1000)
#define MMAP_PERIOD_COUNT_MIN 32
#define MMAP_PERIOD_COUNT_MAX 512
#define MMAP_PERIOD_COUNT_DEFAULT (MMAP_PERIOD_COUNT_MAX)

Aalique Grahame's avatar
Aalique Grahame committed
/* This constant enables extended precision handling.
 * TODO The flag is off until more testing is done.
 */
static const bool k_enable_extended_precision = false;
extern int AUDIO_DEVICE_IN_ALL_CODEC_BACKEND;
struct pcm_config pcm_config_deep_buffer = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,
    .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
    .stop_threshold = INT_MAX,
    .avail_min = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
};
struct pcm_config pcm_config_low_latency = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
    .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
    .stop_threshold = INT_MAX,
    .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
};
struct pcm_config pcm_config_haptics_audio = {
    .channels = 1,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
    .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
    .stop_threshold = INT_MAX,
    .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
};

struct pcm_config pcm_config_haptics = {
    .channels = 1,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
    .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
    .stop_threshold = INT_MAX,
    .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
};

static int af_period_multiplier = 4;
struct pcm_config pcm_config_rt = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = ULL_PERIOD_SIZE, //1 ms
    .period_count = 512, //=> buffer size is 512ms
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = ULL_PERIOD_SIZE*8, //8ms
    .stop_threshold = INT_MAX,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = ULL_PERIOD_SIZE, //1 ms
};

struct pcm_config pcm_config_hdmi_multi = {
    .channels = HDMI_MULTI_DEFAULT_CHANNEL_COUNT, /* changed when the stream is opened */
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE, /* changed when the stream is opened */
    .period_size = HDMI_MULTI_PERIOD_SIZE,
    .period_count = HDMI_MULTI_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = INT_MAX,
    .avail_min = 0,
struct pcm_config pcm_config_mmap_playback = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = MMAP_PERIOD_SIZE,
    .period_count = MMAP_PERIOD_COUNT_DEFAULT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = MMAP_PERIOD_SIZE*8,
    .stop_threshold = INT32_MAX,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = MMAP_PERIOD_SIZE, //1 ms
};

struct pcm_config pcm_config_hifi = {
    .channels = DEFAULT_CHANNEL_COUNT, /* changed when the stream is opened */
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE, /* changed when the stream is opened */
    .period_size = HIFI_BUFFER_OUTPUT_PERIOD_SIZE, /* change #define */
    .period_count = HIFI_BUFFER_OUTPUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S24_3LE,
    .start_threshold = 0,
    .stop_threshold = INT_MAX,
    .avail_min = 0,
};

struct pcm_config pcm_config_audio_capture = {
    .channels = 2,
    .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
};

struct pcm_config pcm_config_mmap_capture = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = MMAP_PERIOD_SIZE,
    .period_count = MMAP_PERIOD_COUNT_DEFAULT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = INT_MAX,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = MMAP_PERIOD_SIZE, //1 ms
};

#define AFE_PROXY_CHANNEL_COUNT 2
#define AFE_PROXY_SAMPLING_RATE 48000

#define AFE_PROXY_PLAYBACK_PERIOD_SIZE  768
#define AFE_PROXY_PLAYBACK_PERIOD_COUNT 4

struct pcm_config pcm_config_afe_proxy_playback = {
    .channels = AFE_PROXY_CHANNEL_COUNT,
    .rate = AFE_PROXY_SAMPLING_RATE,
    .period_size = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
    .period_count = AFE_PROXY_PLAYBACK_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
    .stop_threshold = INT_MAX,
    .avail_min = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
};

#define AFE_PROXY_RECORD_PERIOD_SIZE  768
#define AFE_PROXY_RECORD_PERIOD_COUNT 4

Aalique Grahame's avatar
Aalique Grahame committed
struct pcm_config pcm_config_audio_capture_rt = {
    .channels = 2,
    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
    .period_size = ULL_PERIOD_SIZE,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = ULL_PERIOD_SIZE, //1 ms
};

struct pcm_config pcm_config_audio_capture_rt_48KHz = {
    .channels = 2,
    .rate = 48000,
    .period_size = 48,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = 48, //1 ms
};
struct pcm_config pcm_config_audio_capture_rt_32KHz = {
    .channels = 2,
    .rate = 32000,
    .period_size = 32,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = 32, //1 ms
};
struct pcm_config pcm_config_audio_capture_rt_24KHz = {
    .channels = 2,
    .rate = 24000,
    .period_size = 24,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = 24, //1 ms
};
struct pcm_config pcm_config_audio_capture_rt_16KHz = {
    .channels = 2,
    .rate = 16000,
    .period_size = 16,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = 16, //1 ms
};
struct pcm_config pcm_config_audio_capture_rt_8KHz = {
    .channels = 2,
    .rate = 8000,
    .period_size = 8,
    .period_count = 512,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
    .silence_threshold = 0,
    .silence_size = 0,
    .avail_min = 8, //1 ms
};

struct pcm_config pcm_config_afe_proxy_record = {
    .channels = AFE_PROXY_CHANNEL_COUNT,
    .rate = AFE_PROXY_SAMPLING_RATE,
    .period_size = AFE_PROXY_RECORD_PERIOD_SIZE,
    .period_count = AFE_PROXY_RECORD_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = AFE_PROXY_RECORD_PERIOD_SIZE,
    .stop_threshold = INT_MAX,
    .avail_min = AFE_PROXY_RECORD_PERIOD_SIZE,
};

#define AUDIO_MAX_PCM_FORMATS 7

const uint32_t format_to_bitwidth_table[AUDIO_MAX_PCM_FORMATS] = {
    [AUDIO_FORMAT_DEFAULT] = 0,
    [AUDIO_FORMAT_PCM_16_BIT] = sizeof(uint16_t),
    [AUDIO_FORMAT_PCM_8_BIT] = sizeof(uint8_t),
    [AUDIO_FORMAT_PCM_32_BIT] = sizeof(uint32_t),
    [AUDIO_FORMAT_PCM_8_24_BIT] = sizeof(uint32_t),
    [AUDIO_FORMAT_PCM_FLOAT] = sizeof(float),
    [AUDIO_FORMAT_PCM_24_BIT_PACKED] = sizeof(uint8_t) * 3,
};

const char * const use_case_table[AUDIO_USECASE_MAX] = {
    [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",
    [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
    [USECASE_AUDIO_PLAYBACK_WITH_HAPTICS] = "audio-with-haptics-playback",
Meng Wang's avatar
Meng Wang committed
    [USECASE_AUDIO_PLAYBACK_HAPTICS] = "haptics-playback",
    [USECASE_AUDIO_PLAYBACK_ULL]         = "audio-ull-playback",
    [USECASE_AUDIO_PLAYBACK_MULTI_CH]    = "multi-channel-playback",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
    //Enabled for Direct_PCM
    [USECASE_AUDIO_PLAYBACK_OFFLOAD2] = "compress-offload-playback2",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD3] = "compress-offload-playback3",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD4] = "compress-offload-playback4",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD5] = "compress-offload-playback5",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD6] = "compress-offload-playback6",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD7] = "compress-offload-playback7",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD8] = "compress-offload-playback8",
    [USECASE_AUDIO_PLAYBACK_OFFLOAD9] = "compress-offload-playback9",
    [USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
    [USECASE_AUDIO_PLAYBACK_MMAP] = "mmap-playback",
    [USECASE_AUDIO_PLAYBACK_HIFI] = "hifi-playback",
Aalique Grahame's avatar
Aalique Grahame committed
    [USECASE_AUDIO_PLAYBACK_TTS] = "audio-tts-playback",
    [USECASE_AUDIO_RECORD] = "audio-record",
    [USECASE_AUDIO_RECORD2] = "audio-record2",
    [USECASE_AUDIO_RECORD3] = "audio-record3",
    [USECASE_AUDIO_RECORD_COMPRESS] = "audio-record-compress",
    [USECASE_AUDIO_RECORD_COMPRESS2] = "audio-record-compress2",
    [USECASE_AUDIO_RECORD_COMPRESS3] = "audio-record-compress3",
    [USECASE_AUDIO_RECORD_COMPRESS4] = "audio-record-compress4",
    [USECASE_AUDIO_RECORD_COMPRESS5] = "audio-record-compress5",
    [USECASE_AUDIO_RECORD_COMPRESS6] = "audio-record-compress6",
    [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
    [USECASE_AUDIO_RECORD_LOW_LATENCY2] = "low-latency-record2",
    [USECASE_AUDIO_RECORD_FM_VIRTUAL] = "fm-virtual-record",
    [USECASE_AUDIO_RECORD_MMAP] = "mmap-record",
    [USECASE_AUDIO_RECORD_HIFI] = "hifi-record",
    [USECASE_AUDIO_HFP_SCO] = "hfp-sco",
    [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb",
    [USECASE_AUDIO_HFP_SCO_DOWNLINK] = "hfp-sco-downlink",
    [USECASE_AUDIO_HFP_SCO_WB_DOWNLINK] = "hfp-sco-wb-downlink",
    [USECASE_VOICE_CALL] = "voice-call",
    [USECASE_VOICE2_CALL] = "voice2-call",
    [USECASE_VOLTE_CALL] = "volte-call",
    [USECASE_QCHAT_CALL] = "qchat-call",
    [USECASE_VOWLAN_CALL] = "vowlan-call",
    [USECASE_VOICEMMODE1_CALL] = "voicemmode1-call",
    [USECASE_VOICEMMODE2_CALL] = "voicemmode2-call",
    [USECASE_COMPRESS_VOIP_CALL] = "compress-voip-call",
    [USECASE_INCALL_REC_UPLINK] = "incall-rec-uplink",
    [USECASE_INCALL_REC_DOWNLINK] = "incall-rec-downlink",
    [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = "incall-rec-uplink-and-downlink",
    [USECASE_INCALL_REC_UPLINK_COMPRESS] = "incall-rec-uplink-compress",
    [USECASE_INCALL_REC_DOWNLINK_COMPRESS] = "incall-rec-downlink-compress",
    [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS] = "incall-rec-uplink-and-downlink-compress",

    [USECASE_INCALL_MUSIC_UPLINK] = "incall_music_uplink",
    [USECASE_INCALL_MUSIC_UPLINK2] = "incall_music_uplink2",
    [USECASE_AUDIO_SPKR_CALIB_RX] = "spkr-rx-calib",
    [USECASE_AUDIO_SPKR_CALIB_TX] = "spkr-vi-record",

    [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
    [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
    [USECASE_AUDIO_RECORD_AFE_PROXY2] = "afe-proxy-record2",
    [USECASE_AUDIO_PLAYBACK_SILENCE] = "silence-playback",
    /* Transcode loopback cases */
    [USECASE_AUDIO_TRANSCODE_LOOPBACK_RX] = "audio-transcode-loopback-rx",
    [USECASE_AUDIO_TRANSCODE_LOOPBACK_TX] = "audio-transcode-loopback-tx",

    [USECASE_AUDIO_PLAYBACK_VOIP] = "audio-playback-voip",
    [USECASE_AUDIO_RECORD_VOIP] = "audio-record-voip",
    [USECASE_AUDIO_RECORD_VOIP_LOW_LATENCY] = "audio-record-voip-low-latency",
    /* For Interactive Audio Streams */
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1] = "audio-interactive-stream1",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2] = "audio-interactive-stream2",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3] = "audio-interactive-stream3",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4] = "audio-interactive-stream4",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5] = "audio-interactive-stream5",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6] = "audio-interactive-stream6",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7] = "audio-interactive-stream7",
    [USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8] = "audio-interactive-stream8",
    [USECASE_AUDIO_EC_REF_LOOPBACK] = "ec-ref-audio-capture",

    [USECASE_AUDIO_A2DP_ABR_FEEDBACK] = "a2dp-abr-feedback",

    [USECASE_AUDIO_PLAYBACK_MEDIA] = "media-playback",
    [USECASE_AUDIO_PLAYBACK_MEDIA_LL] = "media-playback-ll",
    [USECASE_AUDIO_PLAYBACK_SYS_NOTIFICATION] = "sys-notification-playback",
    [USECASE_AUDIO_PLAYBACK_NAV_GUIDANCE] = "nav-guidance-playback",
    [USECASE_AUDIO_PLAYBACK_NAV_GUIDANCE_LL] = "nav-guidance-playback-ll",
    [USECASE_AUDIO_PLAYBACK_PHONE] = "phone-playback",
    [USECASE_AUDIO_PLAYBACK_PHONE_LL] = "phone-playback-ll",
    [USECASE_AUDIO_PLAYBACK_ALERTS] = "alerts-playback",
    [USECASE_AUDIO_PLAYBACK_ALERTS_LL] = "alerts-playback-ll",
    [USECASE_AUDIO_PLAYBACK_FRONT_PASSENGER] = "front-passenger-playback",
    [USECASE_AUDIO_PLAYBACK_REAR_SEAT] = "rear-seat-playback",
    [USECASE_AUDIO_FM_TUNER_EXT] = "fm-tuner-ext",
    [USECASE_ICC_CALL] = "icc-call",

    [USECASE_AUDIO_RECORD_BUS] = "audio-record",
    [USECASE_AUDIO_RECORD_BUS_FRONT_PASSENGER] = "front-passenger-record",
    [USECASE_AUDIO_RECORD_BUS_REAR_SEAT] = "rear-seat-record",
    [USECASE_AUDIO_PLAYBACK_SYNTHESIZER] = "synth-loopback",
    [USECASE_AUDIO_RECORD_ECHO_REF_EXT] = "echo-reference-external",
static const audio_usecase_t offload_usecases[] = {
    USECASE_AUDIO_PLAYBACK_OFFLOAD,
    USECASE_AUDIO_PLAYBACK_OFFLOAD2,
    USECASE_AUDIO_PLAYBACK_OFFLOAD3,
    USECASE_AUDIO_PLAYBACK_OFFLOAD4,
    USECASE_AUDIO_PLAYBACK_OFFLOAD5,
    USECASE_AUDIO_PLAYBACK_OFFLOAD6,
    USECASE_AUDIO_PLAYBACK_OFFLOAD7,
    USECASE_AUDIO_PLAYBACK_OFFLOAD8,
    USECASE_AUDIO_PLAYBACK_OFFLOAD9,
};
static const audio_usecase_t interactive_usecases[] = {
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM1,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM2,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM3,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM4,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM5,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM6,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM7,
    USECASE_AUDIO_PLAYBACK_INTERACTIVE_STREAM8,
};

#define STRING_TO_ENUM(string) { #string, string }
struct string_to_enum {
    const char *name;
    uint32_t value;
static const struct string_to_enum channels_name_to_enum_table[] = {
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_2POINT1),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_SURROUND),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_PENTA),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
    STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO),
    STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO),
    STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_1),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_2),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_3),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_4),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_5),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_6),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_7),
    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_8),
static const struct string_to_enum formats_name_to_enum_table[] = {
    STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT),
    STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED),
    STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT),
    STRING_TO_ENUM(AUDIO_FORMAT_AC3),
    STRING_TO_ENUM(AUDIO_FORMAT_E_AC3),
    STRING_TO_ENUM(AUDIO_FORMAT_E_AC3_JOC),
    STRING_TO_ENUM(AUDIO_FORMAT_DOLBY_TRUEHD),
    STRING_TO_ENUM(AUDIO_FORMAT_DTS),
    STRING_TO_ENUM(AUDIO_FORMAT_DTS_HD),
    STRING_TO_ENUM(AUDIO_FORMAT_IEC61937)
};

//list of all supported sample rates by HDMI specification.
static const int out_hdmi_sample_rates[] = {
    32000, 44100, 48000, 88200, 96000, 176400, 192000,
};

static const struct string_to_enum out_sample_rates_name_to_enum_table[] = {
    STRING_TO_ENUM(32000),
    STRING_TO_ENUM(44100),
    STRING_TO_ENUM(48000),
    STRING_TO_ENUM(88200),
    STRING_TO_ENUM(96000),
    STRING_TO_ENUM(176400),
    STRING_TO_ENUM(192000),
    STRING_TO_ENUM(352800),
    STRING_TO_ENUM(384000),
struct in_effect_list {
    struct listnode list;
    effect_handle_t handle;
};

static const audio_usecase_t record_usecases[] = {
    USECASE_AUDIO_RECORD,
    USECASE_AUDIO_RECORD2,
    USECASE_AUDIO_RECORD3,
};

static const audio_usecase_t low_latency_record_usecases[] = {
    USECASE_AUDIO_RECORD_LOW_LATENCY,
    USECASE_AUDIO_RECORD_LOW_LATENCY2,
};

static struct audio_device *adev = NULL;
static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned int audio_device_ref_count;
//cache last MBDRC cal step level
static int last_known_cal_step = -1 ;
static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right);
Arun Mirpuri's avatar
Arun Mirpuri committed
static int out_set_mmap_volume(struct audio_stream_out *stream, float left, float right);
static int out_set_voip_volume(struct audio_stream_out *stream, float left, float right);
static int out_set_pcm_volume(struct audio_stream_out *stream, float left, float right);
#ifdef SOFT_VOLUME
static int out_set_soft_volume_params(struct audio_stream_out *stream);
#endif
static void adev_snd_mon_cb(void *cookie, struct str_parms *parms);
static void in_snd_mon_cb(void * stream, struct str_parms * parms);
static void out_snd_mon_cb(void * stream, struct str_parms * parms);

static int configure_btsco_sample_rate(snd_device_t snd_device);

#ifdef AUDIO_FEATURE_ENABLED_GCOV
extern void  __gcov_flush();
static void enable_gcov()
{
    __gcov_flush();
}
#else
static void enable_gcov()
{
}
#endif

static int in_set_microphone_direction(const struct audio_stream_in *stream,
                                           audio_microphone_direction_t dir);
static int in_set_microphone_field_dimension(const struct audio_stream_in *stream, float zoom);

static bool is_pcm_record_usecase(audio_usecase_t uc_id)
{
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(record_usecases)/sizeof(record_usecases[0]);

    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (uc_id == record_usecases[record_uc_index])
            return true;
    }
    return false;
}

static audio_usecase_t get_record_usecase(struct audio_device *adev)
{
    audio_usecase_t ret_uc = USECASE_INVALID;
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(record_usecases)/sizeof(record_usecases[0]);

    ALOGV("%s: num_usecase: %d", __func__, num_usecase);
    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (!(adev->pcm_record_uc_state & (0x1 << record_uc_index))) {
            adev->pcm_record_uc_state |= 0x1 << record_uc_index;
            ret_uc = record_usecases[record_uc_index];
            break;
        }
    }

    ALOGV("%s: pcm record usecase is %d", __func__, ret_uc);
    return ret_uc;
}

static void free_record_usecase(struct audio_device *adev,
                                audio_usecase_t uc_id)
{
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(record_usecases)/sizeof(record_usecases[0]);

    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (record_usecases[record_uc_index] == uc_id) {
            adev->pcm_record_uc_state &= ~(0x1 << record_uc_index);
            break;
        }
    }
    ALOGV("%s: free pcm record usecase %d", __func__, uc_id);
}

static bool is_pcm_low_latency_record_usecase(audio_usecase_t uc_id)
{
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(low_latency_record_usecases)/sizeof(low_latency_record_usecases[0]);
    ALOGD("%s: Check low latency pcm record usecase", __func__);

    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (uc_id == low_latency_record_usecases[record_uc_index])
            return true;
    }
    return false;
}

static audio_usecase_t get_low_latency_record_usecase(struct audio_device *adev)
{
    audio_usecase_t ret_uc = USECASE_INVALID;
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(low_latency_record_usecases)/sizeof(low_latency_record_usecases[0]);

    ALOGD("%s: get_low_latency_record_usecase: %d", __func__, num_usecase);
    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (!(adev->pcm_low_latency_record_uc_state & (0x1 << record_uc_index))) {
            adev->pcm_low_latency_record_uc_state |= 0x1 << record_uc_index;
            ALOGD("%s: get_low_latency_record_usecase: %d", __func__, record_uc_index);
            ret_uc = low_latency_record_usecases[record_uc_index];
            break;
        }
    }

    ALOGD("%s: low latency pcm record usecase is %d", __func__, ret_uc);
    return ret_uc;
}

static void free_low_latency_record_usecase(struct audio_device *adev,
                                audio_usecase_t uc_id)
{
    unsigned int record_uc_index;
    unsigned int num_usecase = sizeof(low_latency_record_usecases)/sizeof(low_latency_record_usecases[0]);

    for (record_uc_index = 0; record_uc_index < num_usecase; record_uc_index++) {
        if (low_latency_record_usecases[record_uc_index] == uc_id) {
            adev->pcm_low_latency_record_uc_state &= ~(0x1 << record_uc_index);
            break;
        }
    }
    ALOGD("%s: free low latency pcm record usecase %d", __func__, uc_id);
}
static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
                               int flags __unused)
{
    int dir = 0;
    switch (uc_id) {
        case USECASE_AUDIO_RECORD_LOW_LATENCY:
        case USECASE_AUDIO_RECORD_VOIP_LOW_LATENCY:
            dir = 1;
        case USECASE_AUDIO_PLAYBACK_ULL:
            break;
        default:
            return false;
    }

    int dev_id = platform_get_pcm_device_id(uc_id, dir == 0 ?
                                            PCM_PLAYBACK : PCM_CAPTURE);
    if (adev->adm_is_noirq_avail)
        return adev->adm_is_noirq_avail(adev->adm_data,
                                        adev->snd_card, dev_id, dir);
    return false;
}

static void register_out_stream(struct stream_out *out)
{
    struct audio_device *adev = out->dev;
    if (is_offload_usecase(out->usecase) ||
        !adev->adm_register_output_stream)
        return;

    // register stream first for backward compatibility
    adev->adm_register_output_stream(adev->adm_data,
                                     out->handle,
                                     out->flags);

    if (!adev->adm_set_config)
        return;
    if (out->realtime || (out->flags & AUDIO_OUTPUT_FLAG_SYS_NOTIFICATION))
       adev->adm_set_config(adev->adm_data,
                             out->handle,
                             out->pcm, &out->config);
#else
    if (out->realtime)
       adev->adm_set_config(adev->adm_data,
                             out->handle,
                             out->pcm, &out->config);
#endif
}

static void register_in_stream(struct stream_in *in)
{
    struct audio_device *adev = in->dev;
    if (!adev->adm_register_input_stream)
        return;

    adev->adm_register_input_stream(adev->adm_data,
                                    in->capture_handle,
                                    in->flags);

    if (!adev->adm_set_config)
        return;

    if (in->realtime)
        adev->adm_set_config(adev->adm_data,
                             in->capture_handle,
                             in->pcm,
                             &in->config);
}

static void request_out_focus(struct stream_out *out, long ns)
{
    struct audio_device *adev = out->dev;

    if (adev->adm_request_focus_v2)
        adev->adm_request_focus_v2(adev->adm_data, out->handle, ns);
    else if (adev->adm_request_focus)
        adev->adm_request_focus(adev->adm_data, out->handle);
}

static int request_in_focus(struct stream_in *in, long ns)
{
    struct audio_device *adev = in->dev;
    if (adev->adm_request_focus_v2_1)
        ret = adev->adm_request_focus_v2_1(adev->adm_data, in->capture_handle, ns);
    else if (adev->adm_request_focus_v2)
        adev->adm_request_focus_v2(adev->adm_data, in->capture_handle, ns);
    else if (adev->adm_request_focus)
        adev->adm_request_focus(adev->adm_data, in->capture_handle);
}

static void release_out_focus(struct stream_out *out)
{
    struct audio_device *adev = out->dev;

    if (adev->adm_abandon_focus)
        adev->adm_abandon_focus(adev->adm_data, out->handle);
}

static void release_in_focus(struct stream_in *in)
{
    struct audio_device *adev = in->dev;
    if (adev->adm_abandon_focus)
        adev->adm_abandon_focus(adev->adm_data, in->capture_handle);
}

static int parse_snd_card_status(struct str_parms *parms, int *card,
                                 card_status_t *status)
{
    char value[32]={0};
    char state[32]={0};

    int  ret = str_parms_get_str(parms, "SND_CARD_STATUS", value, sizeof(value));
    if (ret < 0)
        return -1;

    // sscanf should be okay as value is of max length 32.
    // same as sizeof state.
    if (sscanf(value, "%d,%s", card, state) < 2)
        return -1;

    *status = !strcmp(state, "ONLINE") ? CARD_STATUS_ONLINE :
                                         CARD_STATUS_OFFLINE;
    return 0;
}

bool is_combo_audio_input_device(struct listnode *devices){

    if ((devices == NULL) || (!list_empty(devices)))
        return false;

    if(compare_device_type(devices, AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_SPEAKER_MIC2))
        return true;
    else
        return false;
}

static inline void adjust_frames_for_device_delay(struct stream_out *out,
                                                  uint32_t *dsp_frames) {
    // Adjustment accounts for A2dp encoder latency with offload usecases
    // Note: Encoder latency is returned in ms.
    if (is_a2dp_out_device_type(&out->device_list)) {
        unsigned long offset =
                (audio_extn_a2dp_get_encoder_latency() * out->sample_rate / 1000);
        *dsp_frames = (*dsp_frames > offset) ? (*dsp_frames - offset) : 0;
    }
}

static inline bool free_entry(void *key  __unused,
                              void *value, void *context __unused)
{
    free(value);
    return true;
}

static inline void free_map(Hashmap *map)
{
    if (map) {
        hashmapForEach(map, free_entry, (void *) NULL);
        hashmapFree(map);
    }
}

static inline void patch_map_remove_l(struct audio_device *adev,
                                audio_patch_handle_t patch_handle)
{
    if (patch_handle == AUDIO_PATCH_HANDLE_NONE)
        return;

    struct audio_patch_info *p_info =
        hashmapGet(adev->patch_map, (void *) (intptr_t) patch_handle);
    if (p_info) {
        ALOGV("%s: Remove patch %d", __func__, patch_handle);
        hashmapRemove(adev->patch_map, (void *) (intptr_t) patch_handle);
        free(p_info->patch);
        free(p_info);
    }
}

static inline int io_streams_map_insert(struct audio_device *adev,
                                    struct audio_stream *stream,
                                    audio_io_handle_t handle,
                                    audio_patch_handle_t patch_handle)
{
    struct audio_stream_info *s_info =
            (struct audio_stream_info *) calloc(1, sizeof(struct audio_stream_info));

    if (s_info == NULL) {
        ALOGE("%s: Could not allocate stream info", __func__);
        return -ENOMEM;
    }
    s_info->stream = stream;
    s_info->patch_handle = patch_handle;

    pthread_mutex_lock(&adev->lock);
    struct audio_stream_info *stream_info =
            hashmapPut(adev->io_streams_map, (void *) (intptr_t) handle, (void *) s_info);
    if (stream_info != NULL)
        free(stream_info);
    pthread_mutex_unlock(&adev->lock);
    ALOGV("%s: Added stream in io_streams_map with handle %d", __func__, handle);
    return 0;
}

static inline void io_streams_map_remove(struct audio_device *adev,
                                     audio_io_handle_t handle)
{
    pthread_mutex_lock(&adev->lock);
    struct audio_stream_info *s_info =
            hashmapRemove(adev->io_streams_map, (void *) (intptr_t) handle);
    if (s_info == NULL)
    ALOGV("%s: Removed stream with handle %d", __func__, handle);
    patch_map_remove_l(adev, s_info->patch_handle);
done:
    pthread_mutex_unlock(&adev->lock);
static struct audio_patch_info* fetch_patch_info_l(struct audio_device *adev,
                                    audio_patch_handle_t handle)
{
    struct audio_patch_info *p_info = NULL;
    p_info = (struct audio_patch_info *)
                 hashmapGet(adev->patch_map, (void *) (intptr_t) handle);
    return p_info;
}

__attribute__ ((visibility ("default")))
bool audio_hw_send_gain_dep_calibration(int level) {
    bool ret_val = false;
    ALOGV("%s: called ...", __func__);

    pthread_mutex_lock(&adev_init_lock);

    if (adev != NULL && adev->platform != NULL) {
        pthread_mutex_lock(&adev->lock);
        ret_val = platform_send_gain_dep_cal(adev->platform, level);
        // cache level info for any of the use case which
        // was not started.
        last_known_cal_step = level;;
        pthread_mutex_unlock(&adev->lock);
    } else {
        ALOGE("%s: %s is NULL", __func__, adev == NULL ? "adev" : "adev->platform");
    }

    pthread_mutex_unlock(&adev_init_lock);