summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Schneider <asn@cryptomilk.org>2017-01-26 17:15:53 +0100
committerAndreas Schneider <asn@cryptomilk.org>2017-01-28 20:31:59 +0100
commit180d803b41bccf3f139893d3c2746ec678caabcb (patch)
tree9faf04e2711e24f8cb62108f351ca7254058b3fa
parent6db4d7241e90b9164dab6f2eaab9856fb67a4fad (diff)
downloadandroid_device_samsung_sltexx-AUDIOHW.tar.gz
android_device_samsung_sltexx-AUDIOHW.tar.xz
android_device_samsung_sltexx-AUDIOHW.zip
WIP audio hw rewriteAUDIOHW
-rw-r--r--hal/audio/audio_hw.c2150
-rw-r--r--hal/audio/audio_hw.h224
-rw-r--r--hal/audio/routing.h398
3 files changed, 1889 insertions, 883 deletions
diff --git a/hal/audio/audio_hw.c b/hal/audio/audio_hw.c
index 513a5d9..37ec4ed 100644
--- a/hal/audio/audio_hw.c
+++ b/hal/audio/audio_hw.c
@@ -34,19 +34,12 @@
#include <cutils/properties.h>
#include <cutils/str_parms.h>
-#include <hardware/audio.h>
-#include <hardware/hardware.h>
-
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_media.h>
#include <system/audio.h>
-#include <tinyalsa/asoundlib.h>
-
-#include <audio_utils/resampler.h>
-#include <audio_route/audio_route.h>
-
+#include "audio_hw.h"
#include "routing.h"
#include "ril_interface.h"
@@ -56,122 +49,173 @@
#define ALOGT(a...) do { } while(0)
#endif
-#define PCM_CARD 0
-#define PCM_CARD_SPDIF 1
-#define PCM_TOTAL 2
-
-#define PCM_DEVICE 0 /* Playback link */
-#define PCM_DEVICE_VOICE 1 /* Baseband link */
-#define PCM_DEVICE_SCO 2 /* Bluetooth link */
-#define PCM_DEVICE_DEEP 3 /* Deep buffer */
-
-#define MIXER_CARD 0
-
/* duration in ms of volume ramp applied when starting capture to remove plop */
#define CAPTURE_START_RAMP_MS 100
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-#define DEVICE_DAPM_PATH "/d/asoc/Pacific WM5110 Sound/dapm"
-#define CODEC_DAPM_PATH "/d/asoc/Pacific WM5110 Sound/florida-codec/dapm"
-
-/*
- * Set the deep-buffer and low-latency output buffer sizes to
- * integral multiple of msec. This reduces the variations in the writes.
- */
-#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960
-#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 5
-
-#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240
-#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
-
-#define AUDIO_CAPTURE_PERIOD_SIZE 320
-#define AUDIO_CAPTURE_PERIOD_COUNT 2
-
-#define AUDIO_CAPTURE_LOW_LATENCY_PERIOD_SIZE 240
-#define AUDIO_CAPTURE_LOW_LATENCY_PERIOD_COUNT 2
-
-#define SCO_CAPTURE_PERIOD_SIZE 240
-#define SCO_CAPTURE_PERIOD_COUNT 2
-
-#define HDMI_MULTI_PERIOD_SIZE 336
-#define HDMI_MULTI_PERIOD_COUNT 8
-#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6 /* 5.1 */
-#define HDMI_MULTI_DEFAULT_SAMPLING_RATE 48000
-/*
- * Default sampling for HDMI multichannel output
- *
- * Maximum number of channel mask configurations supported. Currently the
- * primary output only supports 1 (stereo) and the
- * multi channel HDMI output 2 (5.1 and 7.1)
- */
-#define HDMI_MAX_SUPPORTED_CHANNEL_MASKS 2
-
+#define DAPM_SHUTDOWN_TIME 10000 /* 10 ms */
+
+static struct pcm_device_profile pcm_device_playback = {
+ .config = {
+ .channels = PLAYBACK_DEFAULT_CHANNEL_COUNT,
+ .rate = PLAYBACK_DEFAULT_SAMPLING_RATE,
+ .period_size = PLAYBACK_PERIOD_SIZE,
+ .period_count = PLAYBACK_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = PLAYBACK_START_THRESHOLD(PLAYBACK_PERIOD_SIZE,
+ PLAYBACK_PERIOD_COUNT),
+ .stop_threshold = PLAYBACK_STOP_THRESHOLD(PLAYBACK_PERIOD_SIZE,
+ PLAYBACK_PERIOD_COUNT),
+ .silence_threshold = 0,
+ .silence_size = UINT_MAX,
+ .avail_min = PLAYBACK_AVAILABLE_MIN,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_PLAYBACK,
+ .type = PCM_PLAYBACK,
+ .devices = AUDIO_DEVICE_OUT_WIRED_HEADSET|
+ AUDIO_DEVICE_OUT_WIRED_HEADPHONE|
+ AUDIO_DEVICE_OUT_SPEAKER,
+};
-struct pcm_config pcm_config_fast = {
- .channels = 2,
- .rate = 48000,
- .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
- .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_capture = {
+ .config = {
+ .channels = CAPTURE_DEFAULT_CHANNEL_COUNT,
+ .rate = CAPTURE_DEFAULT_SAMPLING_RATE,
+ .period_size = CAPTURE_PERIOD_SIZE,
+ .period_count = CAPTURE_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = CAPTURE_START_THRESHOLD,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .avail_min = 0,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_CAPTURE,
+ .type = PCM_CAPTURE,
+ .devices = AUDIO_DEVICE_IN_BUILTIN_MIC|
+ AUDIO_DEVICE_IN_WIRED_HEADSET|
+ AUDIO_DEVICE_IN_BACK_MIC,
};
-struct pcm_config pcm_config_deep = {
- .channels = 2,
- .rate = 48000,
- .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,
- .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_capture_low_latency = {
+ .config = {
+ .channels = CAPTURE_DEFAULT_CHANNEL_COUNT,
+ .rate = CAPTURE_DEFAULT_SAMPLING_RATE,
+ .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
+ .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = CAPTURE_START_THRESHOLD,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .avail_min = 0,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_CAPTURE,
+ .type = PCM_CAPTURE_LOW_LATENCY,
+ .devices = AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_BACK_MIC,
};
-struct pcm_config pcm_config_in = {
- .channels = 2,
- .rate = 48000,
- .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
- .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_playback_sco = {
+ .config = {
+ .channels = SCO_DEFAULT_CHANNEL_COUNT,
+ .rate = SCO_DEFAULT_SAMPLING_RATE,
+ .period_size = SCO_PERIOD_SIZE,
+ .period_count = SCO_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = SCO_START_THRESHOLD,
+ .stop_threshold = SCO_STOP_THRESHOLD,
+ .silence_threshold = 0,
+ .avail_min = SCO_AVAILABLE_MIN,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_SCO,
+ .type = PCM_PLAYBACK,
+ .devices = AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT,
};
-struct pcm_config pcm_config_in_low_latency = {
- .channels = 2,
- .rate = 48000,
- .period_size = AUDIO_CAPTURE_LOW_LATENCY_PERIOD_SIZE,
- .period_count = AUDIO_CAPTURE_LOW_LATENCY_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_capture_sco = {
+ .config = {
+ .channels = SCO_DEFAULT_CHANNEL_COUNT,
+ .rate = SCO_DEFAULT_SAMPLING_RATE,
+ .period_size = SCO_PERIOD_SIZE,
+ .period_count = SCO_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = CAPTURE_START_THRESHOLD,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .avail_min = 0,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_SCO,
+ .type = PCM_CAPTURE,
+ .devices = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
};
-struct pcm_config pcm_config_sco = {
- .channels = 1,
- .rate = 8000,
- .period_size = SCO_CAPTURE_PERIOD_SIZE,
- .period_count = SCO_CAPTURE_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_voice = {
+ .config = {
+ .channels = VOICE_DEFAULT_CHANNEL_COUNT,
+ .rate = VOICE_SAMPLING_RATE,
+ .period_size = VOICE_DEFAULT_PERIOD_SIZE,
+ .period_count = VOICE_DEAULT_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = CAPTURE_START_THRESHOLD,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .avail_min = 0,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_VOICE,
+ .type = VOICE_CALL,
+ .devices = AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_BACK_MIC,
};
-struct pcm_config pcm_config_voice = {
- .channels = 2,
- .rate = 8000,
- .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
- .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static struct pcm_device_profile pcm_device_voice_wideband = {
+ .config = {
+ .channels = VOICE_DEFAULT_CHANNEL_COUNT,
+ .rate = VOICE_SAMPLING_RATE_WIDEBAND,
+ .period_size = VOICE_DEFAULT_PERIOD_SIZE,
+ .period_count = VOICE_DEAULT_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = CAPTURE_START_THRESHOLD,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .avail_min = 0,
+ },
+ .card = SOUND_CARD,
+ .id = PCM_DEVICE_VOICE,
+ .type = VOICE_CALL,
+ .devices = AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_BACK_MIC,
};
-struct pcm_config pcm_config_voice_wide = {
- .channels = 2,
- .rate = 16000,
- .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
- .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
+static struct pcm_config pcm_config_deep_buffer = {
+ .channels = DEEP_BUFFER_CHANNEL_COUNT,
+ .rate = DEEP_BUFFER_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_hdmi_multi = {
- .channels = HDMI_MULTI_DEFAULT_CHANNEL_COUNT,
- .rate = HDMI_MULTI_DEFAULT_SAMPLING_RATE,
- .period_size = HDMI_MULTI_PERIOD_SIZE,
- .period_count = HDMI_MULTI_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
+static const char * const use_case_table[AUDIO_USECASE_MAX] = {
+ [USECASE_AUDIO_PLAYBACK] = "playback",
+ [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "playback multi-channel",
+ [USECASE_AUDIO_HFP_SCO] = "hfp-sco",
+ [USECASE_AUDIO_CAPTURE] = "capture",
+ [USECASE_AUDIO_CAPTURE_LOW_LATENCY] = "capture low-latency",
+ [USECASE_VOICE_CALL] = "voice-call",
};
+#if 0
enum output_type {
OUTPUT_DEEP_BUF, // deep PCM buffers output stream
OUTPUT_LOW_LATENCY, // low latency output stream
@@ -284,6 +328,7 @@ struct stream_in {
struct audio_device *dev;
};
+#endif
#define STRING_TO_ENUM(string) { #string, string }
@@ -298,609 +343,1499 @@ const struct string_to_enum out_channels_name_to_enum_table[] = {
STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
};
-static int get_output_device_id(audio_devices_t device)
+struct timespec time_spec_diff(struct timespec time1, struct timespec time0) {
+ struct timespec ret;
+ int xsec = 0;
+ int sign = 1;
+
+ if (time0.tv_nsec > time1.tv_nsec) {
+ xsec = (int) ((time0.tv_nsec - time1.tv_nsec) / (1E9 + 1));
+ time0.tv_nsec -= (long int) (1E9 * xsec);
+ time0.tv_sec += xsec;
+ }
+
+ if ((time1.tv_nsec - time0.tv_nsec) > 1E9) {
+ xsec = (int) ((time1.tv_nsec - time0.tv_nsec) / 1E9);
+ time0.tv_nsec += (long int) (1E9 * xsec);
+ time0.tv_sec -= xsec;
+ }
+
+ ret.tv_sec = time1.tv_sec - time0.tv_sec;
+ ret.tv_nsec = time1.tv_nsec - time0.tv_nsec;
+
+ if (time1.tv_sec < time0.tv_sec) {
+ sign = -1;
+ }
+
+ ret.tv_sec = ret.tv_sec * sign;
+
+ return ret;
+}
+
+static const char *get_snd_device_name(snd_device_t snd_device)
{
- if (device == AUDIO_DEVICE_NONE)
- return OUT_DEVICE_NONE;
+ const char *name = NULL;
- if (popcount(device) == 2) {
- if ((device == (AUDIO_DEVICE_OUT_SPEAKER |
- AUDIO_DEVICE_OUT_WIRED_HEADSET)) ||
- (device == (AUDIO_DEVICE_OUT_SPEAKER |
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE))) {
- return OUT_DEVICE_SPEAKER_AND_HEADSET;
- } else if (device == (AUDIO_DEVICE_OUT_SPEAKER |
- AUDIO_DEVICE_OUT_EARPIECE)) {
- return OUT_DEVICE_SPEAKER_AND_EARPIECE;
- } else {
- return OUT_DEVICE_NONE;
+ if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
+ name = device_table[snd_device];
+ }
+
+ ALOGE_IF(name == NULL, "%s: invalid snd device %d", __func__, snd_device);
+
+ return name;
+}
+
+static const char *get_snd_device_display_name(snd_device_t snd_device)
+{
+ const char *name = get_snd_device_name(snd_device);
+
+ if (name == NULL) {
+ name = "SND DEVICE NOT FOUND";
+ }
+
+ return name;
+}
+
+static struct pcm_device_profile *get_pcm_device(usecase_type_t uc_type,
+ audio_devices_t devices)
+{
+ int i;
+
+ devices &= ~AUDIO_DEVICE_BIT_IN;
+
+ for (i = 0; pcm_devices[i] != NULL; i++) {
+ if ((pcm_devices[i]->type == uc_type) &&
+ (devices & pcm_devices[i]->devices)) {
+ return pcm_devices[i];
}
}
- if (popcount(device) != 1)
- return OUT_DEVICE_NONE;
+ return NULL;
+}
- switch (device) {
- case AUDIO_DEVICE_OUT_SPEAKER:
- return OUT_DEVICE_SPEAKER;
- case AUDIO_DEVICE_OUT_EARPIECE:
- return OUT_DEVICE_EARPIECE;
- case AUDIO_DEVICE_OUT_WIRED_HEADSET:
- return OUT_DEVICE_HEADSET;
- case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
- return OUT_DEVICE_HEADPHONES;
- case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
- return OUT_DEVICE_BT_SCO;
- case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
- return OUT_DEVICE_BT_SCO_HEADSET_OUT;
- case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
- return OUT_DEVICE_BT_SCO_CARKIT;
- default:
- return OUT_DEVICE_NONE;
+static struct audio_usecase *get_usecase_from_id(struct audio_device *adev,
+ audio_usecase_t uc_id)
+{
+ struct audio_usecase *usecase;
+ struct listnode *node;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, adev_list_node);
+ if (usecase->id == uc_id) {
+ return usecase;
+ }
}
+
+ return NULL;
}
-static int get_input_source_id(audio_source_t source, bool wb_amr)
+static struct audio_usecase *get_usecase_from_type(struct audio_device *adev,
+ usecase_type_t type)
{
- switch (source) {
- case AUDIO_SOURCE_DEFAULT:
- return IN_SOURCE_NONE;
- case AUDIO_SOURCE_MIC:
- return IN_SOURCE_MIC;
- case AUDIO_SOURCE_CAMCORDER:
- return IN_SOURCE_CAMCORDER;
- case AUDIO_SOURCE_VOICE_RECOGNITION:
- return IN_SOURCE_VOICE_RECOGNITION;
- case AUDIO_SOURCE_VOICE_COMMUNICATION:
- return IN_SOURCE_VOICE_COMMUNICATION;
- case AUDIO_SOURCE_VOICE_CALL:
- if (wb_amr) {
- return IN_SOURCE_VOICE_CALL_WB;
+ struct audio_usecase *usecase;
+ struct listnode *node;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, adev_list_node);
+ if (usecase->type & type) {
+ return usecase;
}
- return IN_SOURCE_VOICE_CALL;
- default:
- return IN_SOURCE_NONE;
}
+
+ return NULL;
}
-static void do_out_standby(struct stream_out *out);
-static void adev_set_call_audio_path(struct audio_device *adev);
-static int voice_set_volume(struct audio_hw_device *dev, float volume);
-static void start_ril_call(struct audio_device *adev);
+/* always called with adev lock held */
+static int set_voice_volume_l(struct audio_device *adev, float volume)
+{
+ int err = 0;
-static void lock_input_stream(struct stream_in *in);
-static void unlock_input_stream(struct stream_in *in);
-static void lock_output_stream(struct stream_out *out);
-static void unlock_output_stream(struct stream_out *out);
+ adev->voice.volume = volume;
-/**
- * NOTE: when multiple mutexes have to be acquired, always respect the
- * following order: hw device > in stream > out stream
- */
+ if (adev->mode == AUDIO_MODE_IN_CALL) {
+ enum _SoundType sound_type;
-/* Helper functions */
+ switch (adev->out_device) {
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ sound_type = SOUND_TYPE_VOICE;
+ break;
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ sound_type = SOUND_TYPE_SPEAKER;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+ sound_type = SOUND_TYPE_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ case AUDIO_DEVICE_OUT_ALL_SCO:
+ sound_type = SOUND_TYPE_BTVOICE;
+ break;
+ default:
+ sound_type = SOUND_TYPE_VOICE;
+ }
+
+ ril_set_call_volume(&adev->ril, sound_type, volume);
+ }
+
+ return err;
+}
-static int open_hdmi_driver(struct audio_device *adev)
+static snd_device_t get_output_snd_device(struct audio_device *adev,
+ audio_devices_t devices)
{
- if (adev->hdmi_drv_fd < 0) {
- adev->hdmi_drv_fd = open("/dev/video16", O_RDWR);
- if (adev->hdmi_drv_fd < 0)
- ALOGE("%s cannot open video16 - error: %s\n",
- __func__, strerror(errno));
+
+ snd_device_t snd_device = SND_DEVICE_NONE;
+ audio_mode_t mode = adev->mode;
+ bool amr_wb = adev->voice.wb_amr;
+
+ ALOGV("%s: enter: output devices(%#x), mode(%d)", __func__, devices, mode);
+
+ if (devices == AUDIO_DEVICE_NONE ||
+ devices & AUDIO_DEVICE_BIT_IN) {
+ ALOGV("%s: Invalid output devices (%#x)", __func__, devices);
+ goto exit;
+ }
+
+ if (mode == AUDIO_MODE_IN_CALL) {
+ if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+ devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+ if (wb_amr) {
+ snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES_WB;
+ } else {
+ snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
+ }
+ } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ if (wb_amr) {
+ snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_WB;
+ } else {
+ snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
+ }
+ } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
+ if (wb_amr) {
+ snd_device = SND_DEVICE_OUT_VOICE_EARPIECE_WB;
+ } else {
+ snd_device = SND_DEVICE_OUT_VOICE_EARPIECE;
+ }
+ } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ snd_device = SND_DEVICE_OUT_BT_SCO;
+ }
+
+ if (snd_device != SND_DEVICE_NONE) {
+ goto exit;
+ }
+ }
+
+ if (popcount(devices) == 2) {
+ if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+ } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+ } else {
+ ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
+ goto exit;
+ }
+
+ if (snd_device != SND_DEVICE_NONE) {
+ goto exit;
+ }
+ }
+
+ if (popcount(devices) != 1) {
+ ALOGE("%s: Invalid output devices(%#x)", __func__, devices);
+ goto exit;
}
- return adev->hdmi_drv_fd;
+
+ if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+ devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+ snd_device = SND_DEVICE_OUT_HEADPHONES;
+ } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ snd_device = SND_DEVICE_OUT_SPEAKER;
+ } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
+ snd_device = SND_DEVICE_OUT_HANDSET;
+ } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ snd_device = SND_DEVICE_OUT_BT_SCO;
+ } else {
+ ALOGE("%s: Unknown device(s) %#x", __func__, devices);
+ }
+
+exit:
+ ALOGV("%s: exit: snd_device(%s)", __func__, device_table[snd_device]);
+
+ return snd_device;
}
-/* must be called with hw device mutex locked */
-static int enable_hdmi_audio(struct audio_device *adev, int enable)
+static snd_device_t get_input_snd_device(struct audio_device *adev,
+ audio_devices_t out_device)
{
- int ret;
- struct v4l2_control ctrl;
+ audio_source_t source;
+ audio_mode_t mode = adev->mode;
+ audio_devices_t in_device;
+ audio_channel_mask_t channel_mask;
+ snd_device_t snd_device = SND_DEVICE_NONE;
+ struct stream_in *active_input = NULL;
+ struct audio_usecase *usecase;
- ret = open_hdmi_driver(adev);
- if (ret < 0) {
- return ret;
+ usecase = get_usecase_from_type(adev, PCM_CAPTURE | VOICE_CALL);
+ if (usecase != NULL) {
+ active_input = (struct stream_in *)usecase->stream;
}
+ source = (active_input == NULL) ?
+ AUDIO_SOURCE_DEFAULT :
+ active_input->source;
+
+ in_device = ((active_input == NULL) ?
+ AUDIO_DEVICE_NONE :
+ active_input->devices) & ~AUDIO_DEVICE_BIT_IN;
+
+ channel_mask = (active_input == NULL) ?
+ AUDIO_CHANNEL_IN_MONO :
+ active_input->main_channels;
- ctrl.id = V4L2_CID_TV_ENABLE_HDMI_AUDIO;
- ctrl.value = !!enable;
- ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl);
+ ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
+ __func__,
+ out_device,
+ in_device);
+
+ if (mode == AUDIO_MODE_IN_CALL) {
+ if (out_device == AUDIO_DEVICE_NONE) {
+ ALOGE("%s: No output device set for voice call", __func__);
+ goto exit;
+ }
+
+ if (out_device & AUDIO_DEVICE_OUT_EARPIECE ||
+ out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+ snd_device = SND_DEVICE_IN_EARPIECE_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+ snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
+ snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
+ }
+ } else if (source == AUDIO_SOURCE_CAMCORDER) {
+ if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
+ in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+ snd_device = SND_DEVICE_IN_CAMCORDER_MIC;
+ }
+ } else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
+ source == AUDIO_SOURCE_MIC) {
+ if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
+ in_device = AUDIO_DEVICE_IN_BACK_MIC;
+ }
+#if 0
+ if (active_input) {
+ if (active_input->enable_aec) {
+ if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC;
+ } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
+ if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC;
+ } else {
+ snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC;
+ }
+ } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
+ snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC;
+ }
+ }
+ /* TODO: set echo reference */
+ }
+#endif
+ } else if (source == AUDIO_SOURCE_DEFAULT) {
+ goto exit;
+ }
- if (ret < 0) {
- ALOGE("V4L2_CID_TV_ENABLE_HDMI_AUDIO ioctl error (%d)", errno);
+ if (snd_device != SND_DEVICE_NONE) {
+ goto exit;
}
- return ret;
+ if (in_device != AUDIO_DEVICE_NONE &&
+ !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) &&
+ !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
+ if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
+ snd_device = SND_DEVICE_IN_EARPIECE_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
+ snd_device = SND_DEVICE_IN_HEADSET_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
+ } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
+ snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else {
+ ALOGE("%s: Unknown input device(s) %#x", __func__, in_device);
+ ALOGW("%s: Using default handset-mic", __func__);
+ snd_device = SND_DEVICE_IN_HANDSET_MIC;
+ }
+ } else {
+ if (out_device & AUDIO_DEVICE_OUT_EARPIECE) {
+ snd_device = SND_DEVICE_IN_EARPIECE_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+ snd_device = SND_DEVICE_IN_HEADSET_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+ snd_device = SND_DEVICE_IN_HANDSET_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+ } else {
+ ALOGE("%s: Unknown output device(s) %#x", __func__, out_device);
+ ALOGW("%s: Using default handset-mic", __func__);
+ snd_device = SND_DEVICE_IN_HANDSET_MIC;
+ }
+ }
+
+exit:
+ ALOGV("%s: exit: in_snd_device(%s)", __func__, device_table[snd_device]);
+
+ return snd_device;
}
-/* must be called with hw device mutex locked */
-static int read_hdmi_channel_masks(struct audio_device *adev, struct stream_out *out) {
- int ret;
- struct v4l2_control ctrl;
+static int enable_snd_device(struct audio_device *adev,
+ struct audio_usecase *uc_info,
+ snd_device_t snd_device)
+{
+ const char *snd_device_name = get_snd_device_name(snd_device);
+ struct timespec activation_time;
+ struct timespec elapsed_time;
- ret = open_hdmi_driver(adev);
- if (ret < 0)
- return ret;
+ if (snd_device_name == NULL) {
+ return -EINVAL;
+ }
- ctrl.id = V4L2_CID_TV_MAX_AUDIO_CHANNELS;
- ret = ioctl(adev->hdmi_drv_fd, VIDIOC_G_CTRL, &ctrl);
- if (ret < 0) {
- ALOGE("V4L2_CID_TV_MAX_AUDIO_CHANNELS ioctl error (%d)", errno);
- return ret;
+ if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES) {
+ ALOGV("Request to enable combo device: enable individual devices\n");
+
+ enable_snd_device(adev, uc_info, SND_DEVICE_OUT_SPEAKER);
+ enable_snd_device(adev, uc_info, SND_DEVICE_OUT_HEADPHONES);
+
+ return 0;
}
- ALOGV("%s ioctl %d got %d max channels", __func__, ret, ctrl.value);
+ adev->snd_dev_ref_cnt[snd_device]++;
+ if (adev->snd_dev_ref_cnt[snd_device] > 1) {
+ ALOGV("%s: snd_device(%d: %s) is already active",
+ __func__,
+ snd_device,
+ snd_device_name);
- if (ctrl.value != 6 && ctrl.value != 8)
- return -ENOSYS;
+ return 0;
+ }
- out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
- if (ctrl.value == 8)
- out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1;
+ ALOGV("%s: snd_device(%d: %s)",
+ __func__,
+ snd_device,
+ snd_device_name);
- return ret;
+ clock_gettime(CLOCK_MONOTONIC, &activation_time);
+
+ elapsed_time = time_spec_diff(adev->mixer.shutdown_time,
+ activation_time);
+ if (elapsed_time.tv_sec == 0) {
+ long elapsed_usec = elapsed_time.tv_nsec / 1000;
+
+ if (elapsed_usec < DAPM_STUTDOWN_TIME) {
+ usleep(DAPM_STUTDOWN_TIME - elapsed_usec);
+ }
+ }
+
+ audio_route_apply_and_update_path(adev->mixer.audio_route,
+ snd_device_name);
+
+ return 0;
}
-/* must be called with hw device mutex locked */
-static int set_hdmi_channels(struct audio_device *adev, int channels) {
- int ret;
- struct v4l2_control ctrl;
+static int disable_snd_device(struct audio_device *adev,
+ struct audio_usecase *uc_info,
+ snd_device_t snd_device)
+{
+ const char *snd_device_name = get_snd_device_name(snd_device);
+ struct mixer_card *mixer_card;
- ret = open_hdmi_driver(adev);
- if (ret < 0)
- return ret;
+ if (snd_device_name == NULL) {
+ return -EINVAL;
+ }
- ctrl.id = V4L2_CID_TV_SET_NUM_CHANNELS;
- ctrl.value = channels;
- ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl);
- if (ret < 0)
- ALOGE("V4L2_CID_TV_SET_NUM_CHANNELS ioctl error (%d)", errno);
+ if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES) {
+ ALOGV("Request to disable combo device: disable individual devices\n");
- return ret;
+ disable_snd_device(adev, uc_info, SND_DEVICE_OUT_SPEAKER);
+ disable_snd_device(adev, uc_info, SND_DEVICE_OUT_HEADPHONES);
+
+ return 0;
+ }
+
+ if (adev->snd_dev_ref_cnt[snd_device] <= 0) {
+ ALOGE("%s: device ref cnt is already 0", __func__);
+
+ return -EINVAL;
+ }
+
+ adev->snd_dev_ref_cnt[snd_device]--;
+ if (adev->snd_dev_ref_cnt[snd_device] == 0) {
+ ALOGV("%s: snd_device(%d: %s)", __func__,
+ snd_device,
+ snd_device_name);
+
+ audio_route_reset_and_update_path(adev->mixer.audio_route,
+ snd_device_name);
+
+ /* Store the shutdown time */
+ clock_gettime(CLOCK_MONOTONIC, &adev->mixer.last_shutdown);
+ }
+
+ return 0;
}
-static const char *audio_device_to_dapm(int out_device_id)
+/* TODO */
+static void start_ril_call(struct audio_device *adev)
{
- switch (out_device_id) {
- case OUT_DEVICE_SPEAKER_AND_EARPIECE:
- case OUT_DEVICE_SPEAKER_AND_HEADSET:
- case OUT_DEVICE_EARPIECE:
- return "RCV";
- case OUT_DEVICE_SPEAKER:
- return "SPK";
- case OUT_DEVICE_HEADSET:
- case OUT_DEVICE_HEADPHONES:
- return "HP";
+ switch (adev->out_device) {
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ adev->two_mic_control = true;
+ break;
+ default:
+ adev->two_mic_control = false;
+ break;
}
- return NULL;
+ if (adev->two_mic_control) {
+ ALOGV("%s: enabling two mic control", __func__);
+ ril_set_two_mic_control(&adev->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
+ } else {
+ ALOGV("%s: disabling two mic control", __func__);
+ ril_set_two_mic_control(&adev->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
+ }
+
+ adev_set_call_audio_path(adev);
+ voice_set_volume(&adev->hw_device, adev->voice_volume);
+
+ ril_set_call_clock_sync(&adev->ril, SOUND_CLOCK_START);
}
-static void output_device_off(int out_device_id)
+static int select_devices(struct audio_device *adev,
+ audio_usecase_t uc_id)
{
- char *state = "Off";
- const char *device;
- char dapm[64] = {0};
- bool ok = false;
- int i;
+ snd_device_t out_snd_device = SND_DEVICE_NONE;
+ snd_device_t in_snd_device = SND_DEVICE_NONE;
+ struct audio_usecase *usecase = NULL;
+ struct audio_usecase *vc_usecase = NULL;
+ struct stream_in *active_input = NULL;
+ struct stream_out *active_out;
+
+ ALOGV("%s: usecase(%d)", __func__, uc_id);
- device = audio_device_to_dapm(out_device_id);
- if (device == NULL) {
- goto out;
+ if (uc_id == USECASE_AUDIO_CAPTURE_HOTWORD) {
+ return 0;
}
- snprintf(dapm, sizeof(dapm), "%s/%s", DEVICE_DAPM_PATH, device);
- ALOGV("%s: Check if %s is turned off\n", __func__, device);
+ usecase = get_usecase_from_type(adev, PCM_CAPTURE|VOICE_CALL);
+ if (usecase != NULL) {
+ active_input = (struct stream_in *)usecase->stream;
+ }
- for (i = 0; i < 20; i++) {
- const char *p;
- char line[32] = {0};
- FILE *fp;
+ usecase = get_usecase_from_id(adev, uc_id);
+ if (usecase == NULL) {
+ ALOGE("%s: Could not find the usecase(%d)", __func__, uc_id);
+ return -EINVAL;
+ }
+ active_out = (struct stream_out *)usecase->stream;
- fp = fopen(dapm, "r");
- if (fp == NULL) {
- ALOGE("%s: Failed to open %s\n", __func__, dapm);
- break;
+ if (usecase->type == VOICE_CALL) {
+ out_snd_device = get_output_snd_device(adev, active_out->devices);
+ in_snd_device = get_input_snd_device(adev, active_out->devices);
+ usecase->devices = active_out->devices;
+ } else {
+ /*
+ * If the voice call is active, use the sound devices of voice call
+ * usecase so that it would not result any device switch. All the
+ * usecases will be switched to new device when select_devices() is
+ * called for voice call usecase.
+ */
+ if (adev->voice.in_call) {
+ vc_usecase = get_usecase_from_id(adev, USECASE_VOICE_CALL);
+ if (usecase == NULL) {
+ ALOGE("%s: Could not find the voice call usecase", __func__);
+ } else {
+ in_snd_device = vc_usecase->in_snd_device;
+ out_snd_device = vc_usecase->out_snd_device;
+ }
}
- p = fgets(line, sizeof(line), fp);
- fclose(fp);
- if (p == NULL) {
- break;
+ if (usecase->type == PCM_PLAYBACK) {
+ usecase->devices = active_out->devices;
+ in_snd_device = SND_DEVICE_NONE;
+ if (out_snd_device == SND_DEVICE_NONE) {
+ out_snd_device = get_output_snd_device(adev,
+ active_out->devices);
+ if (active_out == adev->primary_output &&
+ active_input &&
+ active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+ select_devices(adev, active_input->usecase);
+ }
+ }
+ } else if (usecase->type == PCM_CAPTURE) {
+ usecase->devices = ((struct stream_in *)usecase->stream)->devices;
+ out_snd_device = SND_DEVICE_NONE;
+ if (in_snd_device == SND_DEVICE_NONE) {
+ if (active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
+ adev->primary_output && !adev->primary_output->standby) {
+ in_snd_device = get_input_snd_device(adev,
+ adev->primary_output->devices);
+ } else {
+ in_snd_device = get_input_snd_device(adev,
+ AUDIO_DEVICE_NONE);
+ }
+ }
}
+ }
- p = strstr(line, state);
- if (p != NULL) {
- ok = true;
- break;
- }
- usleep(5);
+ if (out_snd_device == usecase->out_snd_device &&
+ in_snd_device == usecase->in_snd_device) {
+ return 0;
}
-out:
- if (ok) {
- ALOGV("%s: Output device %s turned off!\n", __func__, device);
- } else {
- ALOGE("%s: Failed to wait for %s to turn off", __func__, device);
- usleep(50);
+
+ ALOGV("%s: out_snd_device(%d: %s) in_snd_device(%d: %s)",
+ __func__,
+ out_snd_device,
+ get_snd_device_display_name(out_snd_device),
+ in_snd_device,
+ get_snd_device_display_name(in_snd_device));
+
+
+ /* Disable current sound devices */
+ if (usecase->out_snd_device != SND_DEVICE_NONE) {
+ disable_snd_device(adev, usecase, usecase->out_snd_device);
+ }
+
+ if (usecase->in_snd_device != SND_DEVICE_NONE) {
+ disable_snd_device(adev, usecase, usecase->in_snd_device);
+ }
+
+ /*
+ * Already tell the modem that we are in a call. This should make it
+ * faster to accept an incoming call.
+ */
+ if (adev->voice.in_call) {
+ start_ril_call(adev);
+ }
+
+ /* Enable new sound devices */
+ if (out_snd_device != SND_DEVICE_NONE) {
+ enable_snd_device(adev, usecase, out_snd_device);
}
+
+ if (in_snd_device != SND_DEVICE_NONE) {
+ enable_snd_device(adev, usecase, in_snd_device);
+ }
+
+ usecase->in_snd_device = in_snd_device;
+ usecase->out_snd_device = out_snd_device;
+
+ return 0;
}
-const char *audio_inputs[] = {
- "Headset Mic",
- "Main Mic",
- "Sub Mic",
- "Third Mic"
-};
+static int get_playback_delay(struct stream_out *out,
+ size_t frames,
+ struct echo_reference_buffer *buffer)
+{
+ struct pcm_device *pcm_device;
+ unsigned int kernel_frames;
+ int status;
+ int primary_pcm = 0;
+
+ pcm_device = node_to_item(list_head(&out->pcm_dev_list),
+ struct pcm_device, stream_list_node);
+
+ status = pcm_get_htimestamp(pcm_device->pcm,
+ &kernel_frames,
+ &buffer->time_stamp);
+ if (status < 0) {
+ buffer->time_stamp.tv_sec = 0;
+ buffer->time_stamp.tv_nsec = 0;
+ buffer->delay_ns = 0;
+ ALOGV("get_playback_delay(): pcm_get_htimestamp error,"
+ "setting playbackTimestamp to 0");
+ return status;
+ }
+
+ kernel_frames = pcm_get_buffer_size(pcm_device->pcm) - kernel_frames;
-static void input_devices_off(void)
+ /* adjust render time stamp with delay added by current driver buffer.
+ * Add the duration of current frame as we want the render time of the last
+ * sample being written. */
+ buffer->delay_ns = (long)(((int64_t)(kernel_frames + frames) * 1000000000) /
+ out->config.rate);
+
+ ALOGT("get_playback_delay_time_stamp: secs: [%10ld], nsecs: [%9ld], "
+ "kernel_frames: [%5u], delay_ns: [%d],",
+ buffer->time_stamp.tv_sec,
+ buffer->time_stamp.tv_nsec,
+ kernel_frames,
+ buffer->delay_ns);
+
+ return 0;
+}
+
+/* This function reads PCM data and:
+ * - resample if needed
+ * - process if pre-processors are attached
+ * - discard unwanted channels
+ */
+static ssize_t read_and_process_frames(struct stream_in *in, void* buffer, ssize_t frames)
{
- char *state = "Off";
- bool ok = false;
- size_t i;
+ ssize_t frames_wr = 0;
+ audio_buffer_t in_buf;
+ audio_buffer_t out_buf;
+ size_t src_channels = in->config.channels;
+ size_t dst_channels = audio_channel_count_from_in_mask(in->main_channels);
+ int i;
+ void *proc_buf_out;
+ struct pcm_device *pcm_device;
+ bool has_additional_channels = (dst_channels != src_channels) ? true : false;
+
+ /* Additional channels might be added on top of main_channels:
+ * - aux_channels (by processing effects)
+ * - extra channels due to HW limitations
+ * In case of additional channels, we cannot work inplace
+ */
+ if (has_additional_channels) {
+ proc_buf_out = in->proc_buf_out;
+ } else {
+ proc_buf_out = buffer;
+ }
- for (i = 0; i < ARRAY_SIZE(audio_inputs); i++) {
- char dapm[64] = {0};
- int j;
+ if (list_empty(&in->pcm_dev_list)) {
+ ALOGE("%s: pcm device list empty", __func__);
+ return -EINVAL;
+ }
- snprintf(dapm, sizeof(dapm), "%s/%s", DEVICE_DAPM_PATH, audio_inputs[i]);
+ pcm_device = node_to_item(list_head(&in->pcm_dev_list),
+ struct pcm_device,
+ stream_list_node);
+
+ /* No processing effects attached */
+ if (has_additional_channels) {
+ /* With additional channels, we cannot use original buffer */
+ if (in->proc_buf_size < (size_t)frames) {
+ size_t size_in_bytes = pcm_frames_to_bytes(pcm_device->pcm, frames);
+ in->proc_buf_size = (size_t)frames;
+ in->proc_buf_out = (int16_t *)realloc(in->proc_buf_out,
+ size_in_bytes);
+ ALOG_ASSERT((in->proc_buf_out != NULL),
+ "process_frames() failed to reallocate proc_buf_out");
+ proc_buf_out = in->proc_buf_out;
+ }
+ }
+ frames_wr = read_frames(in, proc_buf_out, frames);
- for (j = 0; j < 20; j++) {
- const char *p;
- char line[32] = {0};
- FILE *fp;
+ /* Remove all additional channels that have been added on top of main_channels:
+ * - aux_channels
+ * - extra channels from HW due to HW limitations
+ * Assumption is made that the channels are interleaved and that the main
+ * channels are first. */
- fp = fopen(dapm, "r");
- if (fp == NULL) {
- ALOGE("%s: Failed to open %s\n", __func__, dapm);
- break;
- }
+ if (has_additional_channels) {
+ int16_t *src_buffer = (int16_t *)proc_buf_out;
+ int16_t *dst_buffer = (int16_t *)buffer;
- p = fgets(line, sizeof(line), fp);
- fclose(fp);
- if (p == NULL) {
- break;
+ if (dst_channels == 1) {
+ for (i = frames_wr; i > 0; i--) {
+ *dst_buffer++ = *src_buffer;
+ src_buffer += src_channels;
}
-
- p = strstr(line, state);
- if (p != NULL) {
- ok = true;
- break;
+ } else {
+ for (i = frames_wr; i > 0; i--) {
+ memcpy(dst_buffer, src_buffer, dst_channels*sizeof(int16_t));
+ dst_buffer += dst_channels;
+ src_buffer += src_channels;
}
- usleep(5);
}
}
-out:
- if (ok) {
- ALOGV("%s: Input devices turned off!\n", __func__);
- } else {
- ALOGE("%s: Failed to wait for device to turn off", __func__);
- usleep(50);
+
+ return frames_wr;
+}
+
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer *buffer)
+{
+ struct stream_in *in;
+ struct pcm_device *pcm_device;
+
+ if (buffer_provider == NULL || buffer == NULL) {
+ return -EINVAL;
+ }
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ if (list_empty(&in->pcm_dev_list)) {
+ buffer->raw = NULL;
+ buffer->frame_count = 0;
+ in->read_status = -ENODEV;
+ return -ENODEV;
}
+
+ pcm_device = node_to_item(list_head(&in->pcm_dev_list),
+ struct pcm_device,
+ stream_list_node);
+
+ if (in->read_buf_frames == 0) {
+ size_t size_in_bytes = pcm_frames_to_bytes(pcm_device->pcm,
+ in->config.period_size);
+ if (in->read_buf_size < in->config.period_size) {
+ in->read_buf_size = in->config.period_size;
+ in->read_buf = (int16_t *)realloc(in->read_buf, size_in_bytes);
+ ALOG_ASSERT((in->read_buf != NULL),
+ "get_next_buffer() failed to reallocate read_buf");
+ }
+
+ in->read_status = pcm_read(pcm_device->pcm,
+ (void*)in->read_buf,
+ size_in_bytes);
+
+ if (in->read_status != 0) {
+ ALOGE("get_next_buffer() pcm_read error %d", in->read_status);
+ buffer->raw = NULL;
+ buffer->frame_count = 0;
+ return in->read_status;
+ }
+ in->read_buf_frames = in->config.period_size;
+ }
+
+ buffer->frame_count = (buffer->frame_count > in->read_buf_frames) ?
+ in->read_buf_frames :
+ buffer->frame_count;
+ buffer->i16 =
+ in->read_buf +
+ (in->config.period_size - in->read_buf_frames) * in->config.channels;
+
+ return in->read_status;
}
-const char *audio_codecs[] = {
- "DSP1",
- "DSP2",
- "DSP3",
- "DSP4"
-};
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer)
+{
+ struct stream_in *in;
-static void codecs_off(void)
+ if (buffer_provider == NULL || buffer == NULL)
+ return;
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ in->read_buf_frames -= buffer->frame_count;
+}
+
+/*
+ * read_frames() reads frames from kernel driver, down samples to capture rate
+ * if necessary and output the number of frames requested to the buffer
+ * specified
+ */
+static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames)
{
- char *state = "Off";
- bool ok = false;
- size_t i;
+ ssize_t frames_wr = 0;
- for (i = 0; i < ARRAY_SIZE(audio_codecs); i++) {
- char dapm[64] = {0};
- int j;
+ struct pcm_device *pcm_device;
- snprintf(dapm, sizeof(dapm), "%s/%s", CODEC_DAPM_PATH, audio_codecs[i]);
+ if (list_empty(&in->pcm_dev_list)) {
+ ALOGE("%s: pcm device list empty", __func__);
+ return -EINVAL;
+ }
- for (j = 0; j < 20; j++) {
- const char *p;
- char line[32] = {0};
- FILE *fp;
+ pcm_device = node_to_item(list_head(&in->pcm_dev_list),
+ struct pcm_device,
+ stream_list_node);
- fp = fopen(dapm, "r");
- if (fp == NULL) {
- ALOGE("%s: Failed to open %s - %s\n",
- __func__,
- dapm,
- strerror(errno));
- break;
- }
+ while (frames_wr < frames) {
+ size_t frames_rd = frames - frames_wr;
- p = fgets(line, sizeof(line), fp);
- fclose(fp);
- if (p == NULL) {
- break;
- }
+ ALOGT("%s: frames_rd: %zd, frames_wr: %zd, in->config.channels: %d",
+ __func__,
+ frames_rd,
+ frames_wr,
+ in->config.channels);
- p = strstr(line, state);
- if (p != NULL) {
- ok = true;
- break;
+ if (in->resampler != NULL) {
+ in->resampler->resample_from_provider(in->resampler,
+ (int16_t *)((char *)buffer + pcm_frames_to_bytes(pcm_device->pcm, frames_wr)),
+ &frames_rd);
+ } else {
+ struct resampler_buffer buf = {
+ .raw = NULL,
+ .frame_count = frames_rd,
+ };
+ get_next_buffer(&in->buf_provider, &buf);
+ if (buf.raw != NULL) {
+ memcpy((char *)buffer +
+ pcm_frames_to_bytes(pcm_device->pcm, frames_wr),
+ buf.raw,
+ pcm_frames_to_bytes(pcm_device->pcm, buf.frame_count));
+ frames_rd = buf.frame_count;
}
- usleep(5);
+ release_buffer(&in->buf_provider, &buf);
}
+ /* in->read_status is updated by getNextBuffer() also called by
+ * in->resampler->resample_from_provider() */
+ if (in->read_status != 0)
+ return in->read_status;
+
+ frames_wr += frames_rd;
}
-out:
- if (ok) {
- ALOGV("%s: DSPs turned off!\n", __func__);
- } else {
- ALOGE("%s: Failed to wait for codec to turn off", __func__);
- usleep(50);
+
+ return frames_wr;
+}
+
+static int in_release_pcm_devices(struct stream_in *in)
+{
+ struct pcm_device *pcm_device;
+ struct listnode *node;
+ struct listnode *next;
+
+ list_for_each_safe(node, next, &in->pcm_dev_list) {
+ pcm_device = node_to_item(node, struct pcm_device, stream_list_node);
+ list_remove(node);
+ free(pcm_device);
}
+
+ return 0;
}
-static void enable_audio_route(struct audio_device *adev,
- const char *mixer_path)
+static int stop_input_stream(struct stream_in *in)
{
- ALOGV("%s: %s\n", __func__, mixer_path);
+ struct audio_usecase *uc_info;
+ struct audio_device *adev = in->dev;
+
+ adev->active_input = NULL;
+
+ ALOGV("%s: enter: usecase(%d: %s)",
+ __func__,
+ in->usecase,
+ use_case_table[in->usecase]);
+
+ uc_info = get_usecase_from_id(adev, in->usecase);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__,
+ in->usecase);
+ return -EINVAL;
+ }
+
+ /* Disable the tx device */
+ disable_snd_device(adev, uc_info, uc_info->in_snd_device);
- audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+ list_remove(&uc_info->adev_list_node);
+ free(uc_info);
+
+ if (list_empty(&in->pcm_dev_list)) {
+ ALOGE("%s: pcm device list empty", __func__);
+ return -EINVAL;
+ }
+
+ in_release_pcm_devices(in);
+ list_init(&in->pcm_dev_list);
+
+ ALOGV("%s: exit", __func__);
+
+ return 0;
}
-static void disable_audio_route(struct audio_device *adev,
- const char *mixer_path)
+static int start_input_stream(struct stream_in *in)
{
- ALOGV("%s: %s\n", __func__, mixer_path);
+ /* Enable output device and stream routing controls */
+ struct pcm_device_profile *pcm_profile;
+ struct audio_device *adev = in->dev;
+ struct audio_usecase *uc_info;
+ struct pcm_device *pcm_device;
+ bool recreate_resampler = false;
+ int ret = 0;
+
+ ALOGV("%s: enter: usecase(%d)", __func__, in->usecase);
+
+ adev->active_input = in;
+
+ pcm_profile = get_pcm_device(in->usecase_type, in->devices);
+ if (pcm_profile == NULL) {
+ ALOGE("%s: Could not find PCM device id for the usecase(%d)",
+ __func__,
+ in->usecase);
+ ret = -EINVAL;
+ goto error_config;
+ }
+
+ uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ if (uc_info == NULL) {
+ ret = -ENOMEM;
+ goto error_config;
+ }
+
+ uc_info->id = in->usecase;
+ uc_info->type = PCM_CAPTURE;
+ uc_info->stream = (struct audio_stream *)in;
+ uc_info->devices = in->devices;
+ uc_info->in_snd_device = SND_DEVICE_NONE;
+ uc_info->out_snd_device = SND_DEVICE_NONE;
+
+ pcm_device = (struct pcm_device *)calloc(1, sizeof(struct pcm_device));
+ if (pcm_device == NULL) {
+ free(uc_info);
+ ret = -ENOMEM;
+ goto error_config;
+ }
+
+ pcm_device->pcm_profile = pcm_profile;
+ list_init(&in->pcm_dev_list);
+ list_add_tail(&in->pcm_dev_list, &pcm_device->stream_list_node);
+
+ list_add_tail(&adev->usecase_list, &uc_info->adev_list_node);
- audio_route_reset_and_update_path(adev->audio_route, mixer_path);
+ select_devices(adev, in->usecase);
+
+ /* Config should be updated as profile can be changed between different calls
+ * to this function:
+ * - Trigger resampler creation
+ * - Config needs to be updated */
+ if (in->config.rate != pcm_profile->config.rate) {
+ recreate_resampler = true;
+ }
+ in->config = pcm_profile->config;
+
+ if (in->requested_rate != in->config.rate) {
+ recreate_resampler = true;
+ }
+
+ if (recreate_resampler) {
+ if (in->resampler) {
+ release_resampler(in->resampler);
+ in->resampler = NULL;
+ }
+ in->buf_provider.get_next_buffer = get_next_buffer;
+ in->buf_provider.release_buffer = release_buffer;
+ ret = create_resampler(in->config.rate,
+ in->requested_rate,
+ in->config.channels,
+ RESAMPLER_QUALITY_DEFAULT,
+ &in->buf_provider,
+ &in->resampler);
+ }
+
+ /*
+ * Open the PCM device.
+ * The HW is limited to support only the default pcm_profile settings. As
+ * such a change in aux_channels will not have an effect.
+ */
+ ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d, "
+ "smp rate %d format %d, period_size %d",
+ __func__,
+ pcm_device->pcm_profile->card,
+ pcm_device->pcm_profile->id,
+ pcm_device->pcm_profile->config.channels,
+ pcm_device->pcm_profile->config.rate,
+ pcm_device->pcm_profile->config.format,
+ pcm_device->pcm_profile->config.period_size);
+
+ pcm_device->pcm = pcm_open(pcm_device->pcm_profile->card,
+ pcm_device->pcm_profile->id,
+ PCM_IN|PCM_MONOTONIC,
+ &pcm_device->pcm_profile->config);
+
+ if (pcm_device->pcm && !pcm_is_ready(pcm_device->pcm)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(pcm_device->pcm));
+ pcm_close(pcm_device->pcm);
+ pcm_device->pcm = NULL;
+ ret = -EIO;
+ goto error_open;
+ }
+
+ /* force read and proc buffer reallocation in case of frame size or
+ * channel count change */
+ in->proc_buf_frames = 0;
+ in->proc_buf_size = 0;
+ in->read_buf_size = 0;
+ in->read_buf_frames = 0;
+
+ /* if no supported sample rate is available, use the resampler */
+ if (in->resampler) {
+ in->resampler->reset(in->resampler);
+ }
+
+ ALOGV("%s: exit", __func__);
+
+ return ret;
+
+error_open:
+ if (in->resampler) {
+ release_resampler(in->resampler);
+ in->resampler = NULL;
+ }
+ stop_input_stream(in);
+
+error_config:
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+
+ adev->active_input = NULL;
+
+ return ret;
}
-static void enable_audio_device(struct audio_device *adev,
- const char *device)
+static void lock_input_stream(struct stream_in *in)
{
- ALOGV("%s: %s\n", __func__, device);
+ pthread_mutex_lock(&in->pre_lock);
+ pthread_mutex_lock(&in->lock);
+ pthread_mutex_unlock(&in->pre_lock);
+}
- audio_route_apply_and_update_path(adev->audio_route, device);
+static void unlock_input_stream(struct stream_in *in)
+{
+ pthread_mutex_unlock(&in->lock);
}
-static void disable_audio_device(struct audio_device *adev,
- const char *device)
+static void lock_output_stream(struct stream_out *out)
{
- ALOGV("%s: %s\n", __func__, device);
+ pthread_mutex_lock(&out->pre_lock);
+ pthread_mutex_lock(&out->lock);
+ pthread_mutex_unlock(&out->pre_lock);
+}
- audio_route_reset_and_update_path(adev->audio_route, device);
+static void unlock_output_stream(struct stream_out *out)
+{
+ pthread_mutex_unlock(&out->lock);
}
-static bool route_changed(struct audio_device *adev)
+static int uc_release_pcm_devices(struct audio_usecase *usecase)
{
- int output_device_id = get_output_device_id(adev->out_device);
- int input_source_id = get_input_source_id(adev->input_source, adev->wb_amr);
- int new_route_id;
+ struct stream_out *out = (struct stream_out *)usecase->stream;
+ struct pcm_device *pcm_device;
+ struct listnode *node;
+ struct listnode *next;
- new_route_id = (1 << (input_source_id + OUT_DEVICE_CNT)) + (1 << output_device_id);
- return new_route_id != adev->cur_route_id;
+ list_for_each_safe(node, next, &out->pcm_dev_list) {
+ pcm_device = node_to_item(node, struct pcm_device, stream_list_node);
+ list_remove(node);
+ free(pcm_device);
+ }
+
+ return 0;
}
-static void select_devices(struct audio_device *adev)
+static int uc_select_pcm_devices(struct audio_usecase *usecase)
{
- int output_device_id = get_output_device_id(adev->out_device);
- int input_source_id = get_input_source_id(adev->input_source, adev->wb_amr);
- const char *output_route = NULL;
- const char *output_device = NULL;
- const char *input_route = NULL;
- const char *input_device = NULL;
- char current_device[64] = {0};
- int new_route_id;
+ struct stream_out *out = (struct stream_out *)usecase->stream;
+ struct pcm_device *pcm_device;
+ struct pcm_device_profile *pcm_profile;
+ struct mixer_card *mixer_card;
+ audio_devices_t devices = usecase->devices;
-#ifndef HDMI_INCAPABLE
- enable_hdmi_audio(adev, adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL);
-#endif
+ list_init(&out->pcm_dev_list);
- new_route_id = (1 << (input_source_id + OUT_DEVICE_CNT)) + (1 << output_device_id);
- if (new_route_id == adev->cur_route_id) {
- ALOGV("*** %s: Routing hasn't changed, leaving function.", __func__);
- return;
+
+ pcm_profile = get_pcm_device(usecase->type, devices);
+ while (pcm_profile != NULL) {
+ pcm_device = calloc(1, sizeof(struct pcm_device));
+ if (pcm_device == NULL) {
+ return -ENOMEM;
+ }
+
+ pcm_device->pcm_profile = pcm_profile;
+ list_add_tail(&out->pcm_dev_list, &pcm_device->stream_list_node);
+ devices &= ~pcm_profile->devices;
+
+ pcm_profile = get_pcm_device(usecase->type, devices);
}
- adev->cur_route_id = new_route_id;
+ return 0;
+}
- if (input_source_id != IN_SOURCE_NONE) {
- if (output_device_id != OUT_DEVICE_NONE) {
- input_route =
- route_configs[input_source_id][output_device_id]->input_route;
- input_device =
- route_configs[input_source_id][output_device_id]->input_device;
- output_route =
- route_configs[input_source_id][output_device_id]->output_route;
- output_device =
- route_configs[input_source_id][output_device_id]->output_device;
- } else {
- switch (adev->in_device) {
- case AUDIO_DEVICE_IN_WIRED_HEADSET & ~AUDIO_DEVICE_BIT_IN:
- output_device_id = OUT_DEVICE_HEADSET;
- break;
- case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET & ~AUDIO_DEVICE_BIT_IN:
- output_device_id = OUT_DEVICE_BT_SCO_HEADSET_OUT;
- break;
- default:
- if (adev->input_source == AUDIO_SOURCE_VOICE_CALL) {
- output_device_id = OUT_DEVICE_EARPIECE;
- } else {
- output_device_id = OUT_DEVICE_SPEAKER;
- }
- break;
- }
+static int out_close_pcm_devices(struct stream_out *out)
+{
+ struct pcm_device *pcm_device;
+ struct listnode *node;
+ struct audio_device *adev = out->dev;
- input_route =
- (route_configs[input_source_id][output_device_id])->input_route;
- input_device =
- (route_configs[input_source_id][output_device_id])->input_device;
+ list_for_each(node, &out->pcm_dev_list) {
+ pcm_device = node_to_item(node, struct pcm_device, stream_list_node);
+ if (pcm_device->pcm) {
+ pcm_close(pcm_device->pcm);
+ pcm_device->pcm = NULL;
}
- } else {
- if (output_device_id != OUT_DEVICE_NONE) {
- output_route =
- (route_configs[IN_SOURCE_MIC][output_device_id])->output_route;
- output_device =
- (route_configs[IN_SOURCE_MIC][output_device_id])->output_device;
+
+ if (pcm_device->resampler) {
+ release_resampler(pcm_device->resampler);
+ pcm_device->resampler = NULL;
+ }
+
+ if (pcm_device->res_buffer) {
+ free(pcm_device->res_buffer);
+ pcm_device->res_buffer = NULL;
}
}
- ALOGV("***** %s: devices=%#x, input src=%d -> "
- "output route: %s, input route: %s",
- __func__,
- adev->out_device, adev->input_source,
- output_route ? output_route : "none",
- input_route ? input_route : "none");
+ return 0;
+}
- /*
- * The Arizona driver documentation describes firmware loading this way:
- *
- * To load a firmware, or to reboot the ADSP with different firmware you
- * must:
- * - Disconnect the ADSP from any active audio path so that it will be
- * powered-down
- * - Set the firmware control to the firmware you want to load
- * - Connect the ADSP to an active audio path so it will be powered-up
- */
+static int out_open_pcm_devices(struct stream_out *out)
+{
+ struct pcm_device *pcm_device;
+ struct listnode *node;
+ int ret = 0;
- /*
- * Disable the output and input device
- */
- if (adev->active_output.route != NULL) {
- disable_audio_route(adev, adev->active_output.route);
+ list_for_each(node, &out->pcm_dev_list) {
+ pcm_device = node_to_item(node, struct pcm_device, stream_list_node);
+
+ ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
+ __func__,
+ pcm_device->pcm_profile->card,
+ pcm_device->pcm_profile->id);
+
+ pcm_device->pcm = pcm_open(pcm_device->pcm_profile->card,
+ pcm_device->pcm_profile->id,
+ PCM_OUT|PCM_MONOTONIC,
+ &pcm_device->pcm_profile->config);
+ if (pcm_device->pcm && !pcm_is_ready(pcm_device->pcm)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(pcm_device->pcm));
+ pcm_device->pcm = NULL;
+ ret = -EIO;
+ goto error_open;
+ }
+
+ /*
+ * If the stream rate differs from the PCM rate, we need to
+ * create a resampler.
+ */
+ if (out->sample_rate != pcm_device->pcm_profile->config.rate) {
+ ALOGV("%s: create_resampler(), pcm_device_card(%d), "
+ "pcm_device_id(%d), out_rate(%d), device_rate(%d)",
+ __func__,
+ pcm_device->pcm_profile->card,
+ pcm_device->pcm_profile->id,
+ out->sample_rate,
+ pcm_device->pcm_profile->config.rate);
+
+ ret = create_resampler(out->sample_rate,
+ pcm_device->pcm_profile->config.rate,
+ audio_channel_count_from_out_mask(out->channel_mask),
+ RESAMPLER_QUALITY_DEFAULT,
+ NULL,
+ &pcm_device->resampler);
+ pcm_device->res_byte_count = 0;
+ pcm_device->res_buffer = NULL;
+ }
}
- if (adev->active_input.route != NULL) {
- disable_audio_route(adev, adev->active_input.route);
+
+ return ret;
+
+error_open:
+ out_close_pcm_devices(out);
+
+ return ret;
+}
+
+static int disable_output_path_l(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ struct audio_usecase *uc_info;
+
+ uc_info = get_usecase_from_id(adev, out->usecase);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__,
+ out->usecase);
+ return -EINVAL;
}
- codecs_off();
- if (adev->active_output.device != NULL) {
- disable_audio_device(adev, adev->active_output.device);
- output_device_off(adev->active_output.dev_id);
+ disable_snd_device(adev, uc_info, uc_info->out_snd_device);
+ uc_release_pcm_devices(uc_info);
+ list_remove(&uc_info->adev_list_node);
+ free(uc_info);
+
+ return 0;
+}
+
+static int enable_output_path_l(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ struct audio_usecase *uc_info;
+
+ uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ if (uc_info == NULL) {
+ return -ENOMEM;
}
+ uc_info->id = out->usecase;
+ uc_info->type = PCM_PLAYBACK;
+ uc_info->stream = (struct audio_stream *)out;
+ uc_info->devices = out->devices;
+ uc_info->in_snd_device = SND_DEVICE_NONE;
+ uc_info->out_snd_device = SND_DEVICE_NONE;
+ uc_select_pcm_devices(uc_info);
+
+ list_add_tail(&adev->usecase_list, &uc_info->adev_list_node);
+ select_devices(adev, out->usecase);
+
+ return 0;
+}
+
+static int stop_output_stream(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ int ret = 0;
- if (adev->active_input.device != NULL) {
- disable_audio_device(adev, adev->active_input.device);
- input_devices_off();
+ ALOGV("%s: enter: usecase(%d: %s)", __func__,
+ out->usecase,
+ use_case_table[out->usecase]);
+
+ ret = disable_output_path_l(out);
+
+ ALOGV("%s: exit: status(%d)", __func__, ret);
+
+ return ret;
+}
+
+static int start_output_stream(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ int ret = 0;
+
+ ALOGV("%s: enter: usecase(%d: %s) devices(%#x) channels(%d)",
+ __func__,
+ out->usecase,
+ use_case_table[out->usecase],
+ out->devices, out->config.channels);
+
+ ret = enable_output_path_l(out);
+ if (ret != 0) {
+ return ret;
}
- /*
- * Already tell the modem that we are in a call. This should make it
- * faster to accept an incoming call.
- */
- if (adev->in_call) {
- start_ril_call(adev);
+ out->compr = NULL;
+ ret = out_open_pcm_devices(out);
+ if (ret != 0) {
+ goto error_open;
}
- /*
- * Apply the new audio routes
- */
+ ALOGV("%s: exit", __func__);
- /* OUTPUT */
- if (output_route != NULL) {
- enable_audio_route(adev, output_route);
- adev->active_output.route = output_route;
- } else {
- adev->active_output.route = NULL;
+ return 0;
+
+error_open:
+ stop_output_stream(out);
+
+ return ret;
+}
+
+static int stop_voice_call(struct audio_device *adev)
+{
+ struct audio_usecase *uc_info;
+
+ ALOGV("%s: enter", __func__);
+ if (!adev->voice.in_call) {
+ return;
}
- if (output_device != NULL) {
- enable_audio_device(adev, output_device);
- adev->active_output.device = output_device;
- adev->active_output.dev_id = output_device_id;
- } else {
- adev->active_output.device = NULL;
- adev->active_output.dev_id = -1;
+ adev->voice.in_call = false;
+
+ /* Do not change devices if we are switching to WB */
+ if (adev->mode != AUDIO_MODE_IN_CALL) {
+ uc_info = get_usecase_from_id(adev, USECASE_VOICE_CALL);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__, USECASE_VOICE_CALL);
+ return -EINVAL;
+ }
+
+ disable_snd_device(adev, uc_info, uc_info->out_snd_device);
+ disable_snd_device(adev, uc_info, uc_info->in_snd_device);
+
+ uc_release_pcm_devices(uc_info);
+ list_remove(&uc_info->adev_list_node);
+ free(uc_info);
}
- /* INPUT */
- if (input_route != NULL) {
- enable_audio_route(adev, input_route);
- adev->active_input.route = input_route;
- } else {
- adev->active_input.route = NULL;
+ ALOGV("%s: exit", __func__);
+
+ return 0;
+}
+
+/* always called with adev lock held */
+static int start_voice_call(struct audio_device *adev)
+{
+ struct audio_usecase *uc_info;
+
+ ALOGV("%s: enter", __func__);
+
+ uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ uc_info->id = USECASE_VOICE_CALL;
+ uc_info->type = VOICE_CALL;
+ uc_info->stream = (struct audio_stream *)adev->primary_output;
+ uc_info->devices = adev->primary_output->devices;
+ uc_info->in_snd_device = SND_DEVICE_NONE;
+ uc_info->out_snd_device = SND_DEVICE_NONE;
+
+ uc_select_pcm_devices(uc_info);
+
+ list_add_tail(&adev->usecase_list, &uc_info->adev_list_node);
+
+ select_devices(adev, USECASE_VOICE_CALL);
+
+
+ /* TODO: implement voice call start */
+
+ /* set cached volume */
+ set_voice_volume_l(adev, adev->voice_volume);
+
+ adev->in_call = true;
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+static int check_input_parameters(uint32_t sample_rate,
+ audio_format_t format,
+ int channel_count)
+{
+ if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ return -EINVAL;
}
- if (input_device != NULL) {
- enable_audio_device(adev, input_device);
- adev->active_input.device = input_device;
- } else {
- adev->active_input.device = NULL;
+ if ((channel_count < 1) || (channel_count > 2)) {
+ return -EINVAL;
+ }
+
+ switch (sample_rate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+ default:
+ return -EINVAL;
}
+
+ return 0;
}
-static void force_non_hdmi_out_standby(struct audio_device *adev)
+static size_t get_input_buffer_size(uint32_t sample_rate,
+ audio_format_t format,
+ int channel_count,
+ usecase_type_t usecase_type,
+ audio_devices_t devices)
{
- enum output_type type;
- struct stream_out *out;
+ struct pcm_device_profile *pcm_profile;
+ size_t size = 0;
+ int ret;
- for (type = 0; type < OUTPUT_TOTAL; ++type) {
- out = adev->outputs[type];
- if (type == OUTPUT_HDMI || !out)
- continue;
- lock_output_stream(out);
- do_out_standby(out);
- unlock_output_stream(out);
+ ret = check_input_parameters(sample_rate, format, channel_count);
+ if (ret != 0) {
+ return 0;
}
+
+ pcm_profile = get_pcm_device(usecase_type, devices);
+ if (pcm_profile == NULL)
+ 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.
+ */
+ size = (pcm_profile->config.period_size * sample_rate) / pcm_profile->config.rate;
+ size = ((size + 15) / 16) * 16;
+
+ return (size * channel_count * audio_bytes_per_sample(format));
}
-/**********************************************************
- * BT SCO functions
- **********************************************************/
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ return out->sample_rate;
+}
-/* must be called with the hw device mutex locked, OK to hold other mutexes */
-static void start_bt_sco(struct audio_device *adev)
+static int out_set_sample_rate(struct audio_stream *stream __unused,
+ uint32_t rate __unused)
{
- if (adev->pcm_sco_rx != NULL || adev->pcm_sco_tx != NULL) {
- ALOGW("%s: SCO PCMs already open!\n", __func__);
- return;
- }
+ return -ENOSYS;
+}
- ALOGV("%s: Opening SCO PCMs", __func__);
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
- adev->pcm_sco_rx = pcm_open(PCM_CARD,
- PCM_DEVICE_SCO,
- PCM_OUT | PCM_MONOTONIC,
- &pcm_config_sco);
- if (adev->pcm_sco_rx != NULL && !pcm_is_ready(adev->pcm_sco_rx)) {
- ALOGE("%s: cannot open PCM SCO RX stream: %s",
- __func__, pcm_get_error(adev->pcm_sco_rx));
- goto err_sco_rx;
- }
+ return out->config.period_size *
+ audio_stream_out_frame_size((const struct audio_stream_out *)stream);
+}
- adev->pcm_sco_tx = pcm_open(PCM_CARD,
- PCM_DEVICE_SCO,
- PCM_IN | PCM_MONOTONIC,
- &pcm_config_sco);
- if (adev->pcm_sco_tx && !pcm_is_ready(adev->pcm_sco_tx)) {
- ALOGE("%s: cannot open PCM SCO TX stream: %s",
- __func__, pcm_get_error(adev->pcm_sco_tx));
- goto err_sco_tx;
- }
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
- pcm_start(adev->pcm_sco_rx);
- pcm_start(adev->pcm_sco_tx);
+ return out->channel_mask;
+}
- return;
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ return out->format;
+}
-err_sco_tx:
- pcm_close(adev->pcm_sco_tx);
- adev->pcm_sco_tx = NULL;
-err_sco_rx:
- pcm_close(adev->pcm_sco_rx);
- adev->pcm_sco_rx = NULL;
+static int out_set_format(struct audio_stream *stream __unused,
+ audio_format_t format __unused)
+{
+ return -ENOSYS;
}
-/* must be called with the hw device mutex locked, OK to hold other mutexes */
-static void stop_bt_sco(struct audio_device *adev) {
- ALOGV("%s: Closing SCO PCMs", __func__);
+static int do_out_standby_l(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ int status = 0;
- if (adev->pcm_sco_rx != NULL) {
- pcm_stop(adev->pcm_sco_rx);
- pcm_close(adev->pcm_sco_rx);
- adev->pcm_sco_rx = NULL;
+ out->standby = true;
+ stop_compressed_output_l(out);
+ out->gapless_mdata.encoder_delay = 0;
+ out->gapless_mdata.encoder_padding = 0;
+ if (out->compr != NULL) {
+ compress_close(out->compr);
+ out->compr = NULL;
}
+ status = stop_output_stream(out);
+
+ return status;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+
+ ALOGV("%s: enter: usecase(%d: %s)",
+ __func__,
+ out->usecase,
+ use_case_table[out->usecase]);
- if (adev->pcm_sco_tx != NULL) {
- pcm_stop(adev->pcm_sco_tx);
- pcm_close(adev->pcm_sco_tx);
- adev->pcm_sco_tx = NULL;
+ lock_output_stream(out);
+ if (!out->standby) {
+ pthread_mutex_lock(&adev->lock);
+ do_out_standby_l(out);
+ pthread_mutex_unlock(&adev->lock);
}
+ unlock_output_stream(out);
+
+ ALOGV("%s: exit", __func__);
+
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream __unused, int fd __unused)
+{
+ return 0;
}
/**********************************************************
@@ -1469,30 +2404,6 @@ static void do_out_standby(struct stream_out *out)
}
}
-static void lock_input_stream(struct stream_in *in)
-{
- pthread_mutex_lock(&in->pre_lock);
- pthread_mutex_lock(&in->lock);
- pthread_mutex_unlock(&in->pre_lock);
-}
-
-static void unlock_input_stream(struct stream_in *in)
-{
- pthread_mutex_unlock(&in->lock);
-}
-
-static void lock_output_stream(struct stream_out *out)
-{
- pthread_mutex_lock(&out->pre_lock);
- pthread_mutex_lock(&out->lock);
- pthread_mutex_unlock(&out->pre_lock);
-}
-
-static void unlock_output_stream(struct stream_out *out)
-{
- pthread_mutex_unlock(&out->lock);
-}
-
/* lock outputs list, all output streams, and device */
static void lock_all_outputs(struct audio_device *adev)
{
@@ -2555,6 +3466,7 @@ static int adev_close(hw_device_t *device)
return 0;
}
+#if 0
static int adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
@@ -2610,6 +3522,110 @@ static int adev_open(const hw_module_t* module, const char* name,
return 0;
}
+#endif
+
+static int adev_open(const hw_module_t *module,
+ const char *name,
+ hw_device_t **device)
+{
+ struct audio_device *adev;
+ int retry_count = 0;
+ int cmp;
+
+ ALOGV("%s: enter", __func__);
+
+ *device = NULL;
+
+ cmp = strcmp(name, AUDIO_HARDWARE_INTERFACE);
+ if (cmp != 0) {
+ return -EINVAL;
+ }
+
+ adev = calloc(1, sizeof(struct audio_device));
+ if (adev == NULL) {
+ return -ENOMEM;
+ }
+
+ adev->device.common.tag = HARDWARE_DEVICE_TAG;
+ adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+ adev->device.common.module = (struct hw_module_t *)module;
+ adev->device.common.close = adev_close;
+
+ adev->device.init_check = adev_init_check;
+ adev->device.set_voice_volume = adev_set_voice_volume;
+ adev->device.set_master_volume = adev_set_master_volume;
+ adev->device.get_master_volume = adev_get_master_volume;
+ adev->device.set_master_mute = adev_set_master_mute;
+ adev->device.get_master_mute = adev_get_master_mute;
+ adev->device.set_mode = adev_set_mode;
+ adev->device.set_mic_mute = adev_set_mic_mute;
+ adev->device.get_mic_mute = adev_get_mic_mute;
+ adev->device.set_parameters = adev_set_parameters;
+ adev->device.get_parameters = adev_get_parameters;
+ adev->device.get_input_buffer_size = adev_get_input_buffer_size;
+ adev->device.open_output_stream = adev_open_output_stream;
+ adev->device.close_output_stream = adev_close_output_stream;
+ adev->device.open_input_stream = adev_open_input_stream;
+ adev->device.close_input_stream = adev_close_input_stream;
+ adev->device.dump = adev_dump;
+
+ /* Set the default route before the PCM stream is opened */
+ adev->mode = AUDIO_MODE_NORMAL;
+ adev->active_input = NULL;
+ adev->primary_output = NULL;
+ adev->voice_volume = 1.0f;
+ adev->tty_mode = TTY_MODE_OFF;
+ adev->bluetooth_nrec = true;
+ adev->in_call = false;
+ /* adev->cur_hdmi_channels = 0; by calloc() */
+ adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int));
+
+ adev->dualmic_config = DUALMIC_CONFIG_NONE;
+ adev->ns_in_voice_rec = false;
+
+ list_init(&adev->usecase_list);
+
+ adev->mixer.audio_route = audio_route_init(MIXER_CARD, NULL);
+ if (adev->mixer.audio_route == NULL) {
+ ALOGE("%s: Failed to init, aborting.", __func__);
+
+ free(adev->snd_dev_ref_cnt);
+ free(adev);
+
+ return -EINVAL;
+ }
+
+ /* Do not sleep on first enable_snd_device() */
+ adev->mixer.shutdown_time = {
+ .tv_sec = 1,
+ }
+
+ /* RIL */
+ ril_open(&adev->ril);
+ /* register callback for wideband AMR setting */
+ ril_register_set_wb_amr_callback(adev_set_wb_amr_callback, (void *)adev);
+
+ *device = &adev->device.common;
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("audio_hal.period_size", value, NULL) > 0) {
+ int trial = atoi(value);
+ if (period_size_is_plausible_for_low_latency(trial)) {
+
+ pcm_device_playback.config.period_size = trial;
+ pcm_device_playback.config.start_threshold =
+ PLAYBACK_START_THRESHOLD(trial, PLAYBACK_PERIOD_COUNT);
+ pcm_device_playback.config.stop_threshold =
+ PLAYBACK_STOP_THRESHOLD(trial, PLAYBACK_PERIOD_COUNT);
+
+ pcm_device_capture_low_latency.config.period_size = trial;
+ }
+ }
+
+ ALOGV("%s: exit", __func__);
+
+ return 0;
+}
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
diff --git a/hal/audio/audio_hw.h b/hal/audio/audio_hw.h
new file mode 100644
index 0000000..7f5a6ac
--- /dev/null
+++ b/hal/audio/audio_hw.h
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+
+#ifndef WOLFSON_AUDIO_HW_H
+#define WOLFSON_AUDIO_HW_H
+
+#include <cutils/list.h>
+#include <hardware/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+#include <tinycompress/tinycompress.h>
+
+#include <audio_utils/resampler.h>
+#include <audio_route/audio_route.h>
+
+#define MIXER_CARD 0
+#define SOUND_CARD 0
+
+#define PCM_CARD 0
+#define PCM_CARD_SPDIF 1
+#define PCM_TOTAL 2
+
+#define PCM_DEVICE_PLAYBACK 0 /* Playback link */
+#define PCM_DEVICE_CAPTURE 0 /* Capture link */
+#define PCM_DEVICE_VOICE 1 /* Baseband link */
+#define PCM_DEVICE_SCO 2 /* Bluetooth link */
+#define PCM_DEVICE_DEEP_BUFFER 3 /* Deep buffer */
+
+#define PLAYBACK_PERIOD_SIZE 240
+#define PLAYBACK_PERIOD_SIZE_DEEP_BUFFER 960
+#define PLAYBACK_PERIOD_COUNT 2
+#define PLAYBACK_PERIOD_COUNT_DEEP_BUFFER 2
+#define PLAYBACK_DEFAULT_CHANNEL_COUNT 2
+#define PLAYBACK_DEFAULT_SAMPLING_RATE 48000
+#define PLAYBACK_START_THRESHOLD(size, count) (((size) * (count)) - 1)
+#define PLAYBACK_STOP_THRESHOLD(size, count) ((size) * ((count) + 2))
+#define PLAYBACK_AVAILABLE_MIN 1
+
+#define DEEP_BUFFER_PERIOD_SIZE 960
+#define DEEP_BUFFER_PERIOD_COUNT 2
+#define DEEP_BUFFER_CHANNEL_COUNT 2
+#define DEEP_BUFFER_SAMPLING_RATE 48000
+
+#define SCO_PERIOD_SIZE 240
+#define SCO_PERIOD_COUNT 2
+#define SCO_DEFAULT_CHANNEL_COUNT 2
+#define SCO_DEFAULT_SAMPLING_RATE 8000
+#define SCO_START_THRESHOLD 335
+#define SCO_STOP_THRESHOLD 336
+#define SCO_AVAILABLE_MIN 1
+
+#define CAPTURE_PERIOD_SIZE 320
+#define CAPTURE_PERIOD_SIZE_LOW_LATENCY 240
+#define CAPTURE_PERIOD_COUNT 2
+#define CAPTURE_PERIOD_COUNT_LOW_LATENCY 2
+#define CAPTURE_DEFAULT_CHANNEL_COUNT 2
+#define CAPTURE_DEFAULT_SAMPLING_RATE 48000
+#define CAPTURE_START_THRESHOLD 1
+
+#define VOICE_DEFAULT_PERIOD_SIZE 320
+#define VOICE_DEFAULT_PERIOD_COUNT 2
+#define VOICE_DEFAULT_CHANNEL_COUNT 2
+#define VOICE_SAMPLING_RATE 8000
+#define VOICE_SAMPLING_RATE_WIDEBAND 16000
+#define VOICE_START_THRESHOLD 1
+
+#define HDMI_PERIOD_SIZE 336
+#define HDMI_PERIOD_COUNT 8
+#define HDMI_DEFAULT_CHANNEL_COUNT 6
+#define HDMI_DEFAULT_SAMPLING_RATE 48000
+#define HDMI_START_THRESHOLD 1
+
+#define HDMI_MAX_SUPPORTED_CHANNEL_MASKS 2
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+typedef int snd_device_t;
+
+typedef enum {
+ USECASE_INVALID = -1,
+
+ /* Playback usecases */
+ USECASE_AUDIO_PLAYBACK = 0,
+ USECASE_AUDIO_PLAYBACK_MULTI_CH,
+
+ USECASE_AUDIO_HFP_SCO,
+
+ /* Capture usecases */
+ USECASE_AUDIO_CAPTURE,
+ USECASE_AUDIO_CAPTURE_LOW_LATENCY,
+
+ USECASE_VOICE_CALL,
+
+ AUDIO_USECASE_MAX
+} audio_usecase_t;
+
+typedef enum {
+ PCM_PLAYBACK,
+ PCM_CAPTURE,
+ VOICE_CALL,
+ PCM_HPF_CALL,
+} usecase_type_t;
+
+struct pcm_device_profile {
+ struct pcm_config config;
+ int card;
+ int id;
+ usecase_type_t type;
+ audio_devices_t devices;
+};
+
+struct stream_out {
+ struct audio_stream_out stream;
+
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
+ pthread_cond_t cond;
+
+ struct pcm_config config;
+ struct listnode pcm_dev_list;
+ bool standby;
+ unsigned int sample_rate;
+ audio_channel_mask_t channel_mask;
+ audio_format_t format;
+ 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];
+ bool muted;
+ /* total frames written, not cleared when entering standby */
+ uint64_t written;
+ int64_t last_write_time_us;
+ audio_io_handle_t handle;
+
+ struct audio_device *dev;
+}
+
+struct stream_in {
+ struct audio_stream_in stream;
+
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by
+ capture thread */
+ struct pcm_config config;
+ struct listnode pcm_dev_list;
+ int standby;
+ audio_source_t source;
+ audio_devices_t devices;
+ uint32_t main_channels;
+ audio_usecase_t usecase;
+ usecase_type_t usecase_type;
+ bool enable_aec;
+ audio_input_flags_t input_flags;
+
+ /* TODO: remove resampler if possible when AudioFlinger supports downsampling from 48 to 8 */
+ unsigned int requested_rate;
+ struct resampler_itfe* resampler;
+ struct resampler_buffer_provider buf_provider;
+ int read_status;
+ int16_t* read_buf;
+ size_t read_buf_size;
+ size_t read_buf_frames;
+
+ struct audio_device* dev;
+};
+
+struct audio_usecase {
+ struct listnode adev_list_node;
+ audio_usecase_t id;
+ usecase_type_t type;
+ audio_devices_t devices;
+ snd_device_t out_snd_device;
+ snd_device_t in_snd_device;
+ struct audio_stream* stream;
+ struct listnode mixer_list;
+};
+
+struct audio_device {
+ struct audio_hw_device hw_device;
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+
+ struct {
+ struct audio_route *audio_route;
+ struct timespec shutdown_time;
+ } mixer;
+
+ audio_mode_t mode;
+ struct stream_in *active_input;
+ struct stream_out *primary_output;
+
+ struct {
+ bool in_call;
+ float volume;
+ bool mic_mute;
+ bool bluetooth_nrec;
+ bool wb_amr;
+ bool two_mic_config;
+ } voice;
+
+ int *snd_dev_ref_cnt;
+ struct listnode usecase_list;
+
+ /* RIL */
+ struct ril_handle ril;
+
+ pthread_mutex_t lock_inputs; /* see note below on mutex acquisition order */
+ pthread_mutex_t lock_outputs; /* see note below on mutex acquisition order */
+};
+
+#endif /* WOLFSON_AUDIO_HW_H */
diff --git a/hal/audio/routing.h b/hal/audio/routing.h
index efc3b6d..3921cf5 100644
--- a/hal/audio/routing.h
+++ b/hal/audio/routing.h
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2013 The CyanogenMod Project
+ * Copyright (C) 2017 The LineagOS Project
+ * Copyright (C) 2017 Andreas Schneider <asn@cryptomilk.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,321 +16,85 @@
* limitations under the License.
*/
-#ifndef _ROUTING_H_
-#define _ROUTING_H_
+#ifndef _AUDIO_ROUTING_H_
+#define _AUDIO_ROUTING_H_
enum {
- OUT_DEVICE_SPEAKER,
- OUT_DEVICE_EARPIECE,
- OUT_DEVICE_HEADSET,
- OUT_DEVICE_HEADPHONES,
- OUT_DEVICE_BT_SCO,
- OUT_DEVICE_BT_SCO_HEADSET_OUT,
- OUT_DEVICE_BT_SCO_CARKIT,
- OUT_DEVICE_SPEAKER_AND_HEADSET,
- OUT_DEVICE_SPEAKER_AND_EARPIECE,
- OUT_DEVICE_TAB_SIZE, /* number of rows in route_configs[][] */
- OUT_DEVICE_NONE,
- OUT_DEVICE_CNT
-};
-
-enum {
- IN_SOURCE_MIC,
- IN_SOURCE_CAMCORDER,
- IN_SOURCE_VOICE_RECOGNITION,
- IN_SOURCE_VOICE_COMMUNICATION,
- IN_SOURCE_VOICE_CALL,
- IN_SOURCE_VOICE_CALL_WB,
- IN_SOURCE_TAB_SIZE, /* number of lines in route_configs[][] */
- IN_SOURCE_NONE,
- IN_SOURCE_CNT
-};
-
-struct route_config {
- const char * const output_route;
- const char * const output_device;
- const char * const input_route;
- const char * const input_device;
-};
-
-const struct route_config voice_speaker = {
- "voice-speaker",
- "device-speaker",
- "voice-speaker-two-mic",
- "device-two-mic",
-};
-
-const struct route_config voice_speaker_wb = {
- "voice-speaker-wb",
- "device-speaker",
- "voice-speaker-two-mic-wb",
- "device-two-mic",
-};
-
-const struct route_config voice_earpiece = {
- "voice-earpiece",
- "device-earpiece",
- "voice-earpiece-two-mic",
- "device-two-mic",
-};
-
-const struct route_config voice_earpiece_wb = {
- "voice-earpiece-wb",
- "device-earpiece",
- "voice-earpiece-two-mic-wb",
- "device-two-mic",
-};
-
-const struct route_config voice_headphones = {
- "voice-headphones",
- "device-headphones",
- "voice-headphones-two-mic",
- "device-two-mic",
-};
-
-const struct route_config voice_headphones_wb = {
- "voice-headphones-wb",
- "device-headphones",
- "voice-headphones-two-mic-wb",
- "device-two-mic",
-};
-
-const struct route_config voice_headset = {
- "voice-headphones",
- "device-headphones",
- "voice-headset-mic",
- "device-headset-mic",
-};
-
-const struct route_config voice_headset_wb = {
- "voice-headphones-wb",
- "device-headphones",
- "voice-headset-mic-wb",
- "device-headset-mic",
-};
-
-const struct route_config voice_bt_sco = {
- "voice-bt-sco",
- "device-sco",
- "voice-bt-sco-headset-mic",
- "device-sco-headset-mic",
-};
-
-const struct route_config voice_bt_sco_wb = {
- "voice-bt-sco-wb",
- "device-sco",
- "voice-bt-sco-headset-mic-wb",
- "device-sco-headset-mic",
-};
-
-const struct route_config voice_bt_sco_headset_out = {
- "voice-bt-sco-headset-out",
- "device-sco-headset-out",
- "voice-bt-sco-headset-mic",
- "device-sco-headset-mic",
-};
-
-const struct route_config voice_bt_sco_headset_out_wb = {
- "voice-bt-sco-headset-out-wb",
- "device-sco-headset-out",
- "voice-bt-sco-headset-mic-wb",
- "device-sco-headset-mic",
-};
-
-const struct route_config media_speaker = {
- "media-speaker",
- "device-speaker",
- "media-builtin-mic",
- "device-builtin-mic",
-};
-
-const struct route_config media_earpiece = {
- "media-earpiece",
- "device-earpiece",
- "media-builtin-mic",
- "device-builtin-mic",
-};
-
-const struct route_config media_headphones = {
- "media-headphones",
- "device-headphones",
- "media-builtin-mic",
- "device-builtin-mic",
-};
-
-const struct route_config media_headset = {
- "media-headphones",
- "device-headphones",
- "media-headset-mic",
- "device-headset-mic",
-};
-
-const struct route_config media_bt_sco = {
- "media-bt-sco",
- "device-sco",
- "media-bt-sco-headset-mic",
- "device-sco-headset-mic",
-};
-
-const struct route_config media_bt_sco_headset_out = {
- "media-bt-sco",
- "device-sco-headset-out",
- "media-bt-sco-headset-mic",
- "device-sco-headset-mic",
-};
-
-const struct route_config camcorder_speaker = {
- "media-speaker",
- "device-speaker",
- "media-second-mic",
- "device-back-mic",
-};
-
-const struct route_config camcorder_headphones = {
- "media-headphones",
- "device-headphones",
- "media-second-mic",
- "device-back-mic",
-};
-
-const struct route_config voice_rec_speaker = {
- "voice-rec-speaker",
- "device-speaker",
- "voice-rec-two-mic",
- "device-builtin-mic",
-};
-
-const struct route_config voice_rec_headphones = {
- "voice-rec-headphones",
- "device-headphones",
- "voice-rec-two-mic",
- "device-builtin-mic",
-};
-
-const struct route_config voice_rec_headset = {
- "voice-rec-headphones",
- "device-headphones",
- "voice-rec-headset-mic",
- "device-headset-mic",
-};
-
-const struct route_config communication_speaker = {
- "communication-speaker",
- "device-speaker",
- "communication-speaker-two-mic",
- "device-two-mic",
-};
-
-const struct route_config communication_earpiece = {
- "communication-earpiece",
- "device-earpiece",
- "communication-earpiece-two-mic",
- "device-two-mic",
-};
-
-const struct route_config communication_headphones = {
- "communication-headphones",
- "device-headphones",
- "communication-earpiece-two-mic",
- "device-two-mic",
-};
-
-const struct route_config communication_headset = {
- "communication-headphones",
- "device-headphones",
- "communication-headset-mic",
- "device-headset-mic",
-};
-
-const struct route_config speaker_and_headphones = {
- "speaker-and-headphones",
- "device-speaker",
- "scenario-two-mic-speaker-default",
- "device-two-mic",
-};
-
-const struct route_config bt_sco_carkit = {
- "bt-sco-carkit",
- "device-sco-carkit",
- "media-bt-sco-headset-mic",
- "device-sco-headset-mic",
-};
-
-const struct route_config none = {
- "none",
- "none",
- "none",
- "none",
-};
-
-const struct route_config * const route_configs[IN_SOURCE_TAB_SIZE]
- [OUT_DEVICE_TAB_SIZE] = {
- { /* IN_SOURCE_MIC */
- &media_speaker, /* OUT_DEVICE_SPEAKER */
- &media_earpiece, /* OUT_DEVICE_EARPIECE */
- &media_headset, /* OUT_DEVICE_HEADSET */
- &media_headphones, /* OUT_DEVICE_HEADPHONES */
- &media_bt_sco, /* OUT_DEVICE_BT_SCO */
- &media_bt_sco_headset_out, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &speaker_and_headphones, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &media_speaker /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
- { /* IN_SOURCE_CAMCORDER */
- &camcorder_speaker, /* OUT_DEVICE_SPEAKER */
- &none, /* OUT_DEVICE_EARPIECE */
- &camcorder_headphones, /* OUT_DEVICE_HEADSET */
- &camcorder_headphones, /* OUT_DEVICE_HEADPHONES */
- &media_bt_sco, /* OUT_DEVICE_BT_SCO */
- &media_bt_sco_headset_out, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &speaker_and_headphones, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &camcorder_speaker /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
- { /* IN_SOURCE_VOICE_RECOGNITION */
- &voice_rec_speaker, /* OUT_DEVICE_SPEAKER */
- &none, /* OUT_DEVICE_EARPIECE */
- &voice_rec_headset, /* OUT_DEVICE_HEADSET */
- &voice_rec_headphones, /* OUT_DEVICE_HEADPHONES */
- &media_bt_sco, /* OUT_DEVICE_BT_SCO */
- &media_bt_sco_headset_out, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &speaker_and_headphones, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &voice_rec_speaker /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
- { /* IN_SOURCE_VOICE_COMMUNICATION */
- &communication_speaker, /* OUT_DEVICE_SPEAKER */
- &communication_earpiece, /* OUT_DEVICE_EARPIECE */
- &communication_headset, /* OUT_DEVICE_HEADSET */
- &communication_headphones, /* OUT_DEVICE_HEADPHONES */
- &media_bt_sco, /* OUT_DEVICE_BT_SCO */
- &media_bt_sco_headset_out, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &speaker_and_headphones, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &communication_earpiece /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
- { /* IN_SOURCE_VOICE_CALL */
- &voice_speaker, /* OUT_DEVICE_SPEAKER */
- &voice_earpiece, /* OUT_DEVICE_EARPIECE */
- &voice_headset, /* OUT_DEVICE_HEADSET */
- &voice_headphones, /* OUT_DEVICE_HEADPHONES */
- &voice_bt_sco, /* OUT_DEVICE_BT_SCO */
- &voice_bt_sco_headset_out, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &voice_headphones, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &voice_earpiece /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
- { /* IN_SOURCE_VOICE_CALL_WB */
- &voice_speaker_wb, /* OUT_DEVICE_SPEAKER */
- &voice_earpiece_wb, /* OUT_DEVICE_EARPIECE */
- &voice_headset_wb, /* OUT_DEVICE_HEADSET */
- &voice_headphones_wb, /* OUT_DEVICE_HEADPHONES */
- &voice_bt_sco_wb, /* OUT_DEVICE_BT_SCO */
- &voice_bt_sco_headset_out_wb, /* OUT_DEVICE_BT_SCO_HEADSET_OUT */
- &bt_sco_carkit, /* OUT_DEVICE_BT_SCO_CARKIT */
- &voice_headphones_wb, /* OUT_DEVICE_SPEAKER_AND_HEADSET */
- &voice_earpiece_wb /* OUT_DEVICE_SPEAKER_AND_EARPIECE */
- },
-};
-
-#endif
+ SND_DEVICE_NONE = 0,
+
+ /* Playback devices */
+ SND_DEVICE_MIN,
+ SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN,
+ SND_DEVICE_OUT_EARPIECE = SND_DEVICE_OUT_BEGIN,
+ SND_DEVICE_OUT_SPEAKER,
+ SND_DEVICE_OUT_HEADPHONES,
+ SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+ SND_DEVICE_OUT_VOICE_EARPIECE,
+ SND_DEVICE_OUT_VOICE_EARPIECE_WB,
+ SND_DEVICE_OUT_VOICE_SPEAKER,
+ SND_DEVICE_OUT_VOICE_SPEAKER_WB,
+ SND_DEVICE_OUT_VOICE_HEADPHONES,
+ SND_DEVICE_OUT_VOICE_HEADPHONES_WB,
+ SND_DEVICE_OUT_HDMI,
+ SND_DEVICE_OUT_SPEAKER_AND_HDMI,
+ SND_DEVICE_OUT_BT_SCO,
+ 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_EARPIECE_MIC = SND_DEVICE_IN_BEGIN,
+ SND_DEVICE_IN_SPEAKER_MIC,
+ SND_DEVICE_IN_HEADSET_MIC,
+ SND_DEVICE_IN_EARPIECE_MIC_AEC,
+ SND_DEVICE_IN_SPEAKER_MIC_AEC,
+ SND_DEVICE_IN_HEADSET_MIC_AEC,
+ SND_DEVICE_IN_VOICE_EARPIECE_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_REC_HEADSET_MIC,
+ SND_DEVICE_IN_VOICE_REC_MIC,
+ SND_DEVICE_IN_END,
+
+ SND_DEVICE_MAX = SND_DEVICE_IN_END,
+};
+
+/* Array to store sound devices */
+static const char * const device_table[SND_DEVICE_MAX] = {
+ [SND_DEVICE_NONE] = "none",
+ /* Playback sound devices */
+ [SND_DEVICE_OUT_EARPIECE] = "earpiece",
+ [SND_DEVICE_OUT_SPEAKER] = "speaker",
+ [SND_DEVICE_OUT_HEADPHONES] = "headphones",
+ [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
+ [SND_DEVICE_OUT_VOICE_EARPIECE] = "voice-earpiece",
+ [SND_DEVICE_OUT_VOICE_EARPIECE] = "voice-earpiece-wb",
+ [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker",
+ [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker-wb",
+ [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones",
+ [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones-wb",
+ [SND_DEVICE_OUT_HDMI] = "hdmi",
+ [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
+ [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
+
+ /* Capture sound devices */
+ [SND_DEVICE_IN_EARPIECE_MIC] = "earpiece-mic",
+ [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic",
+ [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic",
+ [SND_DEVICE_IN_VOICE_EARPIECE_MIC] = "voice-earpiece-mic",
+ [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic",
+ [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic",
+ [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic",
+ [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic",
+ [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
+ [SND_DEVICE_IN_VOICE_REC_HEADSET_MIC] = "voice-rec-headset-mic",
+ [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic",
+};
+
+#endif /* _AUDIO_ROUTING_H */