blob: 0c4f5263d80016b41e629e83464d3cde21e6896e [file] [log] [blame]
/*
* 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.
*/
#include <hardware/audio.h>
#include <tinyalsa/asoundlib.h>
#include <audio_route/audio_route.h>
#define ACDB_DEV_TYPE_OUT 1
#define ACDB_DEV_TYPE_IN 2
#define DUALMIC_CONFIG_NONE 0 /* Target does not contain 2 mics */
#define DUALMIC_CONFIG_ENDFIRE 1
#define DUALMIC_CONFIG_BROADSIDE 2
/* Sound devices specific to the platform
* The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
* devices to enable corresponding mixer paths
*/
typedef enum {
SND_DEVICE_NONE = 0,
/* Playback devices */
SND_DEVICE_MIN,
SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN,
SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
SND_DEVICE_OUT_SPEAKER,
SND_DEVICE_OUT_HEADPHONES,
SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
SND_DEVICE_OUT_VOICE_SPEAKER,
SND_DEVICE_OUT_VOICE_HEADPHONES,
SND_DEVICE_OUT_HDMI,
SND_DEVICE_OUT_SPEAKER_AND_HDMI,
SND_DEVICE_OUT_BT_SCO,
SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
SND_DEVICE_OUT_END,
/*
* Note: IN_BEGIN should be same as OUT_END because total number of devices
* SND_DEVICES_MAX should not exceed MAX_RX + MAX_TX devices.
*/
/* Capture devices */
SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END,
SND_DEVICE_IN_HANDSET_MIC = SND_DEVICE_IN_BEGIN,
SND_DEVICE_IN_SPEAKER_MIC,
SND_DEVICE_IN_HEADSET_MIC,
SND_DEVICE_IN_VOICE_SPEAKER_MIC,
SND_DEVICE_IN_VOICE_HEADSET_MIC,
SND_DEVICE_IN_HDMI_MIC,
SND_DEVICE_IN_BT_SCO_MIC,
SND_DEVICE_IN_CAMCORDER_MIC,
SND_DEVICE_IN_VOICE_DMIC_EF,
SND_DEVICE_IN_VOICE_DMIC_BS,
SND_DEVICE_IN_VOICE_DMIC_EF_TMUS,
SND_DEVICE_IN_VOICE_SPEAKER_DMIC_EF,
SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BS,
SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC,
SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC,
SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC,
SND_DEVICE_IN_VOICE_REC_MIC,
SND_DEVICE_IN_VOICE_REC_DMIC_EF,
SND_DEVICE_IN_VOICE_REC_DMIC_BS,
SND_DEVICE_IN_VOICE_REC_DMIC_EF_FLUENCE,
SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE,
SND_DEVICE_IN_END,
SND_DEVICE_MAX = SND_DEVICE_IN_END,
} snd_device_t;
/* These are the supported use cases by the hardware.
* Each usecase is mapped to a specific PCM device.
* Refer to pcm_device_table[].
*/
typedef enum {
USECASE_INVALID = -1,
/* Playback usecases */
USECASE_AUDIO_PLAYBACK_DEEP_BUFFER = 0,
USECASE_AUDIO_PLAYBACK_LOW_LATENCY,
USECASE_AUDIO_PLAYBACK_MULTI_CH,
/* Capture usecases */
USECASE_AUDIO_RECORD,
USECASE_AUDIO_RECORD_LOW_LATENCY,
USECASE_VOICE_CALL,
AUDIO_USECASE_MAX
} audio_usecase_t;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define SOUND_CARD 0
#define DEFAULT_OUTPUT_SAMPLING_RATE 48000
/*
* tinyAlsa library interprets period size as number of frames
* one frame = channel_count * sizeof (pcm sample)
* so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes
* DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes
* We should take care of returning proper size when AudioFlinger queries for
* the buffer size of an input/output stream
*/
#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 1024
#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 8
#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 256
#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
#define HDMI_MULTI_PERIOD_SIZE 336
#define HDMI_MULTI_PERIOD_COUNT 8
#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6
#define HDMI_MULTI_PERIOD_BYTES (HDMI_MULTI_PERIOD_SIZE * HDMI_MULTI_DEFAULT_CHANNEL_COUNT * 2)
#define AUDIO_CAPTURE_PERIOD_SIZE 320
#define AUDIO_CAPTURE_PERIOD_COUNT 2
#define MAX_SUPPORTED_CHANNEL_MASKS 2
struct stream_out {
struct audio_stream_out stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm_config config;
struct pcm *pcm;
int standby;
int pcm_device_id;
audio_channel_mask_t channel_mask;
audio_devices_t devices;
audio_output_flags_t flags;
audio_usecase_t usecase;
/* Array of supported channel mask configurations. +1 so that the last entry is always 0 */
audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];
struct audio_device *dev;
};
struct stream_in {
struct audio_stream_in stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm_config config;
struct pcm *pcm;
int standby;
int source;
int pcm_device_id;
int device;
audio_channel_mask_t channel_mask;
audio_usecase_t usecase;
struct audio_device *dev;
};
typedef enum {
PCM_PLAYBACK,
PCM_CAPTURE,
VOICE_CALL
} usecase_type_t;
union stream_ptr {
struct stream_in *in;
struct stream_out *out;
};
struct audio_usecase {
struct listnode list;
audio_usecase_t id;
usecase_type_t type;
audio_devices_t devices;
union stream_ptr stream;
};
typedef void (*acdb_deallocate_t)();
typedef int (*acdb_init_t)();
typedef void (*acdb_send_audio_cal_t)(int, int);
typedef void (*acdb_send_voice_cal_t)(int, int);
typedef int (*csd_client_init_t)();
typedef int (*csd_client_deinit_t)();
typedef int (*csd_disable_device_t)();
typedef int (*csd_enable_device_t)(int, int, uint32_t);
typedef int (*csd_volume_t)(int);
typedef int (*csd_mic_mute_t)(int);
typedef int (*csd_start_voice_t)();
typedef int (*csd_stop_voice_t)();
struct audio_device {
struct audio_hw_device device;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct mixer *mixer;
audio_mode_t mode;
audio_devices_t out_device;
struct stream_in *active_input;
struct stream_out *primary_output;
int in_call;
float voice_volume;
bool mic_mute;
int tty_mode;
bool bluetooth_nrec;
bool screen_off;
struct pcm *voice_call_rx;
struct pcm *voice_call_tx;
snd_device_t cur_out_snd_device;
snd_device_t cur_in_snd_device;
bool out_snd_device_active;
bool in_snd_device_active;
struct listnode usecase_list;
struct audio_route *audio_route;
int acdb_settings;
bool mic_type_analog;
bool fluence_in_voice_call;
bool fluence_in_voice_rec;
int dualmic_config;
/* Audio calibration related functions */
void *acdb_handle;
acdb_init_t acdb_init;
acdb_deallocate_t acdb_deallocate;
acdb_send_audio_cal_t acdb_send_audio_cal;
acdb_send_voice_cal_t acdb_send_voice_cal;
/* CSD Client related functions for voice call */
void *csd_client;
csd_client_init_t csd_client_init;
csd_client_deinit_t csd_client_deinit;
csd_disable_device_t csd_disable_device;
csd_enable_device_t csd_enable_device;
csd_volume_t csd_volume;
csd_mic_mute_t csd_mic_mute;
csd_start_voice_t csd_start_voice;
csd_stop_voice_t csd_stop_voice;
};
/*
* NOTE: when multiple mutexes have to be acquired, always take the
* stream_in or stream_out mutex first, followed by the audio_device mutex.
*/
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,
.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,
.avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
};
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,
.avail_min = 0,
};
struct pcm_config pcm_config_audio_capture = {
.channels = 2,
.period_size = AUDIO_CAPTURE_PERIOD_SIZE,
.period_count = AUDIO_CAPTURE_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
struct pcm_config pcm_config_voice_call = {
.channels = 1,
.rate = 8000,
.period_size = 160,
.period_count = 2,
.format = PCM_FORMAT_S16_LE,
};