diff options
author | Mohammed Habibulla <moch@google.com> | 2015-12-08 19:06:24 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-12-08 19:06:24 +0000 |
commit | 36e440a998048da05193ea8efd64a84d23666b78 (patch) | |
tree | 2b83f487923f616d4e4c87c3a5defa28d73f25e5 | |
parent | 9ed81347c6c23923507046a3046a69ae6783f29d (diff) | |
parent | 8ce9a6c153cf6205a9acb579a6a304351170fee5 (diff) | |
download | marvell-36e440a998048da05193ea8efd64a84d23666b78.tar.gz |
Merge "Upload the Audio HAL Source Code" into mnc-brillo-dev
-rwxr-xr-x | peripheral/audio/audio_policy.conf | 6 | ||||
-rw-r--r-- | peripheral/audio/driver/Android.mk | 84 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_config.c | 587 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_effect_mrvl.c | 172 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_effect_mrvl.h | 24 | ||||
-rwxr-xr-x | peripheral/audio/driver/audio_hw_mrvl.c | 3279 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_hw_mrvl.h | 280 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_path.c | 511 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_path.h | 188 | ||||
-rw-r--r-- | peripheral/audio/driver/audio_profile.h | 565 | ||||
-rw-r--r-- | peripheral/audio/driver/include/acm/acm_api.h | 87 | ||||
-rw-r--r-- | peripheral/audio/driver/include/acm/acm_param.h | 36 | ||||
-rw-r--r-- | soc/iap140/modules/audio_hal_module.mk | 20 |
13 files changed, 5830 insertions, 9 deletions
diff --git a/peripheral/audio/audio_policy.conf b/peripheral/audio/audio_policy.conf index 55819f3..cc2ebdf 100755 --- a/peripheral/audio/audio_policy.conf +++ b/peripheral/audio/audio_policy.conf @@ -20,8 +20,8 @@ global_configuration { - attached_output_devices AUDIO_DEVICE_OUT_SPEAKER - default_output_device AUDIO_DEVICE_OUT_SPEAKER + attached_output_devices AUDIO_DEVICE_OUT_WIRED_HEADSET + default_output_device AUDIO_DEVICE_OUT_WIRED_HEADSET attached_input_devices AUDIO_DEVICE_IN_WIRED_HEADSET } @@ -43,7 +43,7 @@ audio_hw_modules { sampling_rates 44100 channel_masks AUDIO_CHANNEL_OUT_STEREO formats AUDIO_FORMAT_PCM_16_BIT - devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET + devices AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET flags AUDIO_OUTPUT_FLAG_PRIMARY } } diff --git a/peripheral/audio/driver/Android.mk b/peripheral/audio/driver/Android.mk new file mode 100644 index 0000000..8b9ade3 --- /dev/null +++ b/peripheral/audio/driver/Android.mk @@ -0,0 +1,84 @@ +# Copyright (C) 2015 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. + +ifneq ($(strip $(BOARD_AUDIO_COMPONENT_APU)),) + +LOCAL_PATH:= $(call my-dir) + +ifneq ($(BOARD_USES_GENERIC_AUDIO),true) + +# prebuild the Marvell proprietory audio codec driver +include $(CLEAR_VARS) +LOCAL_PREBUILT_LIBS := ../../../../../../vendor/bsp/marvell/device/abox_edge/hal/audio/libacm.so +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_OWNER := marvell +include $(BUILD_MULTI_PREBUILT) + +# build audio.primary.mrvl.so +include $(CLEAR_VARS) + +LOCAL_MODULE_TARGET_ARCH := arm + +LOCAL_SRC_FILES:= \ + audio_hw_mrvl.c \ + audio_path.c \ + audio_config.c \ + audio_effect_mrvl.c + +LOCAL_C_INCLUDES += \ + external/icu/icu4c/source/common \ + external/tinyalsa/include/ \ + $(LOCAL_PATH)/include/acm \ + system/media/audio/include \ + system/media/audio_utils/include/audio_utils \ + system/media/audio_effects/include/audio_effects \ + frameworks/av/include + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libtinyalsa \ + libhardware \ + libaudioutils \ + libeffects \ + libexpat \ + libacm + +LOCAL_MODULE:= audio.primary.mrvl +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += -Wno-unused-parameter + +ifeq ($(strip $(BOARD_ENABLE_ADVANCED_AUDIO)),true) + LOCAL_CFLAGS += -DWITH_ADVANCED_AUDIO +endif + +ifeq ($(strip $(BOARD_WITH_STEREO_SPKR)),true) + LOCAL_CFLAGS += -DWITH_STEREO_SPKR +endif + +ifeq ($(strip $(BOARD_WITH_HEADSET_OUTPUT_ONLY)),true) + LOCAL_CFLAGS += -DROUTE_SPEAKER_TO_HEADSET +endif + +LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) -D_POSIX_SOURCE + +ifeq ($(strip $(BOARD_AUDIO_COMPONENT_APU)), MAP-LITE) + LOCAL_CFLAGS += -DWITH_MAP_LITE +endif + +include $(BUILD_SHARED_LIBRARY) + +endif + +endif diff --git a/peripheral/audio/driver/audio_config.c b/peripheral/audio/driver/audio_config.c new file mode 100644 index 0000000..91de485 --- /dev/null +++ b/peripheral/audio/driver/audio_config.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "audio_hw_config" +#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <hardware/hardware.h> +#include <hardware/audio.h> +#include <system/audio.h> +#include <cutils/log.h> +#include <expat.h> + +#include "acm_api.h" +#include "audio_path.h" + +#define AUDIO_PLATFORM_CONFIG_FILE "/etc/platform_audio_config.xml" +#define DEVICE_NAME_LEN_MAX 32 + +struct app_cfg_t { + virtual_mode_t v_mode; + unsigned int device; // support multi configurations + struct app_cfg_t *next; +}; + +struct android_dev_cfg_t { + unsigned int android_dev; + struct app_cfg_t *app_cfg; + struct android_dev_cfg_t *next; +}; + +struct board_dev_cfg_t { + unsigned int hw_dev; + unsigned int connectivity; // connectivity type + unsigned int coupling; // coupling type + struct board_dev_cfg_t *next; +}; + +struct device_name_t { + char name[DEVICE_NAME_LEN_MAX]; + int len; +}; + +struct platform_config_t { + struct board_dev_cfg_t *board_dev_cfg; // support board device list + struct android_dev_cfg_t *droid_dev_cfg; // support android device list + + struct board_dev_cfg_t *current_board_device; + struct device_name_t current_device_name; + struct android_dev_cfg_t *current_droid_device; + struct app_cfg_t *current_app_cfg; +}; + +typedef enum { + PARSING_OK = 0, + PARSING_UNKNOWN_ERROR, + PARSING_ERROR_IO, + PARSING_ERROR_MALFORMED, +} parsing_check_t; + +typedef enum { + SECTION_UNKNOWN = -1, + SECTION_TOP_LEVEL, + SECTION_BOARD_DEVICE_LIST, + SECTION_ANDROID_DEVICE, + SECTION_APPLICATION, + SECTION_DEVICE_IN_BOARD, + SECTION_DEVICE_IN_ANDROID, +} section_layout_t; + +static struct platform_config_t *mrvl_platform_cfg = NULL; +static section_layout_t current_section = SECTION_UNKNOWN; + +// get mic device from platform_audio_config.xml config +unsigned int get_mic_dev(virtual_mode_t v_mode, unsigned int android_dev) { + struct android_dev_cfg_t *droiddev_cfg = mrvl_platform_cfg->droid_dev_cfg; + unsigned int default_dev = HWDEV_INVALID; + + while (droiddev_cfg) { + if (droiddev_cfg->android_dev == android_dev) { + struct app_cfg_t *app_cfg = droiddev_cfg->app_cfg; + while (app_cfg) { + if (app_cfg->v_mode == v_mode) { + ALOGD("%s: find matched dev 0x%x", __FUNCTION__, app_cfg->device); + return app_cfg->device; + } else if (app_cfg->v_mode == V_MODE_DEF) { + default_dev = app_cfg->device; + } + app_cfg = app_cfg->next; + } + + // if none matched app found, use default device + ALOGD("%s: cannot find matched app, use default dev 0x%x", __FUNCTION__, + default_dev); + return default_dev; + } + + droiddev_cfg = droiddev_cfg->next; + } + // return default device + return HWDEV_AMIC1; +} + +// get mic hw flag for connectivity and coupling +unsigned int get_mic_hw_flag(unsigned int hw_dev) { + unsigned int flags = 0; + struct board_dev_cfg_t *dev_cfg = mrvl_platform_cfg->board_dev_cfg; + + // for TTY, use the equivalent device + switch (hw_dev) { + case HWDEV_IN_TTY: + hw_dev = HWDEV_HSMIC; + break; + case HWDEV_IN_TTY_VCO_DUAL_AMIC: + hw_dev = HWDEV_DUAL_AMIC; + break; + case HWDEV_IN_TTY_VCO_DUAL_AMIC_SPK_MODE: + hw_dev = HWDEV_DUAL_AMIC_SPK_MODE; + break; + case HWDEV_IN_TTY_VCO_DUAL_DMIC1: + hw_dev = HWDEV_DUAL_DMIC1; + break; + case HWDEV_IN_TTY_VCO_AMIC1: + hw_dev = HWDEV_AMIC1; + break; + case HWDEV_IN_TTY_VCO_AMIC2: + hw_dev = HWDEV_AMIC2; + break; + default: + // keep the current hw device. + break; + } + + while (dev_cfg) { + if (dev_cfg->hw_dev == hw_dev) { + flags = (dev_cfg->coupling | dev_cfg->connectivity); + } + dev_cfg = dev_cfg->next; + } + return flags; +} + +static void get_android_dev_by_user_selection(char *dev_name) { + uint32_t mic_mode = get_mic_mode(); + + ALOGD("%s mic_mode= %d", __FUNCTION__, mic_mode); + + switch (mic_mode) { + case MIC_MODE_MIC1: + case MIC_MODE_MIC2: + case MIC_MODE_DUALMIC: + if (strstr(dev_name, "_SPK_MODE")) { + strcpy(dev_name, mic_mode_to_dev_name[mic_mode]); + strcat(dev_name, "_SPK_MODE"); + } else + strcpy(dev_name, mic_mode_to_dev_name[mic_mode]); + break; + + case MIC_MODE_NONE: + default: + break; + } +} + +static unsigned int get_android_dev_byname(char *dev_name) { + if (!strcmp(dev_name, "AUDIO_DEVICE_IN_BUILTIN_MIC")) { + return AUDIO_DEVICE_IN_BUILTIN_MIC; + } else if (!strcmp(dev_name, "AUDIO_DEVICE_IN_BACK_MIC")) { + return AUDIO_DEVICE_IN_BACK_MIC; + } + return 0; +} + +static virtual_mode_t get_mode_byname(char *app_name) { + int i = 0; + + for (i = 0; i < (int)(sizeof(vtrl_mode_name) / sizeof(char *)); i++) { + if (!strcmp(vtrl_mode_name[i], app_name)) { + return i; + } + } + return V_MODE_INVALID; +} + +static unsigned int get_hwdev_byname(char *dev_name) { + int i = 0; + + get_android_dev_by_user_selection(dev_name); // Change the dev_name according + // to the User-Selection (if + // needed) + + for (i = 0; i < (int)(sizeof(input_devname) / sizeof(char *)); i++) { + if (!strcmp(input_devname[i], dev_name)) { + return (HWDEV_BIT_IN | (HWDEV_IN_BASE << i)); + } + } + return HWDEV_INVALID; +} + +static void parseBoardDevice(struct platform_config_t *config_data, + const char **attrs) { + struct board_dev_cfg_t *dev_cfg; + const char *connectivity = NULL; + const char *coupling = NULL; + int i = 0; + + dev_cfg = (struct board_dev_cfg_t *)calloc(1, sizeof(struct board_dev_cfg_t)); + + if (dev_cfg == NULL) { + ALOGE("%s/L%d: out of memory", __FUNCTION__, __LINE__); + return; + } + + while (attrs[i] != NULL) { + if (strcmp("connectivity", attrs[i]) == 0) { + connectivity = attrs[i + 1]; + ++i; + } else if (strcmp("coupling", attrs[i]) == 0) { + coupling = attrs[i + 1]; + } + + ++i; + } + + if (connectivity != NULL) { + ALOGD("%s: connectivity = \"%s\"", __FUNCTION__, connectivity); + if (!strcmp(connectivity, "diff")) { + dev_cfg->connectivity = CONNECT_DIFF; + } else if (!strcmp(connectivity, "quasi_diff")) { + dev_cfg->connectivity = CONNECT_QUASI_DIFF; + } else if (!strcmp(connectivity, "single_ended")) { + dev_cfg->connectivity = CONNECT_SINGLE_ENDED; + } else { + dev_cfg->connectivity = CONNECT_UNKNOWN; + } + } else { + ALOGE("[device in board] failed to find the attribute connectivity"); + return; + } + + if (coupling != NULL) { + ALOGD("%s: coupling = \"%s\"", __FUNCTION__, coupling); + if (!strcmp(coupling, "ac")) { + dev_cfg->coupling = COUPLING_AC; + } else if (!strcmp(coupling, "dc")) { + dev_cfg->coupling = COUPLING_DC; + } else { + dev_cfg->coupling = COUPLING_UNKNOWN; + } + } else { + ALOGE("[device in board] failed to find the attribute coupling"); + return; + } + + config_data->current_board_device = dev_cfg; +} + +static void finishBoardDevice(struct platform_config_t *config_data, + char *device_name) { + ALOGD("%s: find board config device %s", __FUNCTION__, device_name); + + if (config_data->current_board_device != NULL) { + config_data->current_board_device->hw_dev = get_hwdev_byname(device_name); + + if (config_data->board_dev_cfg == NULL) { + config_data->board_dev_cfg = config_data->current_board_device; + } else { + struct board_dev_cfg_t *last_dev_cfg = config_data->board_dev_cfg; + while (last_dev_cfg->next) last_dev_cfg = last_dev_cfg->next; + last_dev_cfg->next = config_data->current_board_device; + } + config_data->current_board_device = NULL; + } else { + ALOGE("%s:%d current_board_device is null", __FUNCTION__, __LINE__); + } +} + +static void addAndroidDevice(struct platform_config_t *config_data, + const char **attrs) { + // Save Android Device information, support multi devices config + struct android_dev_cfg_t *droid_dev_cfg; + const char *android_dev = NULL; + + droid_dev_cfg = + (struct android_dev_cfg_t *)calloc(1, sizeof(struct android_dev_cfg_t)); + + if (droid_dev_cfg == NULL) { + ALOGE("%s/L%d: out of memory", __FUNCTION__, __LINE__); + return; + } + + if (strcmp("identifier", attrs[0]) == 0) { + android_dev = attrs[1]; + } + + if (android_dev != NULL) { + droid_dev_cfg->android_dev = get_android_dev_byname((char *)android_dev); + ALOGD("%s: find android dev identifier %s", __FUNCTION__, android_dev); + } else { + ALOGE( + "Can't find the identifier for AndroidDevice. attrs[0][%s], " + "attrs[1][%s]", + attrs[0], attrs[1]); + } + + // add android device config to list + if (config_data->droid_dev_cfg == NULL) { + config_data->droid_dev_cfg = droid_dev_cfg; + } else { + struct android_dev_cfg_t *last_path_cfg = config_data->droid_dev_cfg; + while (last_path_cfg->next) last_path_cfg = last_path_cfg->next; + last_path_cfg->next = droid_dev_cfg; + } + + config_data->current_droid_device = droid_dev_cfg; +} + +static void addAppToAndroidDevice(struct platform_config_t *config_data, + const char **attrs) { + const char *app_name = NULL; + struct app_cfg_t *app_cfg = NULL; + + app_cfg = (struct app_cfg_t *)calloc(1, sizeof(struct app_cfg_t)); + + if (app_cfg == NULL) { + ALOGE("%s/L%d: out of memory", __FUNCTION__, __LINE__); + return; + } + + if (strcmp("identifier", attrs[0]) == 0) { + app_name = attrs[1]; + } + + if (app_name != NULL) { + ALOGD("%s: find app config, identifier %s", __FUNCTION__, app_name); + app_cfg->v_mode = get_mode_byname((char *)app_name); + } else { + ALOGE( + "Can't find the identifier for Application. attrs[0][%s], attrs[1][%s]", + attrs[0], attrs[1]); + return; + } + + if (config_data->current_droid_device != NULL) { + if (config_data->current_droid_device->app_cfg == NULL) { + config_data->current_droid_device->app_cfg = app_cfg; + } else { + struct app_cfg_t *last_app_cfg = + config_data->current_droid_device->app_cfg; + while (last_app_cfg->next) last_app_cfg = last_app_cfg->next; + last_app_cfg->next = app_cfg; + } + } else { + ALOGE("%s:%d current_droid_device is null", __FUNCTION__, __LINE__); + return; + } + + config_data->current_app_cfg = app_cfg; +} + +static void finishAppDroidDevice(struct platform_config_t *config_data, + char *device_name) { + if (device_name != NULL) { + ALOGD("%s: find android device %s", __FUNCTION__, device_name); + config_data->current_app_cfg->device |= get_hwdev_byname(device_name); + } else { + ALOGE("%s: device name is NULL", __FUNCTION__); + } +} + +static void XMLCALL +startElementHandler(void *userData, const char *name, const char **attr) { + struct platform_config_t *config_data; + + config_data = (struct platform_config_t *)userData; + + if (strcmp("MarvellPlatformAudioConfiguration", name) == 0) { + current_section = SECTION_TOP_LEVEL; + } else if (strcmp("BoardDeviceList", name) == 0) { + if (current_section == SECTION_TOP_LEVEL) { + current_section = SECTION_BOARD_DEVICE_LIST; + } else { + ALOGE("Wrong section(%d) in parsing to section: BoardDeviceList", + current_section); + current_section = SECTION_UNKNOWN; + } + } else if (strcmp("AndroidDevice", name) == 0) { + if (current_section == SECTION_TOP_LEVEL) { + addAndroidDevice(config_data, attr); + current_section = SECTION_ANDROID_DEVICE; + } else { + ALOGE("Wrong section(%d) in parsing to section: AndroidDevice", + current_section); + current_section = SECTION_UNKNOWN; + } + } else if (strcmp("Application", name) == 0) { + if (current_section == SECTION_ANDROID_DEVICE) { + addAppToAndroidDevice(config_data, attr); + current_section = SECTION_APPLICATION; + } else { + ALOGE("Wrong section(%d) in parsing to section: Application", + current_section); + current_section = SECTION_UNKNOWN; + } + } else if (strcmp("Device", name) == 0) { + if (current_section == SECTION_BOARD_DEVICE_LIST) { + current_section = SECTION_DEVICE_IN_BOARD; + parseBoardDevice(config_data, attr); + } else if (current_section == SECTION_APPLICATION) { + current_section = SECTION_DEVICE_IN_ANDROID; + } + } else { + ALOGE("Wrong element(%s), current section is %d", name, current_section); + } +} + +static void XMLCALL endElementHandler(void *userData, const char *name) { + struct platform_config_t *config_data; + + config_data = (struct platform_config_t *)userData; + if (strcmp("Application", name) == 0) { + current_section = SECTION_ANDROID_DEVICE; + config_data->current_device_name + .name[config_data->current_device_name.len] = '\0'; + finishAppDroidDevice(config_data, config_data->current_device_name.name); + config_data->current_device_name.len = 0; + config_data->current_app_cfg = NULL; + } else if (strcmp("AndroidDevice", name) == 0) { + current_section = SECTION_TOP_LEVEL; + config_data->current_droid_device = NULL; + } else if (strcmp("BoardDeviceList", name) == 0) { + current_section = SECTION_TOP_LEVEL; + } else if (strcmp("Device", name) == 0) { + if (current_section == SECTION_DEVICE_IN_BOARD) { + current_section = SECTION_BOARD_DEVICE_LIST; + config_data->current_device_name + .name[config_data->current_device_name.len] = '\0'; + finishBoardDevice(config_data, config_data->current_device_name.name); + config_data->current_device_name.len = 0; + } else if (current_section == SECTION_DEVICE_IN_ANDROID) { + current_section = SECTION_APPLICATION; + } + } +} + +static void XMLCALL +characterDataHandler(void *userData, const XML_Char *s, int len) { + struct platform_config_t *config_data; + + config_data = (struct platform_config_t *)userData; + + if (current_section == SECTION_DEVICE_IN_BOARD || + current_section == SECTION_DEVICE_IN_ANDROID) { + strncpy(config_data->current_device_name.name + + config_data->current_device_name.len, + s, len); + config_data->current_device_name.len += len; + } +} + +int init_platform_config() { + char *config_file_name = AUDIO_PLATFORM_CONFIG_FILE; + XML_Parser parser; + FILE *file; + parsing_check_t parsing_check = PARSING_OK; + + file = fopen(config_file_name, "r"); + + if (file == NULL) { + ALOGE("unable to open platform audio configuration xml file: %s", + config_file_name); + return -1; + } else { + ALOGI("%s: config file %s", __FUNCTION__, config_file_name); + } + + mrvl_platform_cfg = + (struct platform_config_t *)calloc(1, sizeof(struct platform_config_t)); + + if (mrvl_platform_cfg == NULL) { + ALOGE("%s/L%d: out of memory", __FUNCTION__, __LINE__); + fclose(file); + return -1; + } + ALOGI( + "current_board_device = %p, current_droid_device=%p, device name len=%d", + mrvl_platform_cfg->current_board_device, + mrvl_platform_cfg->current_droid_device, + mrvl_platform_cfg->current_device_name.len); + + parser = XML_ParserCreate(NULL); + if (!parser) { + ALOGE("%s: Couldn't allocate memory for parser\n", __FUNCTION__); + return -1; + } + + XML_SetUserData(parser, mrvl_platform_cfg); + XML_SetElementHandler(parser, startElementHandler, endElementHandler); + XML_SetCharacterDataHandler(parser, characterDataHandler); + + mrvl_platform_cfg->current_device_name.len = 0; + + const int BUFF_SIZE = 512; + while (parsing_check == PARSING_OK) { + void *buff = XML_GetBuffer(parser, BUFF_SIZE); + if (buff == NULL) { + ALOGE("failed in call to XML_GetBuffer()"); + parsing_check = PARSING_UNKNOWN_ERROR; + break; + } + + int bytes_read = fread(buff, 1, BUFF_SIZE, file); + if (bytes_read < 0) { + ALOGE("failed in call to read"); + parsing_check = PARSING_ERROR_IO; + break; + } + + enum XML_Status status = + XML_ParseBuffer(parser, bytes_read, bytes_read == 0); + if (status != XML_STATUS_OK) { + ALOGE("malformed (%s)", XML_ErrorString(XML_GetErrorCode(parser))); + parsing_check = PARSING_ERROR_MALFORMED; + break; + } + + if (bytes_read == 0) { + break; + } + } + + XML_ParserFree(parser); + + fclose(file); + file = NULL; + + ALOGI("%s: config file %s finished parsing", __FUNCTION__, config_file_name); + + return 0; +} + +// free global platform configuration +void deinit_platform_config() { + struct android_dev_cfg_t *tmp_droiddev_cfg = NULL; + struct board_dev_cfg_t *tmp_board_dev_cfg = NULL; + + if (mrvl_platform_cfg == NULL) { + ALOGE("%s: mrvl_platform_cfg is not initialized.", __FUNCTION__); + return; + } + + tmp_board_dev_cfg = mrvl_platform_cfg->board_dev_cfg; + while (tmp_board_dev_cfg) { + mrvl_platform_cfg->board_dev_cfg = mrvl_platform_cfg->board_dev_cfg->next; + free(tmp_board_dev_cfg); + tmp_board_dev_cfg = mrvl_platform_cfg->board_dev_cfg; + } + + tmp_droiddev_cfg = mrvl_platform_cfg->droid_dev_cfg; + while (tmp_droiddev_cfg) { + struct app_cfg_t *tmp_app_cfg = tmp_droiddev_cfg->app_cfg; + while (tmp_app_cfg) { + tmp_droiddev_cfg->app_cfg = tmp_droiddev_cfg->app_cfg->next; + free(tmp_app_cfg); + tmp_app_cfg = tmp_droiddev_cfg->app_cfg; + } + mrvl_platform_cfg->droid_dev_cfg = mrvl_platform_cfg->droid_dev_cfg->next; + free(tmp_droiddev_cfg); + tmp_droiddev_cfg = mrvl_platform_cfg->droid_dev_cfg; + } + + free(mrvl_platform_cfg); + mrvl_platform_cfg = NULL; +} diff --git a/peripheral/audio/driver/audio_effect_mrvl.c b/peripheral/audio/driver/audio_effect_mrvl.c new file mode 100644 index 0000000..74f8830 --- /dev/null +++ b/peripheral/audio/driver/audio_effect_mrvl.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "audio_effect_mrvl" +#define LOG_NDEBUG 0 + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <math.h> + +#include <cutils/log.h> +#include <cutils/str_parms.h> +#include <cutils/list.h> +#include <cutils/properties.h> + +#include <hardware/hardware.h> +#include <hardware/audio.h> +#include <system/audio.h> + +#include "audio_effect_mrvl.h" + +#define RAMP_DOWN_GAIN_INITIAL_VALUE 1 +#define RAMP_UP_GAIN_INITIAL_VALUE 0.0001 + +static bool ramp_up_begin = false; +static bool ramp_down_begin = false; +static double ramp_gain_value = RAMP_UP_GAIN_INITIAL_VALUE; +// the ramp up gain will increase from 0.0001 to 1 in 80ms(80*44.1*4 bytes), +// 120ms(120*44.1*4 bytes) +static int ramp_up_level_table[] = {80, 120}; +// the ramp down gain will decrease from 1 to 0.0001 in 5ms(5*44.1*4 bytes), +// 10ms(10*44.1*4 bytes), 15ms(15*44.1*4 bytes) +static int ramp_down_level_table[] = {5, 10, 15}; +// currently, we can only set the ramp level once, if we want to change the ramp +// level by setting it manually, we need kill +// mediaserver after setting the property or reboot then reset the ramp level +// according to property. ramp ratio is +// calculated by g(n)=g(1)*q^(n-1), q=pow(g(n)/g(1), 1.0/(n-1)), n is num of the +// samples, for example(n = 10ms * 44.1) +static unsigned char ramp_up_level = 0xFF; +static unsigned char ramp_down_level = 0xFF; +static double ramp_up_ratio = 0.0; +static double ramp_down_ratio = 0.0; + +#define RAMP_UP_LEVEL_NUM \ + (sizeof(ramp_up_level_table) / sizeof(ramp_up_level_table[0])) +#define RAMP_DOWN_LEVEL_NUM \ + (sizeof(ramp_down_level_table) / sizeof(ramp_down_level_table[0])) + +unsigned char get_ap_rampdown_level() { + char prop_value[PROPERTY_VALUE_MAX]; + unsigned char level = 0; + if (property_get("audio.approcess.rampdown.level", prop_value, "1")) { + int value = atoi(prop_value); + if (value >= 0 && (unsigned int)value < RAMP_DOWN_LEVEL_NUM) { + level = (unsigned char)value; + } else { + ALOGW("%s: Invalid ramp down level %d, current only support 0 ~ 2", + __FUNCTION__, value); + } + } + return level; +} + +unsigned char get_ap_rampup_level() { + char prop_value[PROPERTY_VALUE_MAX]; + unsigned char level = 0; + if (property_get("audio.approcess.rampup.level", prop_value, "0")) { + int value = atoi(prop_value); + if (value >= 0 && (unsigned int)value < RAMP_UP_LEVEL_NUM) { + level = (unsigned char)value; + } else { + ALOGW("%s: Invalid ramp up level %d, current only support 0 ~ 1", + __FUNCTION__, value); + } + } + return level; +} + +// force set the ramp_up_gain value to RAMP_UP_GAIN_INITIAL_VALUE if ramp up is +// triggered +void ramp_up_start(unsigned int sample_rate) { + ramp_down_begin = false; + ramp_up_begin = true; + ramp_gain_value = RAMP_UP_GAIN_INITIAL_VALUE; + if (ramp_up_level == 0xFF) { + ramp_up_level = get_ap_rampup_level(); + ramp_up_ratio = + pow((double)(RAMP_DOWN_GAIN_INITIAL_VALUE / RAMP_UP_GAIN_INITIAL_VALUE), + (1.0 * 1000) / + (ramp_up_level_table[ramp_up_level] * sample_rate - 1000)); + } +} + +// ramp up has priority to ramp down, if ramp up has been triggered and not +// finished, then ignore the ramp down action +// ignore latter ramp down trigger action if the previous ramp down is still +// on-going +void ramp_down_start(unsigned int sample_rate) { + if (!ramp_down_begin && !ramp_up_begin) { + ramp_down_begin = true; + ramp_up_begin = false; + ramp_gain_value = RAMP_DOWN_GAIN_INITIAL_VALUE; + } + if (ramp_down_level == 0xFF) { + ramp_down_level = get_ap_rampdown_level(); + ramp_down_ratio = + pow((double)(RAMP_UP_GAIN_INITIAL_VALUE / RAMP_DOWN_GAIN_INITIAL_VALUE), + (1.0 * 1000) / + (ramp_down_level_table[ramp_down_level] * sample_rate - 1000)); + } +} + +void ramp_process(void *buffer, size_t bytes) { + unsigned int i = 0; + short *buf = (short *)buffer; + + if (!ramp_down_begin && !ramp_up_begin) { + return; + } + + if (ramp_down_begin) { + for (i = 0; i < bytes / (sizeof(short) * 2); i++) { + buf[2 * i] = (short)((float)buf[2 * i] * ramp_gain_value); + buf[2 * i + 1] = (short)((float)buf[2 * i + 1] * ramp_gain_value); + ramp_gain_value *= ramp_down_ratio; + if (RAMP_UP_GAIN_INITIAL_VALUE > ramp_gain_value) { + memset(buf + 2 * (i + 1), 0, bytes - 2 * (i + 1) * 2); + ramp_down_begin = false; + // FIXME because there is a lock between path operation and in read + // thread, + // we can only trigger one ramp action, if ramp down is triggered, ramp + // up + // will be triggered automatically after the ramp down finished. + ramp_up_begin = true; + ramp_gain_value = RAMP_UP_GAIN_INITIAL_VALUE; + return; + } + } + } + if (ramp_up_begin) { + for (i = 0; i < bytes / (sizeof(short) * 2); i++) { + buf[2 * i] = (short)((float)buf[2 * i] * ramp_gain_value); + buf[2 * i + 1] = (short)((float)buf[2 * i + 1] * ramp_gain_value); + ramp_gain_value *= ramp_up_ratio; + if (ramp_gain_value > RAMP_DOWN_GAIN_INITIAL_VALUE) { + ramp_up_begin = false; + return; + } + } + } +} diff --git a/peripheral/audio/driver/audio_effect_mrvl.h b/peripheral/audio/driver/audio_effect_mrvl.h new file mode 100644 index 0000000..84e3332 --- /dev/null +++ b/peripheral/audio/driver/audio_effect_mrvl.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __MRVL_AUDIO_EFFECT_H__ +#define __MRVL_AUDIO_EFFECT_H__ + +void ramp_up_start(unsigned int sample_rate); +void ramp_down_start(unsigned int sample_rate); +void ramp_process(void *buffer, size_t bytes); + +#endif /* __MRVL_AUDIO_EFFECT_H__ */ diff --git a/peripheral/audio/driver/audio_hw_mrvl.c b/peripheral/audio/driver/audio_hw_mrvl.c new file mode 100755 index 0000000..83daf2a --- /dev/null +++ b/peripheral/audio/driver/audio_hw_mrvl.c @@ -0,0 +1,3279 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "audio_hw_mrvl" +#define LOG_NDEBUG 0 + +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <cutils/log.h> +#include <cutils/str_parms.h> +#include <cutils/list.h> +#include <cutils/properties.h> + +#include <hardware/hardware.h> +#include <system/audio.h> + +#include "audio_hw_mrvl.h" +#include "acm_api.h" + +#include "audio_effect_mrvl.h" + +// it should be accessed in protection of madev->lock +static struct mrvl_path_status mrvl_path_manager; + +// ---------------------------------------------------------------------- +// pcm_config used for tiny alsa device open +// ---------------------------------------------------------------------- +// used for low latency output +static struct pcm_config pcm_config_ll = { + .channels = 2, + .rate = SAMPLE_RATE_OUT_DEFAULT, + .format = PCM_FORMAT_S16_LE, + .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE, + .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT, + .start_threshold = + LOW_LATENCY_OUTPUT_PERIOD_SIZE * (LOW_LATENCY_OUTPUT_PERIOD_COUNT - 1), + .stop_threshold = + LOW_LATENCY_OUTPUT_PERIOD_SIZE * LOW_LATENCY_OUTPUT_PERIOD_COUNT, +}; +// used for deep buffer output +static struct pcm_config pcm_config_db = { + .channels = 2, + .rate = SAMPLE_RATE_OUT_DEFAULT, + .format = PCM_FORMAT_S16_LE, + .period_size = DEEP_BUFFER_LONG_PERIOD_SIZE, + .period_count = DEEP_BUFFER_LONG_PERIOD_COUNT, + .start_threshold = + DEEP_BUFFER_SHORT_PERIOD_SIZE * (DEEP_BUFFER_SHORT_PERIOD_COUNT - 1), + .avail_min = DEEP_BUFFER_LONG_PERIOD_SIZE, +}; +// used for phone output +static struct pcm_config pcm_config_phone = { + .channels = 2, + .rate = SAMPLE_RATE_PHONE, + .format = PCM_FORMAT_S16_LE, + .period_size = PHONE_OUTPUT_PERIOD_SIZE, + .period_count = PHONE_OUTPUT_PERIOD_COUNT, +}; +// used for fm output +static struct pcm_config pcm_config_fm = { + .channels = 2, + .rate = SAMPLE_RATE_OUT_FM, + .format = PCM_FORMAT_S16_LE, + .period_size = FM_OUTPUT_PERIOD_SIZE, + .period_count = FM_OUTPUT_PERIOD_COUNT, +}; +// used for default input +static struct pcm_config pcm_config_input = { + .channels = 2, + .rate = SAMPLE_RATE_IN_DEFAULT, + .format = PCM_FORMAT_S16_LE, + .period_size = LOW_LATENCY_INPUT_PERIOD_SIZE, + .period_count = LOW_LATENCY_INPUT_PERIOD_COUNT, + .start_threshold = 1, + .stop_threshold = + LOW_LATENCY_INPUT_PERIOD_SIZE * LOW_LATENCY_INPUT_PERIOD_COUNT, +}; + +// output modes, descending order by +// priority(VC>VOIP/VT>FM>DeepBuffer>LowLatency) +static struct vtrl_mode_priority_cfg priority_modes_output[] = { + {ID_IPATH_RX_VC, AUDIO_MODE_IN_CALL, V_MODE_VC}, + {ID_IPATH_RX_VC, AUDIO_MODE_NORMAL, V_MODE_VC}, + {ID_IPATH_RX_VC_ST, AUDIO_MODE_NORMAL, V_MODE_VC}, + {ID_IPATH_RX_HIFI_LL, AUDIO_MODE_IN_COMMUNICATION, V_MODE_VOIP}, + {ID_IPATH_RX_HIFI_LL, AUDIO_MODE_IN_VT_CALL, V_MODE_VT}, + {ID_IPATH_RX_FM, AUDIO_MODE_NORMAL, V_MODE_FM}, + {ID_IPATH_RX_HFP, AUDIO_MODE_IN_CALL, V_MODE_HFP}, + {ID_IPATH_RX_HIFI_DB, AUDIO_MODE_NORMAL, V_MODE_HIFI_DB}, + {ID_IPATH_RX_HIFI_LL, AUDIO_MODE_NORMAL, V_MODE_HIFI_LL}}; + +// input modes, descending order by priority(currently it can not support DB +// recording) +static struct vtrl_mode_priority_cfg priority_modes_input[] = { + {ID_IPATH_TX_VC, AUDIO_MODE_IN_CALL, V_MODE_VC}, + {ID_IPATH_TX_HIFI, AUDIO_MODE_IN_COMMUNICATION, V_MODE_VOIP}, + {ID_IPATH_TX_HIFI, AUDIO_MODE_IN_VT_CALL, V_MODE_VT}, + {ID_IPATH_TX_HFP, AUDIO_MODE_IN_CALL, V_MODE_HFP}, + {ID_IPATH_TX_FM, AUDIO_MODE_NORMAL, V_MODE_FM}, + {ID_IPATH_TX_HIFI, AUDIO_MODE_NORMAL, V_MODE_HIFI_LL}}; + +// virtual modes num definition for output and input +#define NUM_VTRL_MODES_OUTPUT \ + (sizeof(priority_modes_output) / sizeof(priority_modes_output[0])) +#define NUM_VTRL_MODES_INPUT \ + (sizeof(priority_modes_input) / sizeof(priority_modes_input[0])) + +static enum BT_STATUS_UPDATE { + BT_UPDATE_NONE = 0x0, + BT_UPDATE_NREC_MODE = 0x1, + BT_UPDATE_HEADSET_TYPE = 0x2 +}BT_STATUS_UPDATE; + +// ---------------------------------------------------------------------- +// Internal Helper Functions +// ---------------------------------------------------------------------- +static uint32_t out_get_sample_rate(const struct audio_stream *stream); +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream); +static audio_format_t out_get_format(const struct audio_stream *stream); + +uint32_t get_mic_mode(void) { return mrvl_path_manager.mic_mode; } + +unsigned char get_cpmute_rampdown_level() { + char prop_value[PROPERTY_VALUE_MAX]; + unsigned char level = 0; + if (property_get("audio.cpmute.rampdown.level", prop_value, "0")) { + int value = atoi(prop_value); + if (value >= 0 && value < 3) { + level = (unsigned char)value; + } else { + ALOGW("%s: Invalid ramp down level %d, current only support 0 ~ 2", + __FUNCTION__, value); + } + } + return level; +} + +unsigned char get_cpmute_rampup_level() { + char prop_value[PROPERTY_VALUE_MAX]; + unsigned char level = 0; + if (property_get("audio.cpmute.rampup.level", prop_value, "0")) { + int value = atoi(prop_value); + if (value >= 0 && value < 2) { + level = (unsigned char)value; + } else { + ALOGW("%s: Invalid ramp up level %d, current only support 0 ~ 1", + __FUNCTION__, value); + } + } + return level; +} + +static bool is_in_voip_vt(audio_mode_t mode) { + return (AUDIO_MODE_IN_COMMUNICATION == mode || AUDIO_MODE_IN_VT_CALL == mode); +} + +void add_vtrl_mode(struct listnode *mode_list, virtual_mode_t v_mode, + bool is_priority_highest) { + struct virtual_mode *vtrl_mode = calloc(1, sizeof(struct virtual_mode)); + if (vtrl_mode == NULL) { + return; + } + + vtrl_mode->v_mode = v_mode; + vtrl_mode->is_priority_highest = is_priority_highest; + list_add_tail(mode_list, &vtrl_mode->link); +} + +// get current active virtual modes for RX direction +// the priority of virtual modes is VC>VOIP/VT>FM>DeepBuffer>LowLatency +// it supports three modes concurrent (current it supports +// VC/DeepBuffer/LowLatency) +// each one of VC/FM/DeepBuffer modes can be concurrent with LowLatency +// VOIP/VT can only be concurrent with DeepBuffer +void get_out_vrtl_mode(struct mrvl_audio_device *madev, + struct listnode *active_mode_list) { + int i = 0; + bool is_highest = true; + virtual_mode_t highest_mode = V_MODE_INVALID; + // the priorities of modes are sorted by descending order in array + // priority_modes[] + for (i = 0; i < (int)NUM_VTRL_MODES_OUTPUT; i++) { + if (mrvl_path_manager.itf_state[priority_modes_output[i].path_interface]) { + // VOIP/VT and LowLatency have a common interface, but audio mode is + // different + if (priority_modes_output[i].path_interface == ID_IPATH_RX_HIFI_LL) { + if (((priority_modes_output[i].audio_mode == + AUDIO_MODE_IN_COMMUNICATION) || + (priority_modes_output[i].audio_mode == AUDIO_MODE_IN_VT_CALL)) && + (priority_modes_output[i].audio_mode != madev->mode)) { + continue; + } + } + // LowLatency could not exist with VOIP/VT simultaneously + if ((priority_modes_output[i].v_mode == V_MODE_HIFI_LL) && + ((highest_mode == V_MODE_VOIP) || (highest_mode == V_MODE_VT))) { + continue; + } + // first mode we get from the array is the highest mode, the left are + // non-highest + add_vtrl_mode(active_mode_list, priority_modes_output[i].v_mode, + is_highest); + if (highest_mode == V_MODE_INVALID) { + highest_mode = priority_modes_output[i].v_mode; + is_highest = false; + } + } + } +} + +// get current active virtual mode for TX direction +void get_in_vrtl_mode(struct mrvl_audio_device *madev, virtual_mode_t *v_mode) { + int i = 0; + // the priorities of modes are sorted by descending order in array + // priority_modes[] + for (i = 0; i < (int)NUM_VTRL_MODES_INPUT; i++) { + if (mrvl_path_manager.itf_state[priority_modes_input[i].path_interface]) { + if ((priority_modes_input[i].path_interface == ID_IPATH_TX_HIFI) && + (priority_modes_input[i].audio_mode != madev->mode)) { + continue; + } + *v_mode = priority_modes_input[i].v_mode; + break; + } + } + + if (madev->loopback_param.on) { + if (madev->loopback_param.type == CP_LOOPBACK) { + *v_mode = V_MODE_CP_LOOPBACK; + } else if (madev->loopback_param.type == HARDWARE_LOOPBACK) { + *v_mode = V_MODE_HW_LOOPBACK; + } else if (madev->loopback_param.type == APP_LOOPBACK) { + *v_mode = V_MODE_APP_LOOPBACK; + } + } +} + +// this function returns the hw device, takes into account the tty mode. +unsigned int get_headset_hw_device(struct mrvl_audio_device *madev, + unsigned int android_dev) { + unsigned int hardware_dev = HWDEV_INVALID; + + switch (android_dev) { + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + switch (madev->tty_mode) { + case TTY_MODE_FULL: + case TTY_MODE_VCO: + hardware_dev = HWDEV_OUT_TTY; + break; + case TTY_MODE_HCO: // HCO output device handled in convert2_hwdev() + case TTY_MODE_OFF: + default: + hardware_dev = HWDEV_HEADPHONE; + break; + } + break; + case AUDIO_DEVICE_IN_WIRED_HEADSET: + switch (madev->tty_mode) { + case TTY_MODE_FULL: + case TTY_MODE_HCO: + hardware_dev = HWDEV_IN_TTY; + break; + case TTY_MODE_VCO: { + virtual_mode_t v_mode = V_MODE_INVALID; + + // get current active virtual mode + get_in_vrtl_mode(madev, + &v_mode); // in TTY mode, it MUST be V_MODE_VC + switch (get_mic_dev(v_mode, AUDIO_DEVICE_IN_BUILTIN_MIC)) { + case HWDEV_DUAL_AMIC: + hardware_dev = HWDEV_IN_TTY_VCO_DUAL_AMIC; + break; + case HWDEV_DUAL_AMIC_SPK_MODE: + hardware_dev = HWDEV_IN_TTY_VCO_DUAL_AMIC_SPK_MODE; + break; + case HWDEV_DUAL_DMIC1: + hardware_dev = HWDEV_IN_TTY_VCO_DUAL_DMIC1; + break; + case HWDEV_AMIC1: + hardware_dev = HWDEV_IN_TTY_VCO_AMIC1; + break; + case HWDEV_AMIC2: + hardware_dev = HWDEV_IN_TTY_VCO_AMIC2; + break; + default: + break; + } + } break; + case TTY_MODE_OFF: + default: + hardware_dev = HWDEV_HSMIC; + break; + } + break; + default: + // hardware_dev remains HWDEV_INVALID; + break; + } + + return hardware_dev; +} + +// this function returns the tty param to be used for getting VCM Profile. +static unsigned int get_tty_param(struct mrvl_audio_device *madev) { + unsigned int tty_param; + + switch (madev->tty_mode) { + case TTY_MODE_FULL: + tty_param = VCM_TTY_FULL; + break; + case TTY_MODE_HCO: + tty_param = VCM_TTY_HCO; + break; + case TTY_MODE_VCO: { + virtual_mode_t v_mode; + // get current active virtual mode + get_in_vrtl_mode(madev, &v_mode); // in TTY mode, it MUST be V_MODE_VC + if ((get_mic_dev(v_mode, AUDIO_DEVICE_IN_BUILTIN_MIC) == + HWDEV_DUAL_AMIC) || + (get_mic_dev(v_mode, AUDIO_DEVICE_IN_BUILTIN_MIC) == + HWDEV_DUAL_AMIC_SPK_MODE) || + (get_mic_dev(v_mode, AUDIO_DEVICE_IN_BUILTIN_MIC) == + HWDEV_DUAL_DMIC1)) + tty_param = VCM_TTY_VCO_DUALMIC; + else + tty_param = VCM_TTY_VCO; + break; + } + case TTY_MODE_OFF: + default: + tty_param = 0; + break; + } + + return tty_param; +} + +// this function returns the call param to be used for getting VCM Profile. +static unsigned int get_call_params(struct mrvl_audio_device *madev) { + unsigned int params = 0; + + if (madev->use_extra_vol) { + params |= VCM_EXTRA_VOL; + } + if (madev->bt_headset_type == BT_WB) { + params |= VCM_BT_WB; + } + // check whether BT enables NREC or not. + if (madev->use_sw_nrec) { + params |= VCM_BT_NREC_OFF; + } + if (mrvl_path_manager.enabled_in_hwdev & ~HWDEV_BIT_IN & + (HWDEV_DUAL_AMIC | HWDEV_DUAL_AMIC_SPK_MODE | HWDEV_DUAL_DMIC1)) { + params |= VCM_DUAL_MIC; + } + params |= get_tty_param(madev); + + return params; +} + +// convert an android device to hardware device to control ACM +unsigned int convert2_hwdev(struct mrvl_audio_device *madev, + unsigned int android_dev) { + unsigned int hardware_dev = HWDEV_INVALID; + switch (android_dev) { + case AUDIO_DEVICE_OUT_EARPIECE: + hardware_dev = HWDEV_EARPIECE; + break; + case AUDIO_DEVICE_OUT_SPEAKER: +#ifdef WITH_STEREO_SPKR + hardware_dev = (madev->tty_mode == TTY_MODE_HCO) + ? HWDEV_OUT_TTY_HCO_STEREOSPEAKER + : HWDEV_STEREOSPEAKER; +#else + hardware_dev = (madev->tty_mode == TTY_MODE_HCO) + ? HWDEV_OUT_TTY_HCO_SPEAKER + : HWDEV_SPEAKER; +#endif + break; + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + hardware_dev = (madev->bt_headset_type == BT_WB) + ? ((madev->use_sw_nrec) ? HWDEV_BT_NREC_OFF_WB + : HWDEV_BLUETOOTH_WB) + : ((madev->use_sw_nrec) ? HWDEV_BT_NREC_OFF_NB + : HWDEV_BLUETOOTH_NB); + break; + case AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET: + case AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + hardware_dev = HWDEV_HEADPHONE; + break; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + hardware_dev = get_headset_hw_device(madev, android_dev); + break; + case AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE: +#ifdef WITH_STEREO_SPKR + hardware_dev = HWDEV_STEREOSPEAKER | HWDEV_HEADPHONE; +#else + hardware_dev = HWDEV_SPEAKER | HWDEV_HEADPHONE; +#endif + break; + case AUDIO_DEVICE_IN_BUILTIN_MIC: + case AUDIO_DEVICE_IN_BACK_MIC: { + virtual_mode_t v_mode = V_MODE_INVALID; + + // get current active virtual mode + get_in_vrtl_mode(madev, &v_mode); + hardware_dev = get_mic_dev(v_mode, android_dev); + + // in case of Headphone (3-pole headset), change the input (mic) hw device + if (madev->out_device == AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + switch (hardware_dev) { + case HWDEV_AMIC1: + case HWDEV_AMIC1_SPK_MODE: + hardware_dev = HWDEV_AMIC1_HP_MODE; + break; + case HWDEV_AMIC2: + case HWDEV_AMIC2_SPK_MODE: + hardware_dev = HWDEV_AMIC2_HP_MODE; + break; + case HWDEV_DUAL_AMIC: + case HWDEV_DUAL_AMIC_SPK_MODE: + hardware_dev = HWDEV_DUAL_AMIC_HP_MODE; + break; + default: + break; + } + } + break; + } + case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET: + hardware_dev = (madev->bt_headset_type == BT_WB) + ? ((madev->use_sw_nrec) ? HWDEV_BTMIC_NREC_OFF_WB + : HWDEV_BTMIC_WB) + : ((madev->use_sw_nrec) ? HWDEV_BTMIC_NREC_OFF_NB + : HWDEV_BTMIC_NB); + break; + case AUDIO_DEVICE_IN_WIRED_HEADSET: + hardware_dev = get_headset_hw_device(madev, android_dev); + break; + default: + ALOGI("%s: Invalid input of audio device 0x%x", __FUNCTION__, + android_dev); + hardware_dev = HWDEV_INVALID; + break; + } + return hardware_dev; +} + +int add_vrtl_path(struct listnode *path_node, struct virtual_mode *vtrl_mode, + unsigned device) { + struct virtual_path *v_path = calloc(1, sizeof(struct virtual_path)); + if (v_path == NULL) { + return -1; + } + + v_path->v_mode = vtrl_mode->v_mode; + v_path->is_priority_highest = vtrl_mode->is_priority_highest; + v_path->path_device = device; + list_add_tail(path_node, &v_path->link); + + return 0; +} + +// select corresponding input device by output device +unsigned int get_input_dev(unsigned int out_device) { + unsigned int input_dev = AUDIO_DEVICE_IN_BUILTIN_MIC; + + switch (out_device) { + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + input_dev = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + break; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + input_dev = AUDIO_DEVICE_IN_WIRED_HEADSET; + break; + case AUDIO_DEVICE_OUT_SPEAKER: + input_dev = AUDIO_DEVICE_IN_BACK_MIC; + break; + default: + input_dev = AUDIO_DEVICE_IN_BUILTIN_MIC; + break; + } + + return input_dev; +} + +// check whether has active output interface +static bool has_active_out_itf(void) { + int i; + bool has_active_itf = false; + + for (i = ID_IPATH_RX_MIN; i <= ID_IPATH_RX_MAX; i++) { + if (mrvl_path_manager.itf_state[i] == true) { + has_active_itf = true; + break; + } + } + return has_active_itf; +} + +// check whether has active input interface +static bool has_active_in_itf(void) { + int i; + bool has_active_itf = false; + + for (i = ID_IPATH_TX_MIN; i <= ID_IPATH_TX_MAX; i++) { + if (mrvl_path_manager.itf_state[i] == true) { + has_active_itf = true; + break; + } + } + return has_active_itf; +} + +void get_active_vrtl_path(struct mrvl_audio_device *madev, + struct listnode *path_node) { + ALOGD("%s: out device 0x%x, in device 0x%x", __FUNCTION__, + mrvl_path_manager.active_out_device, + mrvl_path_manager.active_in_device); + + // check current active output virtual path, maybe two or more active paths + if (mrvl_path_manager.active_out_device && !mrvl_path_manager.mute_all_rx) { + struct listnode out_mode_list; + list_init(&out_mode_list); + // get all active modes of output + get_out_vrtl_mode(madev, &out_mode_list); + // add output virtual path + struct listnode *plist = NULL; + struct listnode *tmp_node = NULL; + struct virtual_mode *tmp_vmode = NULL; + list_for_each_safe(plist, tmp_node, &out_mode_list) { + tmp_vmode = node_to_item(plist, struct virtual_mode, link); + add_vrtl_path(path_node, tmp_vmode, mrvl_path_manager.active_out_device); + list_remove(&tmp_vmode->link); + free(tmp_vmode); + tmp_vmode = NULL; + } + } + + // check current active input virtual path, only support single now + if (mrvl_path_manager.active_in_device) { + virtual_mode_t v_mode = V_MODE_INVALID; + struct virtual_mode temp_vmode; + get_in_vrtl_mode(madev, &v_mode); + // add input virtual path + temp_vmode.v_mode = v_mode; + temp_vmode.is_priority_highest = true; + add_vrtl_path(path_node, &temp_vmode, mrvl_path_manager.active_in_device); + } +} + +void select_virtual_path(struct mrvl_audio_device *madev) { + unsigned int flag = 0; + unsigned int value = 0; + struct listnode active_v_path; + list_init(&active_v_path); + + ALOGD("%s enter", __FUNCTION__); + + // get current active virtual paths + get_active_vrtl_path(madev, &active_v_path); + + // remove old inactive virtual paths + { + struct listnode *plist = NULL; + struct listnode *tmp_node = NULL; + struct virtual_path *tmp_vpath = NULL; + list_for_each_safe(plist, tmp_node, &mrvl_path_manager.out_virtual_path) { + tmp_vpath = node_to_item(plist, struct virtual_path, link); + bool hasItem = false; + flag = 0; + if (tmp_vpath != NULL) { + struct listnode *plist_new = NULL; + struct listnode *tmp_node_new = NULL; + struct virtual_path *tmp_new_vpath = NULL; + list_for_each_safe(plist_new, tmp_node_new, &active_v_path) { + tmp_new_vpath = node_to_item(plist_new, struct virtual_path, link); + if ((tmp_new_vpath != NULL) && + (tmp_new_vpath->v_mode == tmp_vpath->v_mode) && + (tmp_new_vpath->path_device == tmp_vpath->path_device)) { + if (tmp_new_vpath->is_priority_highest && + (!tmp_vpath->is_priority_highest)) { + // the same path, but now its priority asends to the highest, + // should re-enable the new path + break; + } else if ((!tmp_new_vpath->is_priority_highest) && + tmp_vpath->is_priority_highest) { + // the same path, but now its priority desends to non-highest, + // just change the old path priority to non-highest + tmp_vpath->is_priority_highest = false; + } + hasItem = true; + break; + } + } + + // can not find same item in new virtual list, then remove + if (!hasItem) { + // voice recognition + flag |= (audio_is_input_device(tmp_vpath->path_device) && + madev->use_voice_recognition) + ? RECOGNITION + : 0; + // call path manager to disable this virtual path + unsigned int hw_dev = convert2_hwdev(madev, tmp_vpath->path_device); + if (tmp_vpath->path_device & AUDIO_DEVICE_BIT_IN) { + hw_dev = mrvl_path_manager.enabled_in_hwdev; + } + route_vrtl_path(tmp_vpath->v_mode, hw_dev, METHOD_DISABLE, flag, + value); + list_remove(&tmp_vpath->link); + free(tmp_vpath); + tmp_vpath = NULL; + } + } + } + } + + // add new active virtual path to list, and destory the tmp lists + { + struct listnode *plist = NULL; + struct listnode *tmp_node = NULL; + struct virtual_path *tmp_vpath = NULL; + list_for_each_safe(plist, tmp_node, &active_v_path) { + tmp_vpath = node_to_item(plist, struct virtual_path, link); + bool hasItem = false; + flag = 0; + value = 0; + if (tmp_vpath != NULL) { + struct listnode *plist_old = NULL; + struct listnode *tmp_node_old = NULL; + struct virtual_path *tmp_old_vpath = NULL; + list_for_each_safe(plist_old, tmp_node_old, + &mrvl_path_manager.out_virtual_path) { + tmp_old_vpath = node_to_item(plist_old, struct virtual_path, link); + if ((tmp_old_vpath != NULL) && + (tmp_old_vpath->v_mode == tmp_vpath->v_mode) && + (tmp_old_vpath->path_device == tmp_vpath->path_device)) { + hasItem = true; + break; + } + } + + list_remove(&tmp_vpath->link); + // can not find same item in old virtual list, then enable and add it + if (!hasItem) { + // voice recognition + flag |= (audio_is_input_device(tmp_vpath->path_device) && + madev->use_voice_recognition) + ? RECOGNITION + : 0; + // call path manager to enable this virtual path + unsigned int hw_dev = convert2_hwdev(madev, tmp_vpath->path_device); + + // if it is not the highest path ,SHARED_GAIN flag will ignore + // register + // configuration which has register misc "shared" in virtual path xml + if (tmp_vpath->is_priority_highest == false) value |= SHARED_GAIN; + + route_vrtl_path(tmp_vpath->v_mode, hw_dev, METHOD_ENABLE, flag, + value); + list_add_tail(&mrvl_path_manager.out_virtual_path, &tmp_vpath->link); + } else { + free(tmp_vpath); + tmp_vpath = NULL; + } + } + } + } +} + +// audio profile related functions +uint32_t get_profile_flag(struct mrvl_audio_device *madev) { + uint32_t flag = 0; + + if (mrvl_path_manager.active_out_device & AUDIO_DEVICE_OUT_ALL_SCO) { + // set correponding flag bits if feature is on + if (madev->bt_headset_type == BT_WB) { + flag |= PROFILE_BT_WB; + } + // set BT NREC profile: + // NREC_OFF: BT headset doesn't enable NREC. + if (madev->use_sw_nrec) { + flag |= PROFILE_BT_NREC_OFF; + } + } + if (madev->use_extra_vol) { + flag |= PROFILE_EXTRA_VOL; + } + if (mrvl_path_manager.enabled_in_hwdev & ~HWDEV_BIT_IN & + (HWDEV_DUAL_AMIC | HWDEV_DUAL_AMIC_SPK_MODE | HWDEV_DUAL_DMIC1)) { + flag |= PROFILE_DUAL_MIC; + } + + return flag; +} + +uint32_t get_profile(struct mrvl_audio_device *madev) { + int i = 0; + uint32_t flag = get_profile_flag(madev); + + // find corresponding profile + for (i = 0; i < (int)MAX_NUM_PROFILE; i++) { + if ((gProfile[i].v_mode == madev->mode) && + (gProfile[i].out_device == mrvl_path_manager.active_out_device) && + ((gProfile[i].in_device == 0) || + (gProfile[i].in_device == mrvl_path_manager.active_in_device)) && + (gProfile[i].flag == flag)) { + return STR2ENUM_PROFILE(gProfile[i].profile); + } + } + + // the information will be recorded if getting the invalid profile + ALOGE("%s, the current status is, mode %d, active_out_device 0x%x, flag 0x%x", + __FUNCTION__, madev->mode, mrvl_path_manager.active_out_device, flag); + return AUDIO_PROFILE_ID_ENUM_32_BIT; +} + +void set_voice_call_volume(struct mrvl_audio_device *madev, float volume, + bool use_extra_vol) { + signed char input_gain = 0; + signed char input_gain_wb = 0; + signed char output_gain = 0; + signed char output_gain_wb = 0; + unsigned char vc_output_vol = volume * 100; + + // get MSA gain from ACM XML, fix Mic volume to 100 + get_msa_gain(convert2_hwdev(madev, madev->in_device), 100, &input_gain, + &input_gain_wb, use_extra_vol); + get_msa_gain(convert2_hwdev(madev, madev->out_device), vc_output_vol, + &output_gain, &output_gain_wb, use_extra_vol); + +#ifdef WITH_TELEPHONY + vcm_setvolume(input_gain, input_gain_wb, output_gain, output_gain_wb, + vc_output_vol); +#endif +} + +unsigned int get_loopback_headset_flag(struct mrvl_audio_device *madev) { + unsigned int flag = CONNECT_STEREO_HEADSET; + + ALOGD("%s,headset_flag:%d", __FUNCTION__, madev->loopback_param.headset_flag); + + if (madev->loopback_param.on) { + if (madev->loopback_param.headset_flag == MONO_HEADSET_L) { + flag = CONNECT_MONO_HEADSET_L; + } else if (madev->loopback_param.headset_flag == MONO_HEADSET_R) { + flag = CONNECT_MONO_HEADSET_R; + } else { + flag = CONNECT_STEREO_HEADSET; + } + } + return flag; +} + +void route_devices(struct mrvl_audio_device *madev, unsigned int devices, + int enable, unsigned int value) { + unsigned int hw_dev = 0; + ALOGI("%s devices 0x%x %s", __FUNCTION__, devices, + (enable == METHOD_ENABLE) ? "enable" : "disable"); + + // if audio HW is set to all Rx sound mute, then ignore out device enable + if (mrvl_path_manager.mute_all_rx && (enable == METHOD_ENABLE) && + !(devices & AUDIO_DEVICE_BIT_IN)) { + ALOGI("%s in All Rx sound mute status, ignore this operation.", + __FUNCTION__); + return; + } + + // first check speaker device to consider force speaker mode + // only check output direction, input won't enter + if (!(devices & AUDIO_DEVICE_BIT_IN) && + (devices & AUDIO_DEVICE_OUT_SPEAKER)) { + // convert android device to HW device and route HW device + hw_dev = convert2_hwdev(madev, AUDIO_DEVICE_OUT_SPEAKER); + route_hw_device(hw_dev, enable, value); + devices &= ~AUDIO_DEVICE_OUT_SPEAKER; + } + + // check if has other available input/output devices to process + if (devices & ~AUDIO_DEVICE_BIT_IN) { + hw_dev = convert2_hwdev(madev, devices); + // flag for connectivity and coupling + value |= get_mic_hw_flag(hw_dev); + if ((devices & AUDIO_DEVICE_BIT_IN) && (enable == METHOD_ENABLE)) { + mrvl_path_manager.enabled_in_hwdev = hw_dev; + } else if ((devices & AUDIO_DEVICE_BIT_IN) && (enable == METHOD_DISABLE)) { + hw_dev = mrvl_path_manager.enabled_in_hwdev; + } else if (!(devices & AUDIO_DEVICE_BIT_IN) && + (devices & (AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE))) { + value |= get_loopback_headset_flag(madev); + ALOGI("choose headset flag: 0x%x", value); + } + route_hw_device(hw_dev, enable, value); + } +} + +static void select_input_device(struct mrvl_audio_device *madev) { + ALOGI("%s in_device 0x%x", __FUNCTION__, madev->in_device); + + // check active input interface to make sure device is configured later than + // interface + if (!has_active_in_itf()) { + ALOGI("%s no active input interface, return.", __FUNCTION__); + return; + } + + // check input parameter + if (((mrvl_path_manager.active_in_device == madev->in_device) && + (mrvl_path_manager.enabled_in_hwdev == + convert2_hwdev(madev, madev->in_device))) || + ((madev->in_device & ~AUDIO_DEVICE_BIT_IN) == AUDIO_DEVICE_NONE)) { + ALOGI("%s same or null device, return.", __FUNCTION__); + return; + } + + // disable old device if exists + if (mrvl_path_manager.active_in_device) { + // hot disable device, which means don't shutdown codec + route_devices(madev, mrvl_path_manager.active_in_device, METHOD_DISABLE, 1); + mrvl_path_manager.active_in_device = AUDIO_DEVICE_NONE; + select_virtual_path(madev); + } + + // enable the new device + route_devices(madev, madev->in_device, METHOD_ENABLE, 0); + mrvl_path_manager.active_in_device = madev->in_device; + + // process new virtual path + select_virtual_path(madev); +} + +static struct pcm *open_tiny_alsa_device(unsigned int device, + unsigned int direction, + struct pcm_config *config) { + ALOGI( + "%s: device %u, direction %u, channels %u, sample_rate %u, period_size " + "%u", + __FUNCTION__, device, direction, config->channels, config->rate, + config->period_size); + + struct pcm *pcm = NULL; + unsigned int req_period_size = config->period_size; + unsigned int req_period_count = config->period_count; + pcm = pcm_open(ALSA_CARD_DEFAULT, device, direction, config); + if (!pcm_is_ready(pcm)) { + ALOGE("%s: unable to open PCM device%u (%s)", __FUNCTION__, device, + pcm_get_error(pcm)); + pcm_close(pcm); + pcm = NULL; + } else { + if (((device == ALSA_DEVICE_DEFAULT) || + (device == ALSA_DEVICE_DEEP_BUFFER)) && + ((config->period_size != req_period_size) || + (config->period_count != req_period_count))) { + ALOGE("%s: Warning: period size changed error!!!", __FUNCTION__); + } + } + return pcm; +} + +static void select_output_device(struct mrvl_audio_device *madev) { + ALOGI("%s switch device from old device 0x%x to new device 0x%x ", + __FUNCTION__, mrvl_path_manager.active_out_device, madev->out_device); + + bool cp_mute = false; + // check active output interface to make sure device is configured later than + // interface + if (!has_active_out_itf()) { + ALOGI("%s no active output interface, return.", __FUNCTION__); + return; + } + + if (madev->in_fm && popcount(madev->out_device) == 2) { + ALOGI("%s Mute FM when playing force speaker stream", __FUNCTION__); + set_hw_volume(V_MODE_FM, convert2_hwdev(madev, madev->fm_device), 0); + } + + if (madev->mode == AUDIO_MODE_IN_CALL) { + // select input devices and route input device + madev->in_device = get_input_dev(madev->out_device); +// Mute CP before switch Tx path. +#ifdef WITH_TELEPHONY + vcm_mute_all(true, get_cpmute_rampdown_level()); + cp_mute = true; +#endif + select_input_device(madev); + + if ((madev->tty_mode == TTY_MODE_HCO) && + (madev->in_device == AUDIO_DEVICE_IN_WIRED_HEADSET)) { + ALOGI( + "%s tty_mode= %d, in_device= 0x%x ==> Change out_device from 0x%x to " + "AUDIO_DEVICE_OUT_SPEAKER", + __FUNCTION__, madev->tty_mode, madev->in_device, madev->out_device); + madev->out_device = AUDIO_DEVICE_OUT_SPEAKER; + } + + // notify CP to update device and re-set volume + if ((!madev->in_call) || + (mrvl_path_manager.active_out_device != madev->out_device)) { + unsigned int params = get_call_params(madev); +#ifdef WITH_TELEPHONY + vcm_select_path(madev->out_device, madev->in_device, params); + set_voice_call_volume(madev, madev->voice_volume, madev->use_extra_vol); +#endif + ALOGD("%s CP enter In Call state.", __FUNCTION__); + madev->in_call = true; // update IN_CALL status + } + } else { + if (madev->in_call) { + ALOGD("%s CP leave In Call state.", __FUNCTION__); + madev->in_call = false; // update IN_CALL status + + // If call ended, and user's choice is TTY_HCO, and the headset was pulled + // out during + // the call, the next call while using loud-speaker should NOT be in + // TTY_HCO mode + if ((madev->tty_mode != TTY_MODE_OFF) && + (madev->in_device != AUDIO_DEVICE_IN_WIRED_HEADSET)) { + ALOGD("%s: Call Ended and HS is out ==> set TTY Mode to TTY_OFF", + __FUNCTION__); + madev->tty_mode = TTY_MODE_OFF; + } + } + } + + if ((is_in_voip_vt(madev->mode)) && (!madev->loopback_param.on)) { + // select input devices and route input device + madev->in_device = get_input_dev(madev->out_device); + if (madev->active_input) { + ramp_down_start(SAMPLE_RATE_IN_DEFAULT); + select_input_device(madev); + } + } + + // check duplicate devices for smoothly switch + unsigned int unchange_dev = + mrvl_path_manager.active_out_device & madev->out_device; + + // first check device to disable + unsigned int dev_todisable = + mrvl_path_manager.active_out_device & ~unchange_dev; + if (dev_todisable) { + // hot disable device, which means don't shutdown codec + route_devices(madev, dev_todisable, METHOD_DISABLE, 1); + mrvl_path_manager.active_out_device &= ~dev_todisable; + } + + // then check device to enable + unsigned int dev_toenable = madev->out_device & ~unchange_dev; + if (dev_toenable) { + route_devices(madev, dev_toenable, METHOD_ENABLE, 0); + mrvl_path_manager.active_out_device |= dev_toenable; + } + + // process new virtual path + select_virtual_path(madev); + +// Unmute CP if needed after switch Rx path. +#ifdef WITH_TELEPHONY + if (cp_mute) { + vcm_mute_all(false, get_cpmute_rampup_level()); + cp_mute = false; + } +#endif + + // use saved fm_volume value to update fm volume if fm virtual path exists + if (madev->fm_handle && (madev->fm_volume != 0)) { + set_hw_volume(V_MODE_FM, convert2_hwdev(madev, madev->out_device), + madev->fm_volume); + } +} + +static void select_interface(struct mrvl_audio_device *madev, + path_interface_t ipath, bool enable) { + int i = 0; + ALOGI("%s path %d %s", __FUNCTION__, ipath, enable ? "enable" : "disable"); + + if ((ipath < ID_IPATH_RX_MIN) || (ipath > ID_IPATH_TX_MAX)) { + ALOGE("%s path invalid %d", __FUNCTION__, ipath); + return; + } + + // ignore duplicate commands. interface is disabled in some concurrency + // and may disabled again when enter standby + if (mrvl_path_manager.itf_state[ipath] == enable) { + ALOGI("%s ignore duplicated control for %d", __FUNCTION__, ipath); + return; + } + + // update path interface status + mrvl_path_manager.itf_state[ipath] = enable; + + if (enable) { + // enable path interface before enabling device + route_interface(ipath, METHOD_ENABLE); + } else { + // if all playback input paths disabled, then disable output path + bool all_standby = true; + for (i = ID_IPATH_RX_MIN; i <= ID_IPATH_RX_MAX; i++) { + if (mrvl_path_manager.itf_state[i] == true) { + all_standby = false; + break; + } + } + + // disable all devices + if (all_standby && + (mrvl_path_manager.active_out_device != AUDIO_DEVICE_NONE)) { + route_devices(madev, mrvl_path_manager.active_out_device, METHOD_DISABLE, + 0); + mrvl_path_manager.active_out_device = AUDIO_DEVICE_NONE; + } + + // if all record output paths disabled, then disable input path + all_standby = true; + for (i = ID_IPATH_TX_MIN; i <= ID_IPATH_TX_MAX; i++) { + if (mrvl_path_manager.itf_state[i] == true) { + all_standby = false; + break; + } + } + if (all_standby && + (mrvl_path_manager.active_in_device != AUDIO_DEVICE_NONE)) { + route_devices(madev, mrvl_path_manager.active_in_device, METHOD_DISABLE, + 0); + mrvl_path_manager.active_in_device = AUDIO_DEVICE_NONE; + } + + // disable path interface after disabling device + route_interface(ipath, METHOD_DISABLE); + } + + // process new virtual path + select_virtual_path(madev); +} + +static int start_output_stream_low_latency(struct mrvl_stream_out *out) { + int ret = 0; + struct mrvl_audio_device *madev = out->dev; + ALOGD("%s", __FUNCTION__); + + if (out->handle) { + pcm_close(out->handle); + out->handle = NULL; + } + out->handle = open_tiny_alsa_device(ALSA_DEVICE_DEFAULT, + PCM_OUT | PCM_NORESTART | PCM_MONOTONIC, + &pcm_config_ll); + if (!out->handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return -1; + } + // configure playback input path + select_interface(madev, ID_IPATH_RX_HIFI_LL, true); + + // configure playback output path if necessary + // only when not in VC/FM mode, using fm_handle to distinguish whether FM path + // is enabled + if ((madev->mode != AUDIO_MODE_IN_CALL) && (!madev->fm_handle)) { + select_output_device(madev); + } + + return 0; +} + +static int start_output_stream_deep_buffer(struct mrvl_stream_out *out) { + int ret = 0; + struct mrvl_audio_device *madev = out->dev; + ALOGD("%s", __FUNCTION__); + + if (out->handle) { + pcm_close(out->handle); + out->handle = NULL; + } + out->handle = + open_tiny_alsa_device(ALSA_DEVICE_DEEP_BUFFER, + PCM_OUT | PCM_MMAP | PCM_MONOTONIC, &pcm_config_db); + if (!out->handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return -1; + } + + out->write_threshold = + DEEP_BUFFER_LONG_PERIOD_SIZE * DEEP_BUFFER_LONG_PERIOD_COUNT; + out->use_long_periods = true; + + // configure playback input path + select_interface(madev, ID_IPATH_RX_HIFI_DB, true); + + // configure Music playback output and input path + // only when not in VC/FM mode, using fm_handle to distinguish whether FM path + // is enabled + if ((madev->mode != AUDIO_MODE_IN_CALL) && (!madev->fm_handle)) { + select_output_device(madev); + } + + return 0; +} + +// must be called with hw device and output stream mutexes locked +static int start_input_stream(struct mrvl_stream_in *in) { + int ret = 0; + struct mrvl_audio_device *madev = in->dev; + ALOGD("%s in->source %d", __FUNCTION__, in->source); + if (in->handle) { + pcm_close(in->handle); + in->handle = NULL; + } + + if (in->source == AUDIO_SOURCE_FMRADIO) { + if (madev->mode != AUDIO_MODE_IN_CALL) { + // FIXME, open alsa device + in->handle = open_tiny_alsa_device( + ALSA_DEVICE_DEFAULT, PCM_IN | PCM_NORESTART, &pcm_config_input); + if (!in->handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return -1; + } + // configure FM record output path + select_interface(madev, ID_IPATH_TX_FM, true); + } else { + ALOGW("%s forbid FM record during VC", __FUNCTION__); + return -1; + } + } else { + // open default hifi input alsa device + in->handle = open_tiny_alsa_device(ALSA_DEVICE_DEFAULT, + PCM_IN | PCM_NORESTART | PCM_MONOTONIC, + &pcm_config_input); + if (!in->handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return -1; + } + + // enable voice recognition + if (in->source == AUDIO_SOURCE_VOICE_RECOGNITION) { + madev->use_voice_recognition = true; + } + + // configure hifi record output path + select_interface(madev, ID_IPATH_TX_HIFI, true); + + // configure record input path if necessary + if (madev->mode != AUDIO_MODE_IN_CALL) { + ramp_up_start(SAMPLE_RATE_IN_DEFAULT); + select_input_device(madev); + } + } + + madev->active_input = in; + + return 0; +} +// must be called with hw device and input stream mutexes locked +static int do_input_standby(struct mrvl_stream_in *in) { + ALOGI("%s in %p", __FUNCTION__, in); + struct mrvl_audio_device *madev = in->dev; + + if (!in->standby) { + in->standby = true; + + // stop pcm read/write before close audio path. + if (in->handle) { + pcm_stop(in->handle); + } + + if (in->source == AUDIO_SOURCE_FMRADIO) { + select_interface(in->dev, ID_IPATH_TX_FM, false); + } else { + select_interface(in->dev, ID_IPATH_TX_HIFI, false); + } + + // disable voice recognition + if (in->source == AUDIO_SOURCE_VOICE_RECOGNITION) { + madev->use_voice_recognition = false; + } + + if (in->handle) { + pcm_close(in->handle); + in->handle = NULL; + } + madev->active_input = NULL; + } + return 0; +} + +// must be called with hw device and output stream mutexes locked +static int do_output_standby(struct mrvl_stream_out *out) { + struct mrvl_audio_device *madev = out->dev; + ALOGI("%s out %p standby %s", __FUNCTION__, out, + out->standby ? "true" : "false"); + + if (!out->standby) { + out->standby = true; + + // stop pcm read/write before close audio path. + if (out->handle) { + pcm_stop(out->handle); + } + + if (out == madev->outputs[OUTPUT_LOW_LATENCY]) { + select_interface(out->dev, ID_IPATH_RX_HIFI_LL, false); + } else if (out == madev->outputs[OUTPUT_DEEP_BUF]) { + select_interface(out->dev, ID_IPATH_RX_HIFI_DB, false); + } else { + ALOGE("%s unrecognized stream out", __FUNCTION__); + } + + if (out->handle) { + pcm_close(out->handle); + out->handle = NULL; + } + } + + return 0; +} + +// must be called with hw device mutex locked +static void force_all_standby(struct mrvl_audio_device *madev) { + struct mrvl_stream_out *out = NULL; + struct mrvl_stream_in *in = NULL; + // standby both low latency and deep buffer output and input. + // it can used to device switch or deep buffer output resume + out = madev->outputs[OUTPUT_LOW_LATENCY]; + if (out != NULL && !out->standby) { + pthread_mutex_lock(&out->lock); + do_output_standby(out); + pthread_mutex_unlock(&out->lock); + } + + out = madev->outputs[OUTPUT_DEEP_BUF]; + if (out != NULL && !out->standby) { + pthread_mutex_lock(&out->lock); + do_output_standby(out); + pthread_mutex_unlock(&out->lock); + } + + if (madev->active_input) { + in = madev->active_input; + pthread_mutex_lock(&in->lock); + do_input_standby(in); + pthread_mutex_unlock(&in->lock); + } +} + +// FM handling function, for internal use +static void set_fm_parameters(struct mrvl_audio_device *madev, + struct str_parms *parms) { + int fm_status = 0; + int fm_device = 0; + int fm_volume = 0; + pthread_mutex_lock(&madev->lock); + + // set fm volume + if (str_parms_get_int(parms, AUDIO_PARAMETER_FM_VOLUME, &fm_volume) >= 0) { + ALOGI("%s: set FM volume %d", __FUNCTION__, fm_volume); + madev->fm_volume = fm_volume; + set_hw_volume(V_MODE_FM, convert2_hwdev(madev, madev->out_device), + fm_volume); + str_parms_del(parms, AUDIO_PARAMETER_FM_VOLUME); + } + + // set fm status + if (str_parms_get_int(parms, AUDIO_PARAMETER_FM_STATUS, &fm_status) >= 0) { + switch (fm_status) { + case FM_DISABLE: // disable + if (str_parms_get_int(parms, AUDIO_PARAMETER_FM_DEVICE, &fm_device) >= + 0) { + // disable FM playback input path + ALOGI("%s: disable FM", __FUNCTION__); + if (madev->in_fm) { + select_interface(madev, ID_IPATH_RX_FM, false); + madev->in_fm = false; + madev->fm_device = 0; + madev->fm_volume = 0; + + // close FE when disable FM + if (madev->fm_handle) { + pcm_stop(madev->fm_handle); + pcm_close(madev->fm_handle); + madev->fm_handle = NULL; + } + } + } + break; + case FM_ENABLE: // enable + if (str_parms_get_int(parms, AUDIO_PARAMETER_FM_DEVICE, &fm_device) >= + 0) { + ALOGI("%s: enable FM", __FUNCTION__); + if (!madev->in_fm) { + // open FE when enable FM + if (!madev->fm_handle) { + madev->fm_handle = open_tiny_alsa_device(ALSA_DEVICE_FM, PCM_OUT, + &pcm_config_fm); + if (!madev->fm_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + break; + } + pcm_start(madev->fm_handle); + } + + // enable FM playback input path + select_interface(madev, ID_IPATH_RX_FM, true); + madev->in_fm = true; + } + // enable or switch FM output path + madev->out_device = fm_device; + madev->fm_device = fm_device; + select_output_device(madev); + } + break; + } + str_parms_del(parms, AUDIO_PARAMETER_FM_DEVICE); + str_parms_del(parms, AUDIO_PARAMETER_FM_STATUS); + } + pthread_mutex_unlock(&madev->lock); +} + +static bool is_phone_call(int mode) { return (mode == AUDIO_MODE_IN_CALL); } + +static void select_mode(struct mrvl_audio_device *madev, int mode) { + int old_mode = madev->mode; + + // close FM when change mode from NORMAL to others + if ((old_mode == AUDIO_MODE_NORMAL) && (mode != AUDIO_MODE_NORMAL)) { + if (madev->in_fm) { + select_interface(madev, ID_IPATH_RX_FM, false); + if (madev->fm_handle) { + pcm_stop(madev->fm_handle); + pcm_close(madev->fm_handle); + madev->fm_handle = NULL; + } + } + } + + // case 1: change mode from non-phone call to phone call + if (!is_phone_call(old_mode) && is_phone_call(mode)) { + ALOGI("%s Entering IN_CALL state: %d -> %d", __FUNCTION__, old_mode, mode); + + // force earpiece route for in call state if speaker is the + // only currently selected route. This prevents having to tear + // down the modem PCMs to change route from speaker to earpiece + // after the ringtone is played, but doesn't cause a route + // change if a headset or bt device is already connected. If + // speaker is not the only thing active, just remove it from + // the route. We'll assume it'll never be used initially during + // a call. This works because we're sure that the audio policy + // manager will update the output device after the audio mode + // change, even if the device selection did not change. + if (madev->out_device == AUDIO_DEVICE_OUT_SPEAKER) { + madev->out_device = AUDIO_DEVICE_OUT_EARPIECE; + } else { + madev->out_device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } + + // open FE when enter IN_CALL mode + if (!madev->phone_dl_handle) { + madev->phone_dl_handle = + open_tiny_alsa_device(ALSA_DEVICE_VOICE, PCM_OUT, &pcm_config_phone); + if (!madev->phone_dl_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return; + } + pcm_start(madev->phone_dl_handle); + } + if (!madev->phone_ul_handle) { + madev->phone_ul_handle = + open_tiny_alsa_device(ALSA_DEVICE_VOICE, PCM_IN, &pcm_config_phone); + if (!madev->phone_ul_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + pcm_stop(madev->phone_dl_handle); + pcm_close(madev->phone_dl_handle); + madev->phone_dl_handle = NULL; + return; + } + pcm_start(madev->phone_ul_handle); + } + + // configure VC interface, both Playback and Record + select_interface(madev, ID_IPATH_RX_VC, true); + select_interface(madev, ID_IPATH_TX_VC, true); + + // case 2: change mode from phone call to non-phone call + } else if (is_phone_call(old_mode) && !is_phone_call(mode)) { + ALOGI("%s Leaving IN_CALL state: %d -> %d", __FUNCTION__, old_mode, mode); + + // configure VC interface, both Playback and Record + select_interface(madev, ID_IPATH_RX_VC, false); + select_interface(madev, ID_IPATH_TX_VC, false); + + // close FE when leave IN_CALL mode + if (madev->phone_dl_handle) { + pcm_stop(madev->phone_dl_handle); + pcm_close(madev->phone_dl_handle); + madev->phone_dl_handle = NULL; + } + if (madev->phone_ul_handle) { + pcm_stop(madev->phone_ul_handle); + pcm_close(madev->phone_ul_handle); + madev->phone_ul_handle = NULL; + } + + // case 3: change mode from non-phone call to non-phone call + } else { + ALOGI("%s Nothing change when switching between non-phone calls", + __FUNCTION__); + } + + // resume FM when change mode from others to NORMAL + if ((old_mode != AUDIO_MODE_NORMAL) && (mode == AUDIO_MODE_NORMAL)) { + if (madev->in_fm) { + if (!madev->fm_handle) { + madev->fm_handle = + open_tiny_alsa_device(ALSA_DEVICE_FM, PCM_OUT, &pcm_config_fm); + if (!madev->fm_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return; + } + pcm_start(madev->fm_handle); + } + select_interface(madev, ID_IPATH_RX_FM, true); + } + } + + madev->mode = mode; + select_output_device(madev); + if (madev->active_input) { + select_input_device(madev); + } +} + +static void enable_loopback(struct mrvl_audio_device *madev) { + ALOGI("%s: out_device 0x%x, in_device 0x%x", __FUNCTION__, madev->out_device, + madev->in_device); + + float loopback_volume = 0.8; + + if (madev->loopback_param.type == APP_LOOPBACK) { + select_input_device(madev); + select_output_device(madev); + return; + } + + if (!madev->phone_dl_handle) { + madev->phone_dl_handle = + open_tiny_alsa_device(ALSA_DEVICE_VOICE, PCM_OUT, &pcm_config_phone); + if (!madev->phone_dl_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + return; + } + pcm_start(madev->phone_dl_handle); + } + if (!madev->phone_ul_handle) { + madev->phone_ul_handle = + open_tiny_alsa_device(ALSA_DEVICE_VOICE, PCM_IN, &pcm_config_phone); + if (!madev->phone_ul_handle) { + ALOGE("%s open tiny alsa device error", __FUNCTION__); + pcm_stop(madev->phone_dl_handle); + pcm_close(madev->phone_dl_handle); + madev->phone_dl_handle = NULL; + return; + } + pcm_start(madev->phone_ul_handle); + } + + // configure VC interface, both Playback and Record + if (madev->loopback_param.type == CP_LOOPBACK) { + select_interface(madev, ID_IPATH_RX_VC, true); + select_interface(madev, ID_IPATH_TX_VC, true); + +#ifdef WITH_TELEPHONY + vcm_mute_all(true, get_cpmute_rampdown_level()); + vcm_set_loopback(madev->out_device, true); + set_voice_call_volume(madev, loopback_volume, 0); + set_hw_volume(V_MODE_VC, convert2_hwdev(madev, madev->out_device), + (unsigned char)(loopback_volume * 100)); + vcm_mute_all(false, get_cpmute_rampup_level()); +#endif + + } else if (madev->loopback_param.type == HARDWARE_LOOPBACK) { + select_interface(madev, ID_IPATH_RX_VC_ST, true); + select_interface(madev, ID_IPATH_TX_VC, true); + } else { + ALOGE("%s: Invalid loop type", __FUNCTION__); + } + + select_input_device(madev); + select_output_device(madev); +} + +static void disable_loopback(struct mrvl_audio_device *madev) { + ALOGI("%s: out_device 0x%x, in_device 0x%x, loopback_mode:%d", __FUNCTION__, + madev->out_device, madev->in_device, madev->loopback_param.type); + + if (madev->loopback_param.type == APP_LOOPBACK) { + return; + } + // configure VC interface, both Playback and Record + if (madev->loopback_param.type == CP_LOOPBACK) { + select_interface(madev, ID_IPATH_RX_VC, false); + select_interface(madev, ID_IPATH_TX_VC, false); + +#ifdef WITH_TELEPHONY + vcm_mute_all(true, get_cpmute_rampdown_level()); + set_voice_call_volume(madev, 0, 0); + vcm_set_loopback(madev->out_device, false); + set_hw_volume(V_MODE_VC, convert2_hwdev(madev, madev->out_device), 0); + vcm_mute_all(false, get_cpmute_rampup_level()); +#endif + + } else if (madev->loopback_param.type == HARDWARE_LOOPBACK) { + select_interface(madev, ID_IPATH_RX_VC_ST, false); + select_interface(madev, ID_IPATH_TX_VC, false); + } else { + ALOGE("%s: Invalid loop type", __FUNCTION__); + } + + if (madev->phone_dl_handle) { + pcm_stop(madev->phone_dl_handle); + pcm_close(madev->phone_dl_handle); + madev->phone_dl_handle = NULL; + } + if (madev->phone_ul_handle) { + pcm_stop(madev->phone_ul_handle); + pcm_close(madev->phone_ul_handle); + madev->phone_ul_handle = NULL; + } +} + +static void select_loopback(struct mrvl_audio_device *madev, bool value) { + ALOGD("loopback_out_device:0x%x, loopback_in_device:0x%x", + madev->loopback_param.out_device, madev->loopback_param.in_device); + if (value) { + madev->out_device = madev->loopback_param.out_device; + madev->in_device = madev->loopback_param.in_device; + madev->loopback_param.on = true; + enable_loopback(madev); + } else { + madev->out_device = AUDIO_DEVICE_OUT_SPEAKER; + madev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC; + madev->loopback_param.on = false; + disable_loopback(madev); + } +} + +// ---------------------------------------------------------------------- +// External Interfaces +// ---------------------------------------------------------------------- +static uint32_t out_get_sample_rate(const struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + return out->sample_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + ALOGD("%s sample_rate %d", __FUNCTION__, rate); + out->sample_rate = rate; + return 0; +} + +static size_t out_get_buffer_size_low_latency( + const struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + + // take resampling into account and return the closest majoring + // multiple of 16 frames, as audioflinger expects audio buffers to + // be a multiple of 16 frames. + int buffer_size = ((out->period_size + 15) / 16) * 16; + ALOGD("%s buffer_size %d", __FUNCTION__, buffer_size); + return buffer_size * audio_stream_frame_size((struct audio_stream *)stream); +} + +static size_t out_get_buffer_size_deep_buffer( + const struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + + // take resampling into account and return the closest majoring + // multiple of 16 frames, as audioflinger expects audio buffers to + // be a multiple of 16 frames. + int buffer_size = ((DEEP_BUFFER_SHORT_PERIOD_SIZE + 15) / 16) * 16; + ALOGD("%s buffer_size %d", __FUNCTION__, buffer_size); + return buffer_size * audio_stream_frame_size((struct audio_stream *)stream); +} + +static audio_channel_mask_t out_get_channels( + const struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + return out->channel_mask; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + return (audio_format_t)out->format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + ALOGD("%s", __FUNCTION__); + out->format = format; + return 0; +} + +static int out_standby(struct audio_stream *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + struct mrvl_audio_device *madev = out->dev; + ALOGI("%s out %p", __FUNCTION__, out); + +#ifdef MRVL_AEC + if (out == madev->outputs[OUTPUT_LOW_LATENCY]) { + out_release_effect((struct audio_stream *)out, + (effect_uuid_t *)FX_IID_VOIPRX); + } +#endif + + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); + +#ifdef WITH_ACOUSTIC + acoustic_manager_reset(out->acoustic_manager); +#endif + + do_output_standby(out); + + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) { + const size_t SIZE = 256; + char buffer[SIZE]; + char result[SIZE]; + snprintf(buffer, SIZE, "out_dump()"); + strcpy(result, buffer); + snprintf(buffer, SIZE, "\tsample rate: %d", out_get_sample_rate(stream)); + strcat(result, buffer); + snprintf(buffer, SIZE, "\tchannel count: %d", out_get_channels(stream)); + strcat(result, buffer); + snprintf(buffer, SIZE, "\tformat: %d", out_get_format(stream)); + strcat(result, buffer); + write(fd, result, strlen(result)); + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, + const char *kvpairs) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + struct mrvl_audio_device *madev = out->dev; + struct str_parms *parms; + char value[PARAM_SIZE]; + int ret = 0; + int val = 0; + int hw_vol = 0; + ALOGI("%s: %s", __FUNCTION__, kvpairs); + + parms = str_parms_create_str(kvpairs); + + // for FM handling + set_fm_parameters(madev, parms); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + if (val != 0) { + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&out->lock); + madev->out_device = val; + select_output_device(madev); + // set volume value to ACM for volume calibration for voice call. + if (madev->mode == AUDIO_MODE_IN_CALL) { + set_hw_volume(V_MODE_VC, convert2_hwdev(madev, madev->out_device), + madev->voice_volume * 100); + } + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&madev->lock); + } +#ifdef MRVL_AEC + if (out == madev->outputs[OUTPUT_LOW_LATENCY] && + is_in_voip_vt(madev->mode)) { + uint32_t audio_profile = get_profile(madev); + if (audio_profile != AUDIO_PROFILE_ID_ENUM_32_BIT) { + effect_set_profile(audio_profile, madev); + } + } +#endif + str_parms_del(parms, AUDIO_PARAMETER_STREAM_ROUTING); + } + + // set hw volume that used for force speaker + if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_HW_VOLUME, &hw_vol) >= + 0) { + ALOGI("%s: set HW volume %d", __FUNCTION__, hw_vol); + set_hw_volume(V_MODE_HIFI_LL, convert2_hwdev(madev, madev->out_device), + hw_vol); + str_parms_del(parms, AUDIO_PARAMETER_STREAM_HW_VOLUME); + } + + str_parms_destroy(parms); + +#ifdef WITH_ACOUSTIC + if (madev->out_device) { + acoustic_manager_init(&(out->acoustic_manager), out->sample_rate, + popcount(out->channel_mask), madev->out_device); + } +#endif + + return ret; +} + +static char *out_get_parameters(const struct audio_stream *stream, + const char *keys) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + struct mrvl_audio_device *madev = out->dev; + struct str_parms *param; + char value[PARAM_SIZE]; + const char *key; + char *ret_val = NULL; + ALOGI("%s keys %s", __FUNCTION__, keys); + + param = str_parms_create_str(keys); + if (!param) return ret_val; + key = AUDIO_PARAMETER_STREAM_ROUTING; + + if (str_parms_get_str(param, key, value, sizeof(value)) >= 0) { + str_parms_add_int(param, key, (int)madev->out_device); + ret_val = str_parms_to_str(param); + ALOGV("%s: %s", __FUNCTION__, ret_val); + str_parms_del(param, key); + } + + str_parms_destroy(param); + + return ret_val; +} + +static uint32_t out_get_latency_low_latency( + const struct audio_stream_out *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + ALOGD("%s period_size %d period_count %d", __FUNCTION__, out->period_size, + out->period_count); + return 1000 * out->period_size * out->period_count / out->sample_rate; +} + +static uint32_t out_get_latency_deep_buffer( + const struct audio_stream_out *stream) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + ALOGD("%s period_size %d period_count %d", __FUNCTION__, + DEEP_BUFFER_LONG_PERIOD_SIZE, DEEP_BUFFER_LONG_PERIOD_COUNT); + return 1000 * DEEP_BUFFER_LONG_PERIOD_SIZE * DEEP_BUFFER_LONG_PERIOD_COUNT / + out->sample_rate; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) { + return -ENOSYS; +} + +static ssize_t out_write_low_latency(struct audio_stream_out *stream, + const void *buffer, size_t bytes) { + int ret = -1; + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + struct mrvl_audio_device *madev = out->dev; + bool force_input_standby = false; + size_t out_frames = bytes / audio_stream_frame_size(&out->stream.common); + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_RX_LL, buffer, bytes); +#endif + +#ifdef MRVL_AEC + if (out == madev->outputs[OUTPUT_LOW_LATENCY] && is_in_voip_vt(madev->mode)) { + uint32_t audio_profile = get_profile(madev); + if (audio_profile != AUDIO_PROFILE_ID_ENUM_32_BIT) { + out_load_effect((struct audio_stream *)out, + (effect_uuid_t *)FX_IID_VOIPRX, audio_profile); + } + } +#endif + + // path operations should under protection of madev->lock + // request madev->lock first to avoid deadlock + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&out->lock); + if (out->standby) { + ret = start_output_stream_low_latency(out); + if (ret != 0) { + ALOGE("%s start output error. ret %d", __FUNCTION__, ret); + pthread_mutex_unlock(&madev->lock); + goto exit; + } + out->standby = false; + } + pthread_mutex_unlock(&madev->lock); + +#ifdef MRVL_AEC + if (is_in_voip_vt(madev->mode)) { + effect_rx_process(out, buffer); + } +#endif + +#ifdef WITH_ACOUSTIC + if (!is_in_voip_vt(madev->mode)) { + acoustic_manager_process(out->acoustic_manager, (signed short *)buffer, + out_frames); + } +#endif + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_RX_AF_ACOUSTIC_LL, buffer, bytes); +#endif + + ret = pcm_write(out->handle, buffer, bytes); + if (ret == -EPIPE) { + ALOGW("%s: [UNDERRUN] failed to write, reason: broken pipe", __FUNCTION__); + ret = pcm_write(out->handle, buffer, bytes); + } + + if (ret == 0) { + out->written += bytes / (pcm_config_ll.channels * sizeof(int16_t)); + } else if (ret < 0) { + ALOGE("%s: failed to write, reason: %s", __FUNCTION__, + pcm_get_error(out->handle)); + goto exit; + } + +#ifdef MRVL_AEC + if (is_in_voip_vt(madev->mode)) { + echo_ref_rx_write(out, buffer, bytes); + } +#endif + +exit: + pthread_mutex_unlock(&out->lock); + + // delay when error happened except enter underrun + if ((ret < 0) && (ret != -EPIPE)) { + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / + out_get_sample_rate(&stream->common)); + } + return bytes; +} + +static ssize_t out_write_deep_buffer(struct audio_stream_out *stream, + const void *buffer, size_t bytes) { + int ret = -1; + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + struct mrvl_audio_device *madev = out->dev; + size_t out_frames = bytes / audio_stream_frame_size(&out->stream.common); + bool use_long_periods = true; + int avail_frames = 0; + int kernel_frames = 0; + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_RX_DB, buffer, bytes); +#endif + + // path operations should under protection of madev->lock + // request madev->lock first to avoid deadlock + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&out->lock); + if (out->standby) { + ret = start_output_stream_deep_buffer(out); + if (ret != 0) { + ALOGE("%s start output error. ret %d", __FUNCTION__, ret); + pthread_mutex_unlock(&madev->lock); + goto exit; + } + out->standby = false; + } + use_long_periods = madev->screen_off && !madev->active_input; + pthread_mutex_unlock(&madev->lock); + +#ifdef WITH_ACOUSTIC + if (!is_in_voip_vt(madev->mode)) { + acoustic_manager_process(out->acoustic_manager, (signed short *)buffer, + out_frames); + } +#endif + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_RX_AF_ACOUSTIC_DB, buffer, bytes); +#endif + + if (use_long_periods != out->use_long_periods) { + if (use_long_periods) { + out->write_threshold = + DEEP_BUFFER_LONG_PERIOD_SIZE * DEEP_BUFFER_LONG_PERIOD_COUNT; + } else { + out->write_threshold = + DEEP_BUFFER_SHORT_PERIOD_SIZE * DEEP_BUFFER_SHORT_PERIOD_COUNT; + } + // ALOGV("%s: write_threshold is %d.", __FUNCTION__, out->write_threshold); + out->use_long_periods = use_long_periods; + } + + // do not allow more than out->write_threshold frames in kernel pcm driver + // buffer + do { + struct timespec time_stamp; + + // query the available frames in audio driver buffer + if (pcm_get_htimestamp(out->handle, (unsigned int *)&avail_frames, + &time_stamp) < 0) + break; + + kernel_frames = pcm_get_buffer_size(out->handle) - avail_frames; + // ALOGV("%s: write_threshold is %d, kernel_frames is %d.", __FUNCTION__, + // out->write_threshold, kernel_frames); + if (kernel_frames > out->write_threshold) { + unsigned long time = + (unsigned long)(((int64_t)(kernel_frames - out->write_threshold) * + 1000000) / + SAMPLE_RATE_OUT_DEFAULT); + if (time < MIN_WRITE_SLEEP_US) time = MIN_WRITE_SLEEP_US; + usleep(time); + } + } while (kernel_frames > out->write_threshold); + + ret = pcm_mmap_write(out->handle, buffer, bytes); + if (ret == 0) { + out->written += bytes / (pcm_config_db.channels * sizeof(int16_t)); + } else if (ret == -EPIPE) { + ALOGW("%s: [UNDERRUN] failed to write, reason: broken pipe", __FUNCTION__); + } else if (ret < 0) { + ALOGE("%s: failed to write, reason: %s", __FUNCTION__, + pcm_get_error(out->handle)); + } + +exit: + pthread_mutex_unlock(&out->lock); + + if (ret < 0) { + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / + out_get_sample_rate(&stream->common)); + } + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) { + return -EINVAL; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, + struct timespec *timestamp) { + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + int ret = -1; + + pthread_mutex_lock(&out->lock); + + size_t avail = 0; + if (out->handle) { + if (pcm_get_htimestamp(out->handle, &avail, timestamp) == 0) { + int64_t kernel_buffer_size = + (int64_t)out->period_size * (int64_t)out->period_count; + int64_t signed_frames = out->written - kernel_buffer_size + avail; + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + } + } + pthread_mutex_unlock(&out->lock); + + return ret; +} + +static int out_add_audio_effect_fake(const struct audio_stream *stream, + effect_handle_t effect) { + return 0; +} + +static int out_remove_audio_effect_fake(const struct audio_stream *stream, + effect_handle_t effect) { + return 0; +} + +// audio_stream_in implementation +static uint32_t in_get_sample_rate(const struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + return in->sample_rate; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { + return 0; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + + // take resampling into account and return the closest majoring + // multiple of 16 frames, as audioflinger expects audio buffers to + // be a multiple of 16 frames. + int buffer_size = ((in->period_size + 15) / 16) * 16; + ALOGD("%s buffer_size %d", __FUNCTION__, buffer_size); + return buffer_size * audio_stream_frame_size((struct audio_stream *)stream); +} + +static uint32_t in_get_channels(const struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + return in->channel_mask; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + return in->format; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) { + return 0; +} + +static int in_standby_vc_rec(struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + ALOGI("%s in %p", __FUNCTION__, in); + + pthread_mutex_lock(&in->lock); + if (!in->standby) { +#ifdef WITH_TELEPHONY + vcm_recording_stop(); +#endif + in->standby = true; + } + pthread_mutex_unlock(&in->lock); + return 0; +} + +static int in_standby_primary(struct audio_stream *stream) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + struct mrvl_audio_device *madev = in->dev; + ALOGI("%s in %p", __FUNCTION__, in); + +#ifdef MRVL_AEC + in_release_effect((struct audio_stream *)in, (effect_uuid_t *)FX_IID_VOIPTX); + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&in->lock); + remove_echo_ref(in, madev->outputs[OUTPUT_LOW_LATENCY]); + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&madev->lock); +#endif + + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&in->lock); + +#ifdef WITH_ACOUSTIC + acoustic_manager_reset(in->acoustic_manager); +#endif + + do_input_standby(in); + + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&madev->lock); + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) { return 0; } + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + struct mrvl_audio_device *madev = in->dev; + struct str_parms *parms; + char *str; + char value[32]; + int ret, val = 0; + bool do_standby = false; + ALOGI("%s: %s", __FUNCTION__, kvpairs); + + parms = str_parms_create_str(kvpairs); + + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&in->lock); + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + // no audio source uses val == 0 + if ((in->source != val) && (val != 0)) { + in->source = val; + } + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + if ((val != 0) && ((unsigned int)val != AUDIO_DEVICE_IN_VOICE_CALL) && + (madev->in_device != (unsigned int)val) && + (!is_in_voip_vt(madev->mode))) { + madev->in_device = val; + select_input_device(madev); + } + } + + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&madev->lock); + +#ifdef WITH_ACOUSTIC + if (madev->in_device & ~AUDIO_DEVICE_BIT_IN) { + acoustic_manager_init(&(in->acoustic_manager), in->sample_rate, + popcount(in->channel_mask), madev->in_device); + } +#endif + + str_parms_destroy(parms); + return ret; +} + +static char *in_get_parameters(const struct audio_stream *stream, + const char *keys) { + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)in->dev; + ALOGV("%s: %s", __FUNCTION__, keys); + + struct str_parms *param; + char value[32]; + char *key; + int ret = 0; + char *ret_val = NULL; + + param = str_parms_create_str(keys); + if (!param) return ret_val; + + key = AUDIO_PARAMETER_STREAM_ROUTING; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + str_parms_add_int(param, key, (int)madev->in_device); + ret_val = str_parms_to_str(param); + ALOGV("%s: %s", __FUNCTION__, ret_val); + str_parms_del(param, key); + } + + str_parms_destroy(param); + return ret_val; +} + +static int in_add_audio_effect_fake(const struct audio_stream *stream, + effect_handle_t effect) { + return 0; +} + +static int in_remove_audio_effect_fake(const struct audio_stream *stream, + effect_handle_t effect) { + return 0; +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) { return 0; } + +static ssize_t in_read_vc_rec(struct audio_stream_in *stream, void *buffer, + size_t bytes) { + int ret = 0; + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + + // path operations should under protection of madev->lock + // request madev->lock first to avoid deadlock + pthread_mutex_lock(&in->lock); + if (in->standby) { +#ifdef WITH_TELEPHONY + ret = vcm_recording_start(); +#endif + if (ret != 0) { + ALOGE("%s start input error. ret %d", __FUNCTION__, ret); + goto exit; + } + in->standby = false; + } + +// read stream data from VCM +#ifdef WITH_TELEPHONY + ret = vcm_recording_read(buffer, bytes); +#endif + if (ret < 0) { + ALOGE("%s read VC recording stream error, ret %d", __FUNCTION__, ret); + goto exit; + } + +exit: + pthread_mutex_unlock(&in->lock); + if (ret < 0) { + memset((unsigned char *)buffer, 0, bytes); + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / + in_get_sample_rate(&stream->common)); + } + return bytes; +} + +static ssize_t in_read_primary(struct audio_stream_in *stream, void *buffer, + size_t bytes) { + int ret = 0; + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + struct mrvl_audio_device *madev = in->dev; + +#ifdef MRVL_AEC + if (is_in_voip_vt(madev->mode)) { + uint32_t audio_profile = get_profile(madev); + if (audio_profile != AUDIO_PROFILE_ID_ENUM_32_BIT) { + in_load_effect((struct audio_stream *)stream, + (effect_uuid_t *)FX_IID_VOIPTX, audio_profile, true); + } + } +#endif + + // path operations should under protection of madev->lock + // request madev->lock first to avoid deadlock + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&in->lock); + if (in->standby) { + ret = start_input_stream(in); + if (ret != 0) { + ALOGE("%s start input error. ret %d", __FUNCTION__, ret); + pthread_mutex_unlock(&madev->lock); + goto exit; + } + in->standby = false; + } + +#ifdef MRVL_AEC + if (madev->echo_reference == NULL) { + ALOGI("%s: create echo reference for input stream %p", __FUNCTION__, in); + create_echo_ref(in, madev->outputs[OUTPUT_LOW_LATENCY]); + } +#endif + pthread_mutex_unlock(&madev->lock); + + ret = pcm_read(in->handle, buffer, bytes); + if (ret == -EPIPE) { + ALOGW("%s: [OVERRUN] failed to read, reason: broken pipe", __FUNCTION__); + ret = pcm_read(in->handle, buffer, bytes); + } + + if (ret < 0) { + ALOGE("%s: failed to read, reason: %s", __FUNCTION__, + pcm_get_error(in->handle)); + goto exit; + } + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_TX, buffer, bytes); +#endif + + // ramp down the data before disable TX and ramp up the data after enable TX + ramp_process(buffer, bytes); + +#ifdef WITH_ACOUSTIC + if ((!is_in_voip_vt(madev->mode)) && + (in->source != AUDIO_SOURCE_VOICE_RECOGNITION)) { + int16_t *p = (int16_t *)buffer; + size_t in_frames = bytes / audio_stream_frame_size(&in->stream.common); + acoustic_manager_process(in->acoustic_manager, p, in_frames); + } +#endif + +#ifdef AUDIO_DEBUG + audio_debug_stream_dump(AH_TX_AF_ACOUSTIC, buffer, bytes); +#endif + +#ifdef MRVL_AEC + if (is_in_voip_vt(madev->mode)) { + in_pre_process(in, buffer); + } +#endif + +exit: + pthread_mutex_unlock(&in->lock); + if ((ret < 0) && (ret != -EPIPE)) { + memset((unsigned char *)buffer, 0, bytes); + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / + in_get_sample_rate(&stream->common)); + } + + // clean the buffer if mic_mute is on and input is not for fm. + if ((madev->mic_mute) && (madev->in_device != AUDIO_DEVICE_IN_FMRADIO)) { + memset((unsigned char *)buffer, 0, bytes); + } + + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) { + return 0; +} + +static int mrvl_hw_dev_open_output_stream( + struct audio_hw_device *dev, audio_io_handle_t handle, + audio_devices_t devices, audio_output_flags_t flags, + struct audio_config *config, struct audio_stream_out **stream_out, + const char *address) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + struct mrvl_stream_out *out = NULL; + int ret = 0; + int output_type = 0; + ALOGI("%s: device 0x%x", __FUNCTION__, devices); + + *stream_out = NULL; + + out = (struct mrvl_stream_out *)calloc(1, sizeof(struct mrvl_stream_out)); + if (!out) return -ENOMEM; + + out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + out->sample_rate = SAMPLE_RATE_OUT_DEFAULT; + out->format = AUDIO_FORMAT_PCM_16_BIT; + + if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) { + ALOGV("%s deep buffer", __FUNCTION__); + if (madev->outputs[OUTPUT_DEEP_BUF] != NULL) { + ret = -ENOSYS; + goto err_open; + } + output_type = OUTPUT_DEEP_BUF; + out->period_size = DEEP_BUFFER_LONG_PERIOD_SIZE; + out->period_count = DEEP_BUFFER_LONG_PERIOD_COUNT; + out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + out->stream.common.get_buffer_size = out_get_buffer_size_deep_buffer; + out->stream.get_latency = out_get_latency_deep_buffer; + out->stream.write = out_write_deep_buffer; + } else { + ALOGV("%s low latency", __FUNCTION__); + if (madev->outputs[OUTPUT_LOW_LATENCY] != NULL) { + ret = -ENOSYS; + goto err_open; + } + output_type = OUTPUT_LOW_LATENCY; + out->period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE; + out->period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT; + out->stream.common.get_buffer_size = out_get_buffer_size_low_latency; + out->stream.get_latency = out_get_latency_low_latency; + out->stream.write = out_write_low_latency; + } + + out->stream.common.standby = out_standby; + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect_fake; + out->stream.common.remove_audio_effect = out_remove_audio_effect_fake; + out->stream.get_render_position = out_get_render_position; + out->stream.get_presentation_position = out_get_presentation_position; + out->stream.set_volume = out_set_volume; + out->handle = NULL; + out->standby = true; + out->io_handle = handle; + out->written = 0; + + config->format = out->stream.common.get_format(&out->stream.common); + config->channel_mask = out->stream.common.get_channels(&out->stream.common); + config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common); + + pthread_mutex_init(&out->lock, NULL); + out->dev = madev; + *stream_out = &out->stream; + + pthread_mutex_lock(&madev->lock); + madev->outputs[output_type] = out; +#ifdef ROUTE_SPEAKER_TO_HEADSET + if (devices == AUDIO_DEVICE_OUT_SPEAKER) + madev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADSET; + else + madev->out_device = devices; +#else + madev->out_device = devices; +#endif + pthread_mutex_unlock(&madev->lock); + + list_init(&out->effect_interfaces); + +#ifdef WITH_ACOUSTIC + out->acoustic_manager = 0; +#endif + + ALOGI("%s: success. out %p", __FUNCTION__, out); + + return 0; + +err_open: + free(out); + return ret; +} + +static void mrvl_hw_dev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + struct mrvl_stream_out *out = (struct mrvl_stream_out *)stream; + + ALOGI("%s: out %p", __FUNCTION__, out); + + // standby current outputs + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&out->lock); + + do_output_standby(out); + +#ifdef WITH_ACOUSTIC + acoustic_manager_destroy(out->acoustic_manager); + out->acoustic_manager = 0; +#endif + + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&madev->lock); + + // clear mrvl dev outputs array + if (out == madev->outputs[OUTPUT_LOW_LATENCY]) { + madev->outputs[OUTPUT_LOW_LATENCY] = NULL; + } else if (out == madev->outputs[OUTPUT_DEEP_BUF]) { + madev->outputs[OUTPUT_DEEP_BUF] = NULL; + } + + pthread_mutex_destroy(&out->lock); + free(out); +} + +static int mrvl_hw_dev_set_parameters(struct audio_hw_device *dev, + const char *kvpairs) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + struct str_parms *param = NULL; + char value[32] = {0}; + int intvalue = 0; + const char *key = NULL; + int ret = 0; + enum BT_STATUS_UPDATE bt_status_update = BT_UPDATE_NONE; + bool new_sw_nrec_mode = true; + int new_bt_headset_type = BT_NB; + int is_reinit_audio = 0; + + ALOGI("%s: %s", __FUNCTION__, kvpairs); + + if (strlen(kvpairs) == 0) return -1; + param = str_parms_create_str(kvpairs); + if (!param) { + ALOGW("%s: param create str is null!", __FUNCTION__); + return -1; + } + pthread_mutex_lock(&madev->lock); + + // set TTY mode + key = AUDIO_PARAMETER_KEY_TTY_MODE; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + int tty_mode = TTY_MODE_OFF; + + if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0) + tty_mode = TTY_MODE_OFF; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0) + tty_mode = TTY_MODE_FULL; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0) + tty_mode = TTY_MODE_HCO; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0) + tty_mode = TTY_MODE_VCO; + + if (tty_mode != madev->tty_mode) { + if (madev->mode != AUDIO_MODE_IN_CALL) { + // Do NOT update TTY mode during voice call; user can't do it, + // and it could occur only due to headset in/out. In case of headset + // out/in during TTY call, the tty mode change should be ignored + madev->tty_mode = tty_mode; + } else { + select_output_device(madev); + } + } + str_parms_del(param, key); + } + + // set BT nrec + key = AUDIO_PARAMETER_KEY_BT_NREC; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) { + new_sw_nrec_mode = true; + ALOGI("%s: SW_NREC on board is ON. NREC on BT Headset is unsupported", + __FUNCTION__); + } else { + new_sw_nrec_mode = false; + ALOGI("%s: SW_NREC on board is OFF. NREC on BT Headset is supported", + __FUNCTION__); + } + if (madev->use_sw_nrec != new_sw_nrec_mode) { + bt_status_update |= BT_UPDATE_NREC_MODE; + } + str_parms_del(param, key); + } + + // set BT WB/NB mode + key = AUDIO_PARAMETER_KEY_BT_SCO_WB; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + new_bt_headset_type = + strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0 ? BT_WB : BT_NB; + if (new_bt_headset_type != madev->bt_headset_type) { + bt_status_update |= BT_UPDATE_HEADSET_TYPE; + } + } + + if (bt_status_update != BT_UPDATE_NONE) { + ALOGI( + "%s: disable old sco device and virtual path before enabling new one.", + __FUNCTION__); + audio_devices_t bt_sco_out_device = + madev->out_device & AUDIO_DEVICE_OUT_ALL_SCO; + if (bt_sco_out_device) { + madev->out_device &= ~bt_sco_out_device; + select_output_device(madev); + } + if (bt_status_update & BT_UPDATE_NREC_MODE) { + madev->use_sw_nrec = new_sw_nrec_mode; + ALOGI("%s: update BT routing for NREC mode: %d", __FUNCTION__, + new_sw_nrec_mode); + } + if (bt_status_update & BT_UPDATE_HEADSET_TYPE) { + madev->bt_headset_type = new_bt_headset_type; + ALOGI("%s: update BT routing for dev type: %s", __FUNCTION__, + new_bt_headset_type == BT_WB ? "BT_WB" : "BT_NB"); + } + ALOGI("%s: enable new sco device and virtual path", __FUNCTION__); + madev->out_device |= bt_sco_out_device; + select_output_device(madev); + } + + // set extra volume + key = EXTRA_VOL; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + bool status = (strcmp(value, "true") == 0 ? true : false); + if (madev->use_extra_vol != status) { + ALOGI("Extra volume mode is %s.", status ? "on" : "off"); + if (madev->mode == AUDIO_MODE_IN_CALL) { + unsigned int params = get_call_params(madev); +#ifdef WITH_TELEPHONY + vcm_mute_all(true, get_cpmute_rampdown_level()); + vcm_select_path(madev->out_device, madev->in_device, params); + set_voice_call_volume(madev, madev->voice_volume, status); + vcm_mute_all(false, get_cpmute_rampup_level()); +#endif + } + madev->use_extra_vol = status; + } + } + + // mute all rx sound + key = MUTE_ALL_RX; + if (str_parms_get_int(param, key, &intvalue) == 0) { + if (mrvl_path_manager.mute_all_rx != (bool)intvalue) { + // mute all RX: 1, unmute all Rx: 0 + mrvl_path_manager.mute_all_rx = (bool)intvalue; + ALOGI("%s mute all Rx sound is %s.", __FUNCTION__, + intvalue ? "true" : "false"); + if (mrvl_path_manager.mute_all_rx) { + route_devices(madev, mrvl_path_manager.active_out_device, + METHOD_DISABLE, 0); + select_virtual_path(madev); + } else { + if (mrvl_path_manager.active_out_device) { + route_devices(madev, mrvl_path_manager.active_out_device, + METHOD_ENABLE, 0); + select_virtual_path(madev); + } + } + } + } + + // set screen state + key = AUDIO_PARAMETER_KEY_SCREEN_STATE; + if ((ret = str_parms_get_str(param, key, value, sizeof(value))) >= 0) { + ALOGI("%s: set screen state [%s].", __FUNCTION__, value); + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) { + madev->screen_off = false; + } else { + madev->screen_off = true; + } + } + + // set loopback paramters,loopback mode setting select cp/app/hardware + // loopback type; + // Input/output device setting select specific input/output device during + // loopback; + // headset flag paramters used for selecting left-headset,right-heaset or + // stereo headset. + if ((ret = str_parms_get_str(param, LOOPBACK, value, sizeof(value))) >= 0) { + if (strcmp(value, "on") == 0) { + if (str_parms_get_int(param, LOOPBACK_INPUT_DEVICE, &intvalue) >= 0) { + intvalue = intvalue | AUDIO_DEVICE_BIT_IN; + ALOGI("%s: input device 0x%x", __FUNCTION__, intvalue); + madev->loopback_param.in_device = intvalue; + str_parms_del(param, LOOPBACK_INPUT_DEVICE); + } + if (str_parms_get_int(param, LOOPBACK_OUTPUT_DEVICE, &intvalue) >= 0) { + ALOGI("%s: output device 0x%x", __FUNCTION__, intvalue); + madev->loopback_param.out_device = intvalue; + str_parms_del(param, LOOPBACK_OUTPUT_DEVICE); + } + if (str_parms_get_int(param, LOOPBACK_HEADSET_FLAG, &intvalue) >= 0) { + ALOGI("%s: headset flag %d", __FUNCTION__, intvalue); + madev->loopback_param.headset_flag = intvalue; + str_parms_del(param, LOOPBACK_HEADSET_FLAG); + } + if (str_parms_get_int(param, LOOPBACK_MODE_SETTTING, &intvalue) >= 0) { + ALOGI("%s: loopback mode %d", __FUNCTION__, intvalue); + madev->loopback_param.type = intvalue; + str_parms_del(param, LOOPBACK_MODE_SETTTING); + } + select_loopback(madev, true); + } else { + select_loopback(madev, false); + } + str_parms_del(param, LOOPBACK); + } + + // set voice call EQ parameters + char eq_value[100]; + key = VOICE_USER_EQ; + if ((str_parms_get_str(param, key, eq_value, sizeof(eq_value))) >= 0) { +#ifdef WITH_TELEPHONY + vcm_set_user_eq(eq_value, sizeof(eq_value)); +#endif + str_parms_del(param, key); + } + + // set microphone mode + key = MICROPHONE_MODE; + if (str_parms_get_int(param, key, &intvalue) == 0) { + if ((unsigned int)intvalue != mrvl_path_manager.mic_mode) { + ALOGD("%s: MIC User-Selection changed MIC to %d", __FUNCTION__, intvalue); + mrvl_path_manager.mic_mode = intvalue; + is_reinit_audio = 1; + } + str_parms_del(param, key); + } + + pthread_mutex_unlock(&madev->lock); + str_parms_destroy(param); + + if (is_reinit_audio) { + // reinit the Audio platform config parser + ALOGD( + "%s: User-Selection changed MIC ==> reinit the Audio platform config " + "parser", + __FUNCTION__); + init_platform_config(); + + // during voice-call, if mic selection changed, switch the input device, and + // update the VCM profile + if (madev->mode == AUDIO_MODE_IN_CALL) { + select_input_device(madev); // update the input device +#ifdef WITH_TELEPHONY + unsigned int params = get_call_params(madev); + vcm_select_path(madev->out_device, madev->in_device, + params); // update the VCM profile +#endif + } + } + + return 0; +} + +static char *mrvl_hw_dev_get_parameters(const struct audio_hw_device *dev, + const char *keys) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + char *ret_val = NULL; + char val_str[32]; + struct str_parms *param; + const char *key; + ALOGI("%s keys %s", __FUNCTION__, keys); + + param = str_parms_create_str(keys); + if (!param) return ret_val; + pthread_mutex_lock(&madev->lock); + + key = EXTRA_VOL; + if (str_parms_get_str(param, key, val_str, sizeof(val_str)) >= 0) { + if (snprintf(val_str, sizeof(val_str), "%s", + madev->use_extra_vol ? "true" : "false") >= 0) { + ret_val = strdup(val_str); + } + } + pthread_mutex_unlock(&madev->lock); + str_parms_destroy(param); + return ret_val; +} + +static int mrvl_hw_dev_init_check(const struct audio_hw_device *dev) { + ALOGI("%s", __FUNCTION__); + return 0; +} + +static int mrvl_hw_dev_set_voice_volume(struct audio_hw_device *dev, + float volume) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + ALOGI("%s volume %f", __FUNCTION__, volume); + madev->voice_volume = volume; + if (madev->mode == AUDIO_MODE_IN_CALL) { + set_voice_call_volume(madev, volume, madev->use_extra_vol); + // set volume value to ACM for volume calibration for voice call. + set_hw_volume(V_MODE_VC, convert2_hwdev(madev, madev->out_device), + (unsigned char)(volume * 100)); + } + return 0; +} + +static int mrvl_hw_dev_set_master_volume(struct audio_hw_device *dev, + float volume) { + return -ENOSYS; +} + +static int mrvl_hw_dev_set_mode(struct audio_hw_device *dev, int mode) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + ALOGI("%s mode %d", __FUNCTION__, mode); + + if (mode < 0 || mode >= AUDIO_MODE_CNT) return -1; + pthread_mutex_lock(&madev->lock); + if (madev->mode != mode) { + select_mode(madev, mode); + } + pthread_mutex_unlock(&madev->lock); + return 0; +} + +static int mrvl_hw_dev_set_mic_mute(struct audio_hw_device *dev, bool state) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + ALOGI("%s: state %s, mic_mute %s", __FUNCTION__, state ? "mute" : "unmute", + madev->mic_mute ? "mute" : "unmute"); + if (state != madev->mic_mute) { +#ifdef WITH_TELEPHONY + unsigned char ramp_level = 0; + if (state) { + ramp_level = get_cpmute_rampdown_level(); + } else { + ramp_level = get_cpmute_rampup_level(); + } + vcm_mute_mic(state, ramp_level); +#endif + madev->mic_mute = state; + } + return 0; +} + +static int mrvl_hw_dev_get_mic_mute(const struct audio_hw_device *dev, + bool *state) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + ALOGD("%s: %s", __FUNCTION__, madev->mic_mute ? "mute" : "unmute"); + + *state = madev->mic_mute; + return 0; +} + +static size_t mrvl_hw_dev_get_input_buffer_size( + const struct audio_hw_device *dev, const struct audio_config *config) { + int buffersize = 0; + if (config->format != AUDIO_FORMAT_PCM_16_BIT) { + ALOGW("%s: bad format: %d", __FUNCTION__, config->format); + return 0; + } + + if (config->sample_rate == SAMPLE_RATE_VC_RECORDING) { + buffersize = VC_RECORDING_INPUT_PERIOD_SIZE; + } else { + buffersize = LOW_LATENCY_INPUT_PERIOD_SIZE; + } + + return buffersize * sizeof(uint16_t) * popcount(config->channel_mask); +} + +static int mrvl_hw_dev_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags, + const char *address, + audio_source_t source) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + struct mrvl_stream_in *in; + int input_type = 0; + + if (config != NULL) { + ALOGI("%s req sample rate %d channel mask 0x%x devices 0x%x", __FUNCTION__, + config->sample_rate, config->channel_mask, devices); + } + + *stream_in = NULL; + + in = (struct mrvl_stream_in *)calloc(1, sizeof(struct mrvl_stream_in)); + if (!in) { + ALOGE("%s calloc error", __FUNCTION__); + return -ENOMEM; + } + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect_fake; + in->stream.common.remove_audio_effect = in_remove_audio_effect_fake; + in->stream.set_gain = in_set_gain; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + in->format = ((config != NULL) ? config->format : AUDIO_FORMAT_PCM_16_BIT); + if (devices & AUDIO_DEVICE_IN_VOICE_CALL & ~AUDIO_DEVICE_BIT_IN) { + // phone plug-in only output 16K Mono stream + in->sample_rate = SAMPLE_RATE_VC_RECORDING; + in->period_size = VC_RECORDING_INPUT_PERIOD_SIZE; + in->channel_mask = AUDIO_CHANNEL_IN_MONO; + in->stream.read = in_read_vc_rec; + in->stream.common.standby = in_standby_vc_rec; + input_type = INPUT_VC; + } else { + in->sample_rate = SAMPLE_RATE_IN_DEFAULT; + in->period_size = LOW_LATENCY_INPUT_PERIOD_SIZE; + in->period_count = LOW_LATENCY_INPUT_PERIOD_COUNT; + in->channel_mask = AUDIO_CHANNEL_IN_STEREO; + in->stream.read = in_read_primary; + in->stream.common.standby = in_standby_primary; + input_type = INPUT_PRIMARY; + } + in->handle = NULL; + in->standby = true; + in->io_handle = handle; + + pthread_mutex_init(&in->lock, NULL); + madev->in_device = devices; + + in->dev = madev; + *stream_in = &in->stream; + madev->inputs[input_type] = in; + + list_init(&in->effect_interfaces); + +#ifdef WITH_ACOUSTIC + in->acoustic_manager = 0; +#endif + + if (config != NULL) { + config->format = in->stream.common.get_format(&in->stream.common); + config->channel_mask = in->stream.common.get_channels(&in->stream.common); + config->sample_rate = in->stream.common.get_sample_rate(&in->stream.common); + } + + ALOGI("%s: success. in %p", __FUNCTION__, in); + return 0; +} + +static void mrvl_hw_dev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + struct mrvl_stream_in *in = (struct mrvl_stream_in *)stream; + ALOGI("%s", __FUNCTION__); + + pthread_mutex_lock(&madev->lock); + pthread_mutex_lock(&in->lock); + + do_input_standby(in); + +#ifdef WITH_ACOUSTIC + acoustic_manager_destroy(in->acoustic_manager); + in->acoustic_manager = 0; +#endif + + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&madev->lock); + + // clear mrvl dev inputs array + if (in == madev->inputs[INPUT_PRIMARY]) { + madev->inputs[INPUT_PRIMARY] = NULL; + } else if (in == madev->inputs[INPUT_VC]) { + madev->inputs[INPUT_VC] = NULL; + } + + pthread_mutex_destroy(&in->lock); + free(stream); +} + +static int mrvl_hw_dev_dump(const audio_hw_device_t *device, int fd) { + const size_t SIZE = 256; + char buffer[SIZE]; + char result[SIZE]; + snprintf(buffer, SIZE, "dump()"); + strcpy(result, buffer); + write(fd, result, strlen(result)); + return 0; +} + +static int mrvl_hw_dev_create_audio_patch( + struct audio_hw_device *dev, unsigned int num_sources, + const struct audio_port_config *sources, unsigned int num_sinks, + const struct audio_port_config *sinks, audio_patch_handle_t *handle) { + ALOGI("%s: num_sources %d num_sinks %d handle %d", __FUNCTION__, num_sources, + num_sinks, *handle); + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + bool do_standby = false; + unsigned int i = 0; + audio_devices_t in_dev = 0; + audio_devices_t out_dev = 0; + audio_source_t in_src = 0; + + // limit number of sources to 1 for now + if (num_sources == 0 || num_sources > 1 || num_sinks == 0 || + num_sinks > AUDIO_PATCH_PORTS_MAX) { + return -1; + } + + pthread_mutex_lock(&madev->lock); + + switch (sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + struct mrvl_stream_in *in; + // limit number of sinks to 1 for now + if (num_sinks > 1) { + pthread_mutex_unlock(&madev->lock); + return -1; + } + if (sinks[0].type == AUDIO_PORT_TYPE_MIX) { + for (i = 0; i < INPUT_TOTAL; i++) { + in = madev->inputs[i]; + if (in && sinks[0].ext.mix.handle == in->io_handle) { + in_dev = sources[0].ext.device.type; + in_src = sinks[0].ext.mix.usecase.source; + // no audio source uses value 0 + if ((in->source != (int)in_src) && (in_src != 0)) { + in->source = in_src; + } + if ((madev->in_device != in_dev) && (in_dev != 0) && + (in_dev != AUDIO_DEVICE_IN_VOICE_CALL) && + (!is_in_voip_vt(madev->mode))) { + madev->in_device = in_dev; + select_input_device(madev); + } + +#ifdef WITH_ACOUSTIC + if ((madev->in_device & ~AUDIO_DEVICE_BIT_IN) && + (in->source != AUDIO_SOURCE_VOICE_RECOGNITION)) { + acoustic_manager_init(&(in->acoustic_manager), in->sample_rate, + popcount(in->channel_mask), + madev->in_device); + } +#endif + } + } + } else { + // TODO: device to device patch + } + } break; + case AUDIO_PORT_TYPE_MIX: { + struct mrvl_stream_out *out; + // limit to connections between devices and output streams + for (i = 0; i < num_sinks; i++) { + if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGW("%s: invalid sink type %d for bus source", __FUNCTION__, + sinks[i].type); + pthread_mutex_unlock(&madev->lock); + return -1; + } + } + for (i = 0; i < OUTPUT_TOTAL; i++) { + out = madev->outputs[i]; + if (out && sources[0].ext.mix.handle == out->io_handle) { + for (i = 0; i < num_sinks; i++) { + out_dev |= sinks[i].ext.device.type; + } +#ifdef ROUTE_SPEAKER_TO_HEADSET + if (out_dev == AUDIO_DEVICE_OUT_SPEAKER){ + ALOGI("%s: convert Speaker audio source to Headset audio source", __FUNCTION__); + out_dev = AUDIO_DEVICE_OUT_WIRED_HEADSET; + } +#endif + madev->out_device = out_dev; + + select_output_device(madev); + // set volume value to ACM for volume calibration for voice call. + if (madev->mode == AUDIO_MODE_IN_CALL) { + set_hw_volume(V_MODE_VC, convert2_hwdev(madev, madev->out_device), + madev->voice_volume * 100); + } +#ifdef MRVL_AEC + if (out == madev->outputs[OUTPUT_LOW_LATENCY] && + is_in_voip_vt(madev->mode)) { + uint32_t audio_profile = get_profile(madev); + if (audio_profile != AUDIO_PROFILE_ID_ENUM_32_BIT) { + effect_set_profile(audio_profile, madev); + } + } +#endif +#ifdef WITH_ACOUSTIC + if (madev->out_device) { + acoustic_manager_init(&(out->acoustic_manager), out->sample_rate, + popcount(out->channel_mask), + madev->out_device); + } +#endif + } + } + } break; + default: + ALOGW("%s: invalid source type %d ", __FUNCTION__, sources[0].type); + pthread_mutex_unlock(&madev->lock); + return 0; + } + + struct mrvl_audio_patch *m_patch = calloc(1, sizeof(struct mrvl_audio_patch)); + if (m_patch == NULL) { + ALOGE("%s, memory allocation fail!", __FUNCTION__); + pthread_mutex_unlock(&madev->lock); + return -1; + } + *handle = (int)android_atomic_inc(&madev->unique_id); + m_patch->patch_handle = *handle; + m_patch->num_sources = num_sources; + for (i = 0; i < num_sources; i++) { + m_patch->sources[i] = sources[i]; + } + m_patch->num_sinks = num_sinks; + for (i = 0; i < num_sinks; i++) { + m_patch->sinks[i] = sinks[i]; + } + list_add_tail(&madev->audio_patches, &m_patch->link); + + pthread_mutex_unlock(&madev->lock); + return 0; +} + +static int mrvl_hw_dev_release_audio_patch(struct audio_hw_device *dev, + audio_patch_handle_t handle) { + ALOGI("%s: audio patch handle is %d", __FUNCTION__, handle); + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)dev; + bool do_standby = false; + unsigned int i = 0; + audio_devices_t in_dev = 0; + audio_devices_t out_dev = 0; + + struct listnode *plist = NULL; + struct listnode *tmp_node = NULL; + struct mrvl_audio_patch *tmp_patch = NULL; + + pthread_mutex_lock(&madev->lock); + + list_for_each_safe(plist, tmp_node, &madev->audio_patches) { + tmp_patch = node_to_item(plist, struct mrvl_audio_patch, link); + if (tmp_patch != NULL && tmp_patch->patch_handle == handle) { + switch (tmp_patch->sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + struct mrvl_stream_in *in; + if (tmp_patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { + for (i = 0; i < INPUT_TOTAL; i++) { + in = madev->inputs[i]; + if (in && tmp_patch->sinks[0].ext.mix.handle == in->io_handle) { + // current do nothing and let in_standby to disable path + } + } + } else { + // TODO: device to device patch + } + } break; + case AUDIO_PORT_TYPE_MIX: { + struct mrvl_stream_out *out; + for (i = 0; i < OUTPUT_TOTAL; i++) { + out = madev->outputs[i]; + if (out && tmp_patch->sources[0].ext.mix.handle == out->io_handle) { + // current do nothing and let out_standby to disable path + } + } + } break; + default: + ALOGW("%s: invalid source type %d ", __FUNCTION__, + tmp_patch->sources[0].type); + pthread_mutex_unlock(&madev->lock); + return 0; + } + list_remove(&tmp_patch->link); + free(tmp_patch); + tmp_patch = NULL; + } + } + + pthread_mutex_unlock(&madev->lock); + return 0; +} + +static int mrvl_hw_dev_get_audio_port(struct audio_hw_device *dev, + struct audio_port *port) { + ALOGI("%s", __FUNCTION__); + return 0; +} + +static int mrvl_hw_dev_set_audio_port_config( + struct audio_hw_device *dev, const struct audio_port_config *config) { + ALOGI("%s", __FUNCTION__); + return 0; +} + +static int mrvl_hw_dev_close(hw_device_t *device) { + struct mrvl_audio_device *madev = (struct mrvl_audio_device *)device; + + // Deinit acm module + ACMDeInit(); + + // clean path manager + struct listnode *plist = NULL; + struct listnode *tmp_node = NULL; + struct virtual_path *tmp_vpath = NULL; + list_for_each_safe(plist, tmp_node, &mrvl_path_manager.out_virtual_path) { + tmp_vpath = node_to_item(plist, struct virtual_path, link); + if (tmp_vpath != NULL) { + list_remove(&tmp_vpath->link); + free(tmp_vpath); + tmp_vpath = NULL; + } + } + list_for_each_safe(plist, tmp_node, &mrvl_path_manager.in_virtual_path) { + tmp_vpath = node_to_item(plist, struct virtual_path, link); + if (tmp_vpath != NULL) { + list_remove(&tmp_vpath->link); + free(tmp_vpath); + tmp_vpath = NULL; + } + } + + deinit_platform_config(); + + pthread_mutex_destroy(&madev->lock); + free(madev); + + return 0; +} + +static uint32_t mrvl_hw_dev_get_supported_devices( + const struct audio_hw_device *dev) { + return ( // OUT + AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_AUX_DIGITAL | AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_ALL_SCO | AUDIO_DEVICE_OUT_DEFAULT | + // IN + AUDIO_DEVICE_IN_COMMUNICATION | AUDIO_DEVICE_IN_AMBIENT | + AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_WIRED_HEADSET | + AUDIO_DEVICE_IN_AUX_DIGITAL | AUDIO_DEVICE_IN_VOICE_CALL | + AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_VT_MIC | + AUDIO_DEVICE_IN_FMRADIO | AUDIO_DEVICE_IN_ALL_SCO | + AUDIO_DEVICE_IN_DEFAULT); +} + +static int mrvl_hw_dev_open(const hw_module_t *module, const char *name, + hw_device_t **device) { + struct mrvl_audio_device *madev = NULL; + int ap_mode = 0; + int mDev = 0; + char value[256]; + ALOGI("%s name %s", __FUNCTION__, name); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL; + + madev = + (struct mrvl_audio_device *)calloc(1, sizeof(struct mrvl_audio_device)); + if (!madev) return -ENOMEM; + + madev->device.common.tag = HARDWARE_DEVICE_TAG; + madev->device.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; + madev->device.common.module = (struct hw_module_t *)module; + madev->device.common.close = mrvl_hw_dev_close; + + madev->device.get_supported_devices = mrvl_hw_dev_get_supported_devices; + madev->device.init_check = mrvl_hw_dev_init_check; + madev->device.set_voice_volume = mrvl_hw_dev_set_voice_volume; + madev->device.set_master_volume = mrvl_hw_dev_set_master_volume; + madev->device.set_mode = mrvl_hw_dev_set_mode; + madev->device.set_mic_mute = mrvl_hw_dev_set_mic_mute; + madev->device.get_mic_mute = mrvl_hw_dev_get_mic_mute; + madev->device.set_parameters = mrvl_hw_dev_set_parameters; + madev->device.get_parameters = mrvl_hw_dev_get_parameters; + madev->device.get_input_buffer_size = mrvl_hw_dev_get_input_buffer_size; + madev->device.open_output_stream = mrvl_hw_dev_open_output_stream; + madev->device.close_output_stream = mrvl_hw_dev_close_output_stream; + madev->device.open_input_stream = mrvl_hw_dev_open_input_stream; + madev->device.close_input_stream = mrvl_hw_dev_close_input_stream; + madev->device.dump = mrvl_hw_dev_dump; + madev->device.create_audio_patch = mrvl_hw_dev_create_audio_patch; + madev->device.release_audio_patch = mrvl_hw_dev_release_audio_patch; + madev->device.get_audio_port = mrvl_hw_dev_get_audio_port; + madev->device.set_audio_port_config = mrvl_hw_dev_set_audio_port_config; + + pthread_mutex_init(&madev->lock, NULL); + madev->mode = AUDIO_MODE_NORMAL; + madev->voice_volume = 0.9f; + madev->fm_volume = 0; + madev->out_device = AUDIO_DEVICE_OUT_SPEAKER; + madev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC; + madev->fm_device = 0; + strcpy(madev->plug_type, "plug:phone"); + madev->in_call = false; + madev->in_fm = false; + madev->in_hfp = false; + madev->mic_mute = false; + madev->tty_mode = (int)TTY_MODE_OFF; + + madev->use_sw_nrec = true; + madev->screen_off = true; + + madev->phone_dl_handle = NULL; + madev->phone_ul_handle = NULL; + + madev->hfp_out_handle = NULL; + madev->hfp_in_handle = NULL; + + madev->bt_headset_type = BT_NB; + madev->use_extra_vol = false; + madev->use_voice_recognition = false; + madev->unique_id = 1; + + madev->loopback_param.on = false; + madev->loopback_param.in_device = AUDIO_DEVICE_IN_BUILTIN_MIC; + madev->loopback_param.out_device = AUDIO_DEVICE_OUT_EARPIECE; + madev->loopback_param.headset_flag = STEREO_HEADSET; + madev->loopback_param.type = CP_LOOPBACK; + + // initialize mrvl path manager + list_init(&mrvl_path_manager.in_virtual_path); + list_init(&mrvl_path_manager.out_virtual_path); + + mrvl_path_manager.mic_mode = MIC_MODE_NONE; + + list_init(&madev->audio_patches); + +#ifdef WITH_ACOUSTIC + acoustic_manager_load_config(); +#endif + + // Init acm module + ACMInit(); + init_platform_config(); + + *device = &madev->device.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = mrvl_hw_dev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "Marvll audio HW HAL", + .author = "Marvell APSE/SE1-Audio", + .methods = &hal_module_methods, + }, +}; diff --git a/peripheral/audio/driver/audio_hw_mrvl.h b/peripheral/audio/driver/audio_hw_mrvl.h new file mode 100644 index 0000000..b0cafba --- /dev/null +++ b/peripheral/audio/driver/audio_hw_mrvl.h @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __AUDIO_HW_MRVL_H__ +#define __AUDIO_HW_MRVL_H__ + +#include <hardware/audio.h> +#include <system/audio.h> +#include <echo_reference.h> +#include <cutils/list.h> +#include <cutils/atomic.h> +#include <pthread.h> +#include <tinyalsa/asoundlib.h> + +#include "audio_path.h" +#include "audio_profile.h" + +enum bt_headset_type { + BT_NB, + BT_WB, +}; + +enum loopback_type { + CP_LOOPBACK = 1, + HARDWARE_LOOPBACK, + APP_LOOPBACK, +}; + +enum headset_type { + STEREO_HEADSET, + MONO_HEADSET_L, + MONO_HEADSET_R, +}; + +//#define STREAM_DUMP_DEBUG +#define PARAM_SIZE 20 + +#define ALSA_CARD_DEFAULT 0 + +#define ALSA_DEVICE_DEFAULT 0 +#ifdef WITH_MAP_LITE +#define ALSA_DEVICE_VOICE 1 +#define ALSA_DEVICE_FM 2 +#define ALSA_DEVICE_HFP 3 +// no this device on MAP-Lite, +// keep it as invalid value to compile successfully. +#define ALSA_DEVICE_DEEP_BUFFER 0xFF +#else +#define ALSA_DEVICE_DEEP_BUFFER 1 +#define ALSA_DEVICE_VOICE 2 +#define ALSA_DEVICE_FM 3 +#define ALSA_DEVICE_HFP 4 +#endif + +#define SAMPLE_RATE_OUT_DEFAULT 44100 +#define SAMPLE_RATE_IN_DEFAULT 44100 +#define SAMPLE_RATE_PHONE 16000 +#define SAMPLE_RATE_VC_RECORDING 16000 +#define SAMPLE_RATE_OUT_FM 48000 +#define SAMPLE_RATE_HFP_WB 16000 +#define SAMPLE_RATE_HFP_NB 8000 + +// definition of output/input period and buffer size in frame counts +// must be multiple of 16 !!! +#ifdef WITH_MAP_LITE +#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 1024 +#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 3 +#else +#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 512 +#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 3 +#endif + +#define DEEP_BUFFER_SHORT_PERIOD_SIZE 1024 +#define DEEP_BUFFER_SHORT_PERIOD_COUNT 3 + +#define DEEP_BUFFER_LONG_PERIOD_SIZE 4096 +#define DEEP_BUFFER_LONG_PERIOD_COUNT 2 + +#define LOW_LATENCY_INPUT_PERIOD_SIZE 512 +#define LOW_LATENCY_INPUT_PERIOD_COUNT 4 + +#define PHONE_OUTPUT_PERIOD_SIZE 320 +#define PHONE_OUTPUT_PERIOD_COUNT 2 + +#define FM_OUTPUT_PERIOD_SIZE 160 +#define FM_OUTPUT_PERIOD_COUNT 2 + +#define HFP_OUTPUT_PERIOD_SIZE 160 +#define HFP_OUTPUT_PERIOD_COUNT 2 + +#define VC_RECORDING_INPUT_PERIOD_SIZE 320 + +// minimum sleep time in out_write() when write threshold is not reached +#define MIN_WRITE_SLEEP_US 5000 + +#ifndef WITH_ADVANCED_AUDIO +#define AUDIO_PARAMETER_STREAM_HW_VOLUME "hardware_volume" // uint32_t +#define AUDIO_PARAMETER_FM_STATUS "FM_STATUS" +#define AUDIO_PARAMETER_FM_DEVICE "FM_DEVICE" +#define AUDIO_PARAMETER_FM_VOLUME "FM_VOLUME" +typedef enum { FM_DISABLE = 0, FM_ENABLE, FM_SETVOLUME } fm_status; + +#define AUDIO_STREAM_FM AUDIO_STREAM_DEFAULT +#define AUDIO_SOURCE_FMRADIO AUDIO_SOURCE_DEFAULT +#define AUDIO_SOURCE_VOICE_VT_CALL AUDIO_SOURCE_DEFAULT +#define AUDIO_DEVICE_OUT_FM_HEADPHONE AUDIO_DEVICE_OUT_DEFAULT +#define AUDIO_DEVICE_OUT_FM_SPEAKER (AUDIO_DEVICE_OUT_DEFAULT + 1) +#define AUDIO_DEVICE_IN_FMRADIO AUDIO_DEVICE_IN_DEFAULT +#define AUDIO_DEVICE_IN_VT_MIC AUDIO_DEVICE_IN_DEFAULT +#define AUDIO_MODE_IN_VT_CALL AUDIO_MODE_CURRENT +#endif + +#define VCM_EXTRA_VOL 0x00000001 +#define VCM_BT_NREC_OFF 0x00000002 +#define VCM_BT_WB 0x00000004 +#define VCM_TTY_FULL 0x00000008 +#define VCM_TTY_HCO 0x00000010 +#define VCM_TTY_VCO 0x00000020 +#define VCM_TTY_VCO_DUALMIC 0x00000040 +#define VCM_DUAL_MIC 0x00000080 + +/* TTY mode selection */ +enum tty_modes { TTY_MODE_OFF = 0, TTY_MODE_FULL, TTY_MODE_HCO, TTY_MODE_VCO }; + +static char *const EXTRA_VOL = "Extra_volume"; +static char *const MUTE_ALL_RX = "mute_voice_Rx"; +static char *const VOICE_USER_EQ = "dha"; +static char *const MICROPHONE_MODE = "microphone_mode"; +static char *const LOOPBACK = "Loopback"; +static char *const LOOPBACK_INPUT_DEVICE = "input_dev"; +static char *const LOOPBACK_OUTPUT_DEVICE = "output_dev"; +static char *const LOOPBACK_HEADSET_FLAG = "hs_flag"; +static char *const LOOPBACK_MODE_SETTTING = "loopback_mode"; + +enum output_type { + OUTPUT_LOW_LATENCY, // low latency output stream + OUTPUT_DEEP_BUF, // deep PCM buffers output stream + OUTPUT_HDMI, + OUTPUT_TOTAL +}; + +enum input_type { + INPUT_PRIMARY, // primary input stream + INPUT_VC, // voice call recording input stream + INPUT_TOTAL +}; + +struct mrvl_audio_patch { + audio_patch_handle_t patch_handle; + unsigned int num_sources; + struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX]; + unsigned int num_sinks; + struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX]; + struct listnode link; +}; + +struct virtual_mode { + virtual_mode_t v_mode; + bool is_priority_highest; // whether virtual mode priority is the highest + struct listnode link; +}; + +struct virtual_path { + virtual_mode_t v_mode; + bool is_priority_highest; // whether virtual path priority is the highest + int path_device; // can be input(without mask) or output device + struct listnode link; +}; + +struct mrvl_path_status { + bool itf_state[ID_IPATH_TX_MAX + 1]; // output&input interface state + bool mute_all_rx; // all Rx sound should be muted + uint32_t mic_mode; // mic mode in marvell settings + uint32_t active_out_device; + uint32_t active_in_device; + uint32_t enabled_in_hwdev; + struct listnode out_virtual_path; + struct listnode in_virtual_path; +}; + +struct mrvl_stream_out { + struct audio_stream_out stream; + struct mrvl_audio_device *dev; + audio_format_t format; + audio_channel_mask_t channel_mask; + uint32_t sample_rate; + pthread_mutex_t lock; + struct pcm *handle; + unsigned int period_size; // frame counts + unsigned int period_count; // counts of period + unsigned int written; // total frames written + bool standby; + int write_threshold; + bool use_long_periods; + struct listnode effect_interfaces; + audio_io_handle_t io_handle; + +#ifdef WITH_ACOUSTIC + int acoustic_manager; +#endif +}; + +struct mrvl_stream_in { + struct audio_stream_in stream; + struct mrvl_audio_device *dev; + uint32_t format; + audio_channel_mask_t channel_mask; + uint32_t sample_rate; + int source; + pthread_mutex_t lock; + struct pcm *handle; + unsigned int period_size; // frame counts + unsigned int period_count; // counts of period + bool standby; + struct listnode effect_interfaces; + struct echo_reference_buffer ref_buffer; + audio_io_handle_t io_handle; + +#ifdef WITH_ACOUSTIC + int acoustic_manager; +#endif +}; + +struct mrvl_loopback_param { + uint32_t headset_flag; // record headset type of mono and stereo + uint32_t in_device; + uint32_t out_device; + uint32_t type; + bool on; +}; + +struct mrvl_audio_device { + struct audio_hw_device device; + struct mrvl_stream_in *active_input; + struct mrvl_stream_in *inputs[INPUT_TOTAL]; + struct mrvl_stream_out *outputs[OUTPUT_TOTAL]; + uint32_t out_device; + uint32_t in_device; + uint32_t fm_device; + struct mrvl_loopback_param loopback_param; + bool mic_mute; + int tty_mode; + pthread_mutex_t lock; + float voice_volume; + int fm_volume; + int hfp_volume; + audio_mode_t mode; + char plug_type[256]; + bool in_call; // record voice call status + bool in_fm; // record FM status + bool in_hfp; // record HFP status + bool use_sw_nrec; + bool screen_off; + int bt_headset_type; + int unique_id; + bool use_extra_vol; + bool use_voice_recognition; + struct echo_reference_itfe *echo_reference; + struct pcm *phone_dl_handle; + struct pcm *phone_ul_handle; + struct pcm *fm_handle; + struct pcm *hfp_out_handle; + struct pcm *hfp_in_handle; + struct listnode audio_patches; +}; + +#endif /* __AUDIO_HW_MRVL_H__ */ diff --git a/peripheral/audio/driver/audio_path.c b/peripheral/audio/driver/audio_path.c new file mode 100644 index 0000000..41ee02c --- /dev/null +++ b/peripheral/audio/driver/audio_path.c @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "audio_hw_path" +#define LOG_NDEBUG 0 + +#include <system/audio.h> +#include <cutils/log.h> +#include "audio_path.h" +#include "audio_hw_mrvl.h" +#include "acm_api.h" +#include "acm_param.h" +#include "audio_path.h" + +// define the path interface, include playback input and record output path +#define MAX_NUM_ITF (sizeof(gPathInterface) / sizeof(gPathInterface[0])) +static const struct { + path_interface_t i_path; + char *path_name; +} gPathInterface[] = { + // playback path + {ID_IPATH_RX_HIFI_LL, PATH_NAME(HiFi1Playback)}, + {ID_IPATH_RX_HIFI_DB, PATH_NAME(HiFi2Playback)}, + {ID_IPATH_RX_VC, PATH_NAME(VoicePlayback)}, + {ID_IPATH_RX_HFP, PATH_NAME(HfpPlayback)}, + {ID_IPATH_RX_FM, PATH_NAME(FMPlaybackI2S)}, + {ID_IPATH_RX_VC_ST, PATH_NAME(VoicePlaybackST)}, + + // record path + {ID_IPATH_TX_HIFI, PATH_NAME(HiFi1Record)}, + {ID_IPATH_TX_VC, PATH_NAME(VoiceRecord)}, + {ID_IPATH_TX_HFP, PATH_NAME(HfpRecord)}, + {ID_IPATH_TX_FM, PATH_NAME(FMI2SRecord)}, +}; + +// define the path device, include playback output and record input path +#define MAX_NUM_DEV (sizeof(gPathDevice) / sizeof(gPathDevice[0])) +static const struct { + unsigned int device; + unsigned int flag; + char *apu_path; + char *codec_path; +} gPathDevice[] = { + // output devices + {HWDEV_EARPIECE, 0, PATH_NAME(ToEarpiece), PATH_NAME(Earpiece)}, + {HWDEV_SPEAKER, 0, PATH_NAME(ToSpeaker), PATH_NAME(Speaker)}, + {HWDEV_STEREOSPEAKER, 0, PATH_NAME(ToStereoSpeaker), + PATH_NAME(StereoSpeaker)}, + {HWDEV_HEADPHONE, 0, PATH_NAME(ToHeadphone), PATH_NAME(Headphone)}, + {HWDEV_BLUETOOTH_NB, 0, PATH_NAME(ToBTNB), NULL}, + {HWDEV_BT_NREC_OFF_NB, 0, PATH_NAME(ToBTNB(NREC Mode)), NULL}, + {HWDEV_BLUETOOTH_WB, 0, PATH_NAME(ToBTWB), NULL}, + {HWDEV_BT_NREC_OFF_WB, 0, PATH_NAME(ToBTWB(NREC Mode)), NULL}, + {HWDEV_OUT_TTY, 0, PATH_NAME(ToHeadphone), PATH_NAME(Headphone)}, + {HWDEV_OUT_TTY_HCO_SPEAKER, 0, PATH_NAME(ToSpeaker), PATH_NAME(Speaker)}, + {HWDEV_OUT_TTY_HCO_STEREOSPEAKER, 0, PATH_NAME(ToStereoSpeaker), + PATH_NAME(StereoSpeaker)}, + + // input devices + {HWDEV_AMIC1, 0, PATH_NAME(FromAMic1), PATH_NAME(AMic1)}, + {HWDEV_AMIC1_SPK_MODE, 0, PATH_NAME(FromAMic1(Speaker Mode)), + PATH_NAME(AMic1(Speaker Mode))}, + {HWDEV_AMIC1_HP_MODE, 0, PATH_NAME(FromAMic1(Headphone Mode)), + PATH_NAME(AMic1(Headphone Mode))}, + {HWDEV_AMIC2, 0, PATH_NAME(FromAMic2), PATH_NAME(AMic2)}, + {HWDEV_AMIC2_SPK_MODE, 0, PATH_NAME(FromAMic2(Speaker Mode)), + PATH_NAME(AMic2(Speaker Mode))}, + {HWDEV_AMIC2_HP_MODE, 0, PATH_NAME(FromAMic2(Headphone Mode)), + PATH_NAME(AMic2(Headphone Mode))}, + {HWDEV_DUAL_DMIC1, 0, PATH_NAME(FromDualDMic1), PATH_NAME(DualDMic1)}, + {HWDEV_DMIC1, 0, PATH_NAME(FromDMic1), PATH_NAME(DMic1)}, + {HWDEV_DMIC2, 0, PATH_NAME(FromDMic2), PATH_NAME(DMic2)}, + {HWDEV_HSMIC, 0, PATH_NAME(FromHsMic), PATH_NAME(HsMic)}, + {HWDEV_BTMIC_NB, 0, PATH_NAME(FromBTMicNB), NULL}, + {HWDEV_BTMIC_NREC_OFF_NB, 0, PATH_NAME(FromBTMicNB(NREC Mode)), NULL}, + {HWDEV_BTMIC_WB, 0, PATH_NAME(FromBTMicWB), NULL}, + {HWDEV_BTMIC_NREC_OFF_WB, 0, PATH_NAME(FromBTMicWB(NREC Mode)), NULL}, + {HWDEV_DUAL_AMIC, 0, PATH_NAME(FromDualAMic), PATH_NAME(DualAMic)}, + {HWDEV_DUAL_AMIC_SPK_MODE, 0, PATH_NAME(FromDualAMic(Speaker Mode)), + PATH_NAME(DualAMic(Speaker Mode))}, + {HWDEV_DUAL_AMIC_HP_MODE, 0, PATH_NAME(FromDualAMic(Headphone Mode)), + PATH_NAME(DualAMic(Headphone Mode))}, + {HWDEV_IN_TTY, 0, PATH_NAME(FromHsMic), PATH_NAME(HsMic)}, + {HWDEV_IN_TTY_VCO_AMIC1, 0, PATH_NAME(FromAMic1), PATH_NAME(AMic1)}, + {HWDEV_IN_TTY_VCO_AMIC2, 0, PATH_NAME(FromAMic2), PATH_NAME(AMic2)}, + {HWDEV_IN_TTY_VCO_DUAL_AMIC, 0, PATH_NAME(FromDualAMic), + PATH_NAME(DualAMic)}, + {HWDEV_IN_TTY_VCO_DUAL_AMIC_SPK_MODE, 0, + PATH_NAME(FromDualAMic(Speaker Mode)), PATH_NAME(DualAMic(Speaker Mode))}, + {HWDEV_IN_TTY_VCO_DUAL_DMIC1, 0, PATH_NAME(FromDualDMic1), + PATH_NAME(DualDMic1)}, +}; + +// define virtual path +#define MAX_NUM_VRTL (sizeof(gPathVirtual) / sizeof(gPathVirtual[0])) +static const struct { + virtual_mode_t v_mode; + unsigned int device; + unsigned int flag; + char *path_name; +} gPathVirtual[] = { + // HiFi Playback + {V_MODE_HIFI_LL, HWDEV_EARPIECE, 0, PATH_NAME(HiFi1PlaybackToEarpiece)}, + {V_MODE_HIFI_LL, HWDEV_SPEAKER, 0, PATH_NAME(HiFi1PlaybackToSpeaker)}, + {V_MODE_HIFI_LL, HWDEV_STEREOSPEAKER, 0, + PATH_NAME(HiFi1PlaybackToStereoSpeaker)}, + {V_MODE_HIFI_LL, HWDEV_BLUETOOTH_NB, 0, PATH_NAME(HiFi1PlaybackToBTNB)}, + {V_MODE_HIFI_LL, HWDEV_BLUETOOTH_WB, 0, PATH_NAME(HiFi1PlaybackToBTWB)}, + {V_MODE_HIFI_LL, HWDEV_HEADPHONE, 0, PATH_NAME(HiFi1PlaybackToHeadphone)}, + {V_MODE_HIFI_LL, HWDEV_BT_NREC_OFF_NB, 0, + PATH_NAME(HiFi1PlaybackToBTNB(NREC Mode))}, + {V_MODE_HIFI_LL, HWDEV_BT_NREC_OFF_WB, 0, + PATH_NAME(HiFi1PlaybackToBTWB(NREC Mode))}, + + // force speaker mode + {V_MODE_HIFI_LL, HWDEV_SPEAKER | HWDEV_HEADPHONE, 0, + PATH_NAME(HiFi1PlaybackToSpeakerAndHeadphone)}, + {V_MODE_HIFI_LL, HWDEV_STEREOSPEAKER | HWDEV_HEADPHONE, 0, + PATH_NAME(HiFi1PlaybackToStereoSpeakerAndHeadphone)}, + + {V_MODE_HIFI_DB, HWDEV_EARPIECE, 0, PATH_NAME(HiFi2PlaybackToEarpiece)}, + {V_MODE_HIFI_DB, HWDEV_SPEAKER, 0, PATH_NAME(HiFi2PlaybackToSpeaker)}, + {V_MODE_HIFI_DB, HWDEV_STEREOSPEAKER, 0, + PATH_NAME(HiFi2PlaybackToStereoSpeaker)}, + {V_MODE_HIFI_DB, HWDEV_HEADPHONE, 0, PATH_NAME(HiFi2PlaybackToHeadphone)}, + {V_MODE_HIFI_DB, HWDEV_BLUETOOTH_NB, 0, PATH_NAME(HiFi2PlaybackToBTNB)}, + {V_MODE_HIFI_DB, HWDEV_BLUETOOTH_WB, 0, PATH_NAME(HiFi2PlaybackToBTWB)}, + {V_MODE_HIFI_DB, HWDEV_BT_NREC_OFF_NB, 0, + PATH_NAME(HiFi2PlaybackToBTNB(NREC Mode))}, + {V_MODE_HIFI_DB, HWDEV_BT_NREC_OFF_WB, 0, + PATH_NAME(HiFi2PlaybackToBTWB(NREC Mode))}, + + // HiFi Record + {V_MODE_HIFI_LL, HWDEV_AMIC1, 0, PATH_NAME(HiFi1RecordFromAMic1)}, + {V_MODE_HIFI_LL, HWDEV_AMIC2, 0, PATH_NAME(HiFi1RecordFromAMic2)}, + {V_MODE_HIFI_LL, HWDEV_DMIC1, 0, PATH_NAME(HiFi1RecordFromDMic1)}, + {V_MODE_HIFI_LL, HWDEV_DMIC2, 0, PATH_NAME(HiFi1RecordFromDMic2)}, + {V_MODE_HIFI_LL, HWDEV_DUAL_DMIC1, 0, PATH_NAME(HiFi1RecordFromDualDMic1)}, + {V_MODE_HIFI_LL, HWDEV_HSMIC, 0, PATH_NAME(HiFi1RecordFromHsMic)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NB, 0, PATH_NAME(HiFi1RecordFromBTMicNB)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_WB, 0, PATH_NAME(HiFi1RecordFromBTMicWB)}, + {V_MODE_HIFI_LL, HWDEV_DUAL_AMIC, 0, PATH_NAME(HiFi1RecordFromDualAMic)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NREC_OFF_NB, 0, + PATH_NAME(HiFi1RecordFromBTMicNB(NREC Mode))}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NREC_OFF_WB, 0, + PATH_NAME(HiFi1RecordFromBTMicWB(NREC Mode))}, + + // HiFi Record Recognition + {V_MODE_HIFI_LL, HWDEV_AMIC1, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromAMic1)}, + {V_MODE_HIFI_LL, HWDEV_AMIC2, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromAMic2)}, + {V_MODE_HIFI_LL, HWDEV_DMIC1, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromDMic1)}, + {V_MODE_HIFI_LL, HWDEV_DMIC2, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromDMic2)}, + {V_MODE_HIFI_LL, HWDEV_DUAL_DMIC1, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromDualDMic1)}, + {V_MODE_HIFI_LL, HWDEV_HSMIC, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromHsMic)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NB, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromBTMicNB)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_WB, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromBTMicWB)}, + {V_MODE_HIFI_LL, HWDEV_DUAL_AMIC, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromDualAMic)}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NREC_OFF_NB, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromBTMicNB(NREC Mode))}, + {V_MODE_HIFI_LL, HWDEV_BTMIC_NREC_OFF_WB, RECOGNITION, + PATH_NAME(HiFi1RecognitionRecordFromBTMicWB(NREC Mode))}, + + // Voice Call Playback + {V_MODE_VC, HWDEV_EARPIECE, 0, PATH_NAME(VoicePlaybackToEarpiece)}, + {V_MODE_VC, HWDEV_SPEAKER, 0, PATH_NAME(VoicePlaybackToSpeaker)}, + {V_MODE_VC, HWDEV_STEREOSPEAKER, 0, + PATH_NAME(VoicePlaybackToStereoSpeaker)}, + {V_MODE_VC, HWDEV_HEADPHONE, 0, PATH_NAME(VoicePlaybackToHeadphone)}, + {V_MODE_VC, HWDEV_BLUETOOTH_NB, 0, PATH_NAME(VoicePlaybackToBTNB)}, + {V_MODE_VC, HWDEV_BLUETOOTH_WB, 0, PATH_NAME(VoicePlaybackToBTWB)}, + {V_MODE_VC, HWDEV_OUT_TTY, 0, PATH_NAME(VoicePlaybackToTty)}, + {V_MODE_VC, HWDEV_OUT_TTY_HCO_SPEAKER, 0, + PATH_NAME(VoicePlaybackToTtyHcoSpeaker)}, + {V_MODE_VC, HWDEV_OUT_TTY_HCO_STEREOSPEAKER, 0, + PATH_NAME(VoicePlaybackToTtyHcoStereoSpeaker)}, + {V_MODE_VC, HWDEV_BT_NREC_OFF_NB, 0, + PATH_NAME(VoicePlaybackToBTNB(NREC Mode))}, + {V_MODE_VC, HWDEV_BT_NREC_OFF_WB, 0, + PATH_NAME(VoicePlaybackToBTWB(NREC Mode))}, + + // Voice Call Record + {V_MODE_VC, HWDEV_AMIC1, 0, PATH_NAME(VoiceRecordFromAMic1)}, + {V_MODE_VC, HWDEV_AMIC1_SPK_MODE, 0, + PATH_NAME(VoiceRecordFromAMic1(Speaker Mode))}, + {V_MODE_VC, HWDEV_AMIC1_HP_MODE, 0, + PATH_NAME(VoiceRecordFromAMic1(Headphone Mode))}, + {V_MODE_VC, HWDEV_AMIC2, 0, PATH_NAME(VoiceRecordFromAMic2)}, + {V_MODE_VC, HWDEV_AMIC2_SPK_MODE, 0, + PATH_NAME(VoiceRecordFromAMic2(Speaker Mode))}, + {V_MODE_VC, HWDEV_AMIC2_HP_MODE, 0, + PATH_NAME(VoiceRecordFromAMic2(Headphone Mode))}, + {V_MODE_VC, HWDEV_DMIC1, 0, PATH_NAME(VoiceRecordFromDMic1)}, + {V_MODE_VC, HWDEV_DMIC2, 0, PATH_NAME(VoiceRecordFromDMic2)}, + {V_MODE_VC, HWDEV_HSMIC, 0, PATH_NAME(VoiceRecordFromHsMic)}, + {V_MODE_VC, HWDEV_BTMIC_NB, 0, PATH_NAME(VoiceRecordFromBTMicNB)}, + {V_MODE_VC, HWDEV_BTMIC_WB, 0, PATH_NAME(VoiceRecordFromBTMicWB)}, + {V_MODE_VC, HWDEV_DUAL_AMIC, 0, PATH_NAME(VoiceRecordFromDualAMic)}, + {V_MODE_VC, HWDEV_DUAL_AMIC_SPK_MODE, 0, + PATH_NAME(VoiceRecordFromDualAMic(Speaker Mode))}, + {V_MODE_VC, HWDEV_DUAL_AMIC_HP_MODE, 0, + PATH_NAME(VoiceRecordFromDualAMic(Headphone Mode))}, + {V_MODE_VC, HWDEV_IN_TTY, 0, PATH_NAME(VoiceRecordFromTty)}, + {V_MODE_VC, HWDEV_IN_TTY_VCO_AMIC1, 0, + PATH_NAME(VoiceRecordFromTtyVcoAMic1)}, + {V_MODE_VC, HWDEV_IN_TTY_VCO_AMIC2, 0, + PATH_NAME(VoiceRecordFromTtyVcoAMic2)}, + {V_MODE_VC, HWDEV_IN_TTY_VCO_DUAL_AMIC, 0, + PATH_NAME(VoiceRecordFromTtyVcoDualAMic)}, + {V_MODE_VC, HWDEV_IN_TTY_VCO_DUAL_AMIC_SPK_MODE, 0, + PATH_NAME(VoiceRecordFromTtyVcoDualAMic(Speaker Mode))}, + {V_MODE_VC, HWDEV_IN_TTY_VCO_DUAL_DMIC1, 0, + PATH_NAME(VoiceRecordFromTtyVcoDualDMic1)}, + {V_MODE_VC, HWDEV_BTMIC_NREC_OFF_NB, 0, + PATH_NAME(VoiceRecordFromBTMicNB(NREC Mode))}, + {V_MODE_VC, HWDEV_BTMIC_NREC_OFF_WB, 0, + PATH_NAME(VoiceRecordFromBTMicWB(NREC Mode))}, + + // VOIP Playback + {V_MODE_VOIP, HWDEV_SPEAKER, 0, PATH_NAME(VoipPlaybackToSpeaker)}, + {V_MODE_VOIP, HWDEV_STEREOSPEAKER, 0, + PATH_NAME(VoipPlaybackToStereoSpeaker)}, + {V_MODE_VOIP, HWDEV_HEADPHONE, 0, PATH_NAME(VoipPlaybackToHeadphone)}, + {V_MODE_VOIP, HWDEV_EARPIECE, 0, PATH_NAME(VoipPlaybackToEarpiece)}, + {V_MODE_VOIP, HWDEV_BLUETOOTH_NB, 0, PATH_NAME(VoipPlaybackToBTNB)}, + {V_MODE_VOIP, HWDEV_BLUETOOTH_WB, 0, PATH_NAME(VoipPlaybackToBTWB)}, + {V_MODE_VOIP, HWDEV_BT_NREC_OFF_NB, 0, + PATH_NAME(VoipPlaybackToBTNB(NREC Mode))}, + {V_MODE_VOIP, HWDEV_BT_NREC_OFF_WB, 0, + PATH_NAME(VoipPlaybackToBTWB(NREC Mode))}, + + // VOIP Record + {V_MODE_VOIP, HWDEV_AMIC1, 0, PATH_NAME(VoipRecordFromAMic1)}, + {V_MODE_VOIP, HWDEV_AMIC1_SPK_MODE, 0, + PATH_NAME(VoipRecordFromAMic1(Speaker Mode))}, + {V_MODE_VOIP, HWDEV_AMIC1_HP_MODE, 0, + PATH_NAME(VoipRecordFromAMic1(Headhone Mode))}, + {V_MODE_VOIP, HWDEV_AMIC2, 0, PATH_NAME(VoipRecordFromAMic2)}, + {V_MODE_VOIP, HWDEV_AMIC2_SPK_MODE, 0, + PATH_NAME(VoipRecordFromAMic2(Speaker Mode))}, + {V_MODE_VOIP, HWDEV_AMIC2_HP_MODE, 0, + PATH_NAME(VoipRecordFromAMic2(Headphone Mode))}, + {V_MODE_VOIP, HWDEV_DMIC1, 0, PATH_NAME(VoipRecordFromDMic1)}, + {V_MODE_VOIP, HWDEV_DMIC2, 0, PATH_NAME(VoipRecordFromDMic2)}, + {V_MODE_VOIP, HWDEV_HSMIC, 0, PATH_NAME(VoipRecordFromHsMic)}, + {V_MODE_VOIP, HWDEV_BTMIC_NB, 0, PATH_NAME(VoipRecordFromBTMicNB)}, + {V_MODE_VOIP, HWDEV_BTMIC_WB, 0, PATH_NAME(VoipRecordFromBTMicWB)}, + {V_MODE_VOIP, HWDEV_DUAL_AMIC, 0, PATH_NAME(VoipRecordFromDualAMic)}, + {V_MODE_VOIP, HWDEV_DUAL_AMIC_SPK_MODE, 0, + PATH_NAME(VoipRecordFromDualAMic(Speaker Mode))}, + {V_MODE_VOIP, HWDEV_DUAL_AMIC_HP_MODE, 0, + PATH_NAME(VoipRecordFromDualAMic(Headphone Mode))}, + {V_MODE_VOIP, HWDEV_BTMIC_NREC_OFF_NB, 0, + PATH_NAME(VoipRecordFromBTMicNB(NREC Mode))}, + {V_MODE_VOIP, HWDEV_BTMIC_NREC_OFF_WB, 0, + PATH_NAME(VoipRecordFromBTMicWB(NREC Mode))}, + + // VT Playback + {V_MODE_VT, HWDEV_SPEAKER, 0, PATH_NAME(VTPlaybackToSpeaker)}, + {V_MODE_VT, HWDEV_STEREOSPEAKER, 0, PATH_NAME(VTPlaybackToStereoSpeaker)}, + {V_MODE_VT, HWDEV_HEADPHONE, 0, PATH_NAME(VTPlaybackToHeadphone)}, + {V_MODE_VT, HWDEV_EARPIECE, 0, PATH_NAME(VTPlaybackToEarpiece)}, + {V_MODE_VT, HWDEV_BLUETOOTH_NB, 0, PATH_NAME(VTPlaybackToBTNB)}, + {V_MODE_VT, HWDEV_BLUETOOTH_WB, 0, PATH_NAME(VTPlaybackToBTWB)}, + {V_MODE_VT, HWDEV_BT_NREC_OFF_NB, 0, + PATH_NAME(VTPlaybackToBTNB(NREC Mode))}, + {V_MODE_VT, HWDEV_BT_NREC_OFF_WB, 0, + PATH_NAME(VTPlaybackToBTWB(NREC Mode))}, + + // VT Record + {V_MODE_VT, HWDEV_AMIC1, 0, PATH_NAME(VTRecordFromAMic1)}, + {V_MODE_VT, HWDEV_AMIC1_SPK_MODE, 0, + PATH_NAME(VTRecordFromAMic1(Speaker Mode))}, + {V_MODE_VT, HWDEV_AMIC1_HP_MODE, 0, + PATH_NAME(VTRecordFromAMic1(Headphone Mode))}, + {V_MODE_VT, HWDEV_AMIC2, 0, PATH_NAME(VTRecordFromAMic2)}, + {V_MODE_VT, HWDEV_AMIC2_SPK_MODE, 0, + PATH_NAME(VTRecordFromAMic2(Speaker Mode))}, + {V_MODE_VT, HWDEV_AMIC2_HP_MODE, 0, + PATH_NAME(VTRecordFromAMic2(Headphone Mode))}, + {V_MODE_VT, HWDEV_DMIC1, 0, PATH_NAME(VTRecordFromDMic1)}, + {V_MODE_VT, HWDEV_DMIC2, 0, PATH_NAME(VTRecordFromDMic2)}, + {V_MODE_VT, HWDEV_HSMIC, 0, PATH_NAME(VTRecordFromHsMic)}, + {V_MODE_VT, HWDEV_BTMIC_NB, 0, PATH_NAME(VTRecordFromBTMicNB)}, + {V_MODE_VT, HWDEV_BTMIC_WB, 0, PATH_NAME(VTRecordFromBTMicWB)}, + {V_MODE_VT, HWDEV_DUAL_AMIC, 0, PATH_NAME(VTRecordFromDualAMic)}, + {V_MODE_VT, HWDEV_DUAL_AMIC_SPK_MODE, 0, + PATH_NAME(VTRecordFromDualAMic(Speaker Mode))}, + {V_MODE_VT, HWDEV_DUAL_AMIC_HP_MODE, 0, + PATH_NAME(VTRecordFromDualAMic(Headphone Mode))}, + {V_MODE_VT, HWDEV_BTMIC_NREC_OFF_NB, 0, + PATH_NAME(VTRecordFromBTMicNB(NREC Mode))}, + {V_MODE_VT, HWDEV_BTMIC_NREC_OFF_WB, 0, + PATH_NAME(VTRecordFromBTMicWB(NREC Mode))}, + + // HFP + {V_MODE_HFP, HWDEV_SPEAKER, 0, PATH_NAME(HfpPlaybackToSpeaker)}, + {V_MODE_HFP, HWDEV_AMIC2, 0, PATH_NAME(HfpRecordFromAMic2)}, + {V_MODE_HFP, HWDEV_AMIC1, 0, PATH_NAME(HfpRecordFromAMic1)}, + + // FM Playback + {V_MODE_FM, HWDEV_SPEAKER, 0, PATH_NAME(FMPlaybackI2SToSpeaker)}, + {V_MODE_FM, HWDEV_STEREOSPEAKER, 0, + PATH_NAME(FMPlaybackI2SToStereoSpeaker)}, + {V_MODE_FM, HWDEV_HEADPHONE, 0, PATH_NAME(FMPlaybackI2SToHeadphone)}, + + // FM Record + {V_MODE_FM, HWDEV_INVALID, 0, PATH_NAME(FMI2SRecordFromFM)}, + + // CP Loopback + {V_MODE_CP_LOOPBACK, HWDEV_AMIC1, 0, PATH_NAME(VoiceRecordFromAMic1)}, + {V_MODE_CP_LOOPBACK, HWDEV_AMIC2, 0, PATH_NAME(VoiceRecordFromAMic2)}, + {V_MODE_CP_LOOPBACK, HWDEV_DMIC1, 0, PATH_NAME(VoiceRecordFromDMic1)}, + {V_MODE_CP_LOOPBACK, HWDEV_HSMIC, 0, PATH_NAME(VoiceRecordFromHsMic)}, + + // HW Loopback + {V_MODE_HW_LOOPBACK, HWDEV_AMIC1, 0, PATH_NAME(VoiceRecordFromAMic1)}, + {V_MODE_HW_LOOPBACK, HWDEV_AMIC2, 0, PATH_NAME(VoiceRecordFromAMic2)}, + {V_MODE_HW_LOOPBACK, HWDEV_DMIC1, 0, PATH_NAME(VoiceRecordFromDMic1)}, + {V_MODE_HW_LOOPBACK, HWDEV_HSMIC, 0, PATH_NAME(VoiceRecordFromHsMic)}, + + // APP Loopback + {V_MODE_APP_LOOPBACK, HWDEV_AMIC1, 0, PATH_NAME(VoipRecordFromAMic1)}, + {V_MODE_APP_LOOPBACK, HWDEV_AMIC2, 0, PATH_NAME(VoipRecordFromAMic2)}, + {V_MODE_APP_LOOPBACK, HWDEV_DMIC1, 0, PATH_NAME(VoipRecordFromDMic1)}, + {V_MODE_APP_LOOPBACK, HWDEV_HSMIC, 0, PATH_NAME(VoipRecordFromHsMic)}, +}; + +static void handle_ctl_info(char *path_name, int method, char *old_path_name, + int val) { + ACM_ReturnCode ret = ACM_RC_OK; + if (path_name == NULL) { + ALOGI("%s NULL path, return directly", __FUNCTION__); + return; + } + + switch (method) { + case METHOD_ENABLE: + ALOGI("Enable path %s, value is 0x%08x", path_name, val); + ret = ACMAudioPathEnable(path_name, val); + break; + case METHOD_DISABLE: + ALOGI("Disable path %s, value is 0x%08x", path_name, val); + ret = ACMAudioPathHotDisable(path_name, val); + break; + case METHOD_MUTE: + ALOGI("Mute path %s, value is 0x%08x", path_name, val); + ret = ACMAudioPathMute(path_name, val); + break; + case METHOD_VOLUME_SET: + ALOGI("Set Volume for path %s, value = 0x%08x", path_name, val); + ret = ACMAudioPathVolumeSet(path_name, val); + break; + case METHOD_SWITCH: + ALOGI("Switch from old path %s to path %s, value is 0x%08x", + old_path_name, path_name, val); + ret = ACMAudioPathSwitch(old_path_name, path_name, val); + break; + default: + ALOGI("not support method %d\n", method); + break; + } + if (ret != ACM_RC_OK) { + ALOGW("%s: Audio path handling error, method: %d error code: %d", + __FUNCTION__, method, ret); + } +} + +char *get_vrtl_path(virtual_mode_t v_mode, unsigned int hw_dev, + unsigned int flag) { + int i = 0; + + // find ACM path and enable/disable it through ACM + for (i = 0; i < (int)MAX_NUM_VRTL; i++) { + if ((gPathVirtual[i].v_mode == v_mode) && + (gPathVirtual[i].device == hw_dev) && (gPathVirtual[i].flag == flag)) { + ALOGD("%s find path %s", __FUNCTION__, gPathVirtual[i].path_name); + return gPathVirtual[i].path_name; + } + } + + return NULL; +} + +// parameter "flag" is used to search virtual path in audio HAL +// parameter "value" is used for register value setting in ACM +void route_vrtl_path(virtual_mode_t v_mode, int hw_dev, int enable, + unsigned int flag, unsigned int value) { + char *path = NULL; + ALOGI("%s mode %d hw_dev 0x%x %s", __FUNCTION__, v_mode, hw_dev, + (enable == METHOD_ENABLE) ? "enable" : "disable"); + + path = get_vrtl_path(v_mode, hw_dev, flag); + if (path != NULL) { + // set hardware volume to 0 as default. + // for normal Hifi/Voice call path, gain still use fix value + // for force speaker/FM, we set path mute first, set_volume will set correct + // volume + handle_ctl_info(path, enable, NULL, value); + } +} + +void route_hw_device(unsigned int hw_dev, int enable, unsigned int value) { + int i = 0; + ALOGI("%s hw_dev 0x%x %s", __FUNCTION__, hw_dev, + (enable == METHOD_ENABLE) ? "enable" : "disable"); + + // find ACM path and enable/disable it through ACM + for (i = 0; i < (int)MAX_NUM_DEV; i++) { + if ((gPathDevice[i].device == hw_dev) && + ((gPathDevice[i].flag & value) == gPathDevice[i].flag)) { + ALOGD("%s find path %s and %s", __FUNCTION__, gPathDevice[i].apu_path, + gPathDevice[i].codec_path); + if (enable == METHOD_ENABLE) { + handle_ctl_info(gPathDevice[i].apu_path, enable, NULL, value); + handle_ctl_info(gPathDevice[i].codec_path, enable, NULL, value); + } else { + handle_ctl_info(gPathDevice[i].codec_path, enable, NULL, value); + handle_ctl_info(gPathDevice[i].apu_path, enable, NULL, value); + } + } + } +} + +void route_interface(path_interface_t path_itf, int enable) { + unsigned int value = 0; + int i = 0; + ALOGI("%s path_interface %d %s", __FUNCTION__, path_itf, + (enable == METHOD_ENABLE) ? "enable" : "disable"); + + if ((path_itf < ID_IPATH_RX_MIN) || (path_itf > ID_IPATH_TX_MAX)) { + ALOGE("%s Invalid path error %d", __FUNCTION__, path_itf); + return; + } + + // find ACM path and enable/disable it through ACM + for (i = 0; i < (int)MAX_NUM_ITF; i++) { + if (gPathInterface[i].i_path == path_itf) { + ALOGI("%s find path %s", __FUNCTION__, gPathInterface[i].path_name); + handle_ctl_info(gPathInterface[i].path_name, enable, NULL, value); + } + } +} + +// setting hardware volume accroding to virtual mode and hw device +void set_hw_volume(virtual_mode_t v_mode, int hw_dev, unsigned int value) { + char *path = NULL; + + path = get_vrtl_path(v_mode, hw_dev, 0); + + if (path != NULL) { + ALOGI("%s path %s volume set to 0x%x\n", __FUNCTION__, path, value); + handle_ctl_info(path, METHOD_VOLUME_SET, NULL, value); + } +} + +// get MSA gain from ACM xml according to special device +void get_msa_gain(unsigned int hw_dev, unsigned char volume, + signed char *ret_gain, signed char *ret_gain_wb, + bool use_extra_vol) { + int extra_volume = + (use_extra_vol ? MSA_GAIN_EXTRA_MODE : MSA_GAIN_NORMAL_MODE); + signed char tmp_msa_gain = MSA_GAIN_DEFAULT; + signed char tmp_msa_gainwb = MSA_GAIN_DEFAULT; + ACM_MsaGain msa_gain = {0}; + unsigned int size = sizeof(ACM_MsaGain); + + msa_gain.volume = volume; + msa_gain.gain = 0; + msa_gain.wbGain = 0; + msa_gain.path_direction = 0; + + // fix to VC mode here. only VC have msa gain + msa_gain.path_name = + (const unsigned char *)get_vrtl_path(V_MODE_VC, hw_dev, 0); + + if (ACMSetParameter(ACM_MSA_GAIN_MODE, NULL, extra_volume) != ACM_RC_OK) { + ALOGI("set output msa gain mode failed, use the default normal mode "); + } + + if (ACMGetParameter(ACM_MSA_GAIN, &msa_gain, &size) != ACM_RC_OK) { + ALOGE("get output msa gain failed, use default gain."); + } else { + tmp_msa_gain = msa_gain.gain; + tmp_msa_gainwb = msa_gain.wbGain; + } + + *ret_gain = tmp_msa_gain; + *ret_gain_wb = tmp_msa_gainwb; +} diff --git a/peripheral/audio/driver/audio_path.h b/peripheral/audio/driver/audio_path.h new file mode 100644 index 0000000..8b57779 --- /dev/null +++ b/peripheral/audio/driver/audio_path.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __AUDIO_PATH_MRVL_H__ +#define __AUDIO_PATH_MRVL_H__ + +/********************************************************* + * definition of Audio Path info for ACM + *********************************************************/ +#define PATH_NAME(e) #e + +// Audio path operation methods +#define INVALID_METHOD_ID 0xFFFFFFFF +#define METHOD_ENABLE 0x00000001 +#define METHOD_DISABLE 0x00000002 +#define METHOD_MUTE 0x00000003 +#define METHOD_VOLUME_SET 0x00000004 +#define METHOD_SWITCH 0x00000005 + +// Audio Hardware devices, relative with APU and codec paths +// Output Device must be defined by order, 1 bit 1 step +#define HWDEV_INVALID 0xFFFFFFFF +#define HWDEV_OUT_BASE 0x00000001 +#define HWDEV_EARPIECE (HWDEV_OUT_BASE << 0) +#define HWDEV_SPEAKER (HWDEV_OUT_BASE << 1) +#define HWDEV_STEREOSPEAKER (HWDEV_OUT_BASE << 2) +#define HWDEV_HEADPHONE (HWDEV_OUT_BASE << 3) +#define HWDEV_BLUETOOTH_NB (HWDEV_OUT_BASE << 4) +#define HWDEV_BT_NREC_OFF_NB (HWDEV_OUT_BASE << 5) +#define HWDEV_BLUETOOTH_WB (HWDEV_OUT_BASE << 6) +#define HWDEV_BT_NREC_OFF_WB (HWDEV_OUT_BASE << 7) +#define HWDEV_OUT_TTY (HWDEV_OUT_BASE << 8) +#define HWDEV_OUT_TTY_HCO_SPEAKER (HWDEV_OUT_BASE << 9) +#define HWDEV_OUT_TTY_HCO_STEREOSPEAKER (HWDEV_OUT_BASE << 10) + +// Input Device must be defined by order, 1 bit 1 step +#define HWDEV_BIT_IN 0x80000000 +#define HWDEV_IN_BASE 0x00000001 +#define HWDEV_AMIC1 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 0)) +#define HWDEV_AMIC2 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 1)) +#define HWDEV_DMIC1 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 2)) +#define HWDEV_DMIC2 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 3)) +#define HWDEV_AMIC1_SPK_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 4)) +#define HWDEV_AMIC1_HP_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 5)) +#define HWDEV_AMIC2_SPK_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 6)) +#define HWDEV_AMIC2_HP_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 7)) +#define HWDEV_DUAL_DMIC1 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 8)) +#define HWDEV_HSMIC (HWDEV_BIT_IN | (HWDEV_IN_BASE << 9)) +#define HWDEV_BTMIC_NB (HWDEV_BIT_IN | (HWDEV_IN_BASE << 10)) +#define HWDEV_BTMIC_NREC_OFF_NB (HWDEV_BIT_IN | (HWDEV_IN_BASE << 11)) +#define HWDEV_BTMIC_WB (HWDEV_BIT_IN | (HWDEV_IN_BASE << 12)) +#define HWDEV_BTMIC_NREC_OFF_WB (HWDEV_BIT_IN | (HWDEV_IN_BASE << 13)) +#define HWDEV_DUAL_AMIC (HWDEV_BIT_IN | (HWDEV_IN_BASE << 14)) +#define HWDEV_DUAL_AMIC_SPK_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 15)) +#define HWDEV_DUAL_AMIC_HP_MODE (HWDEV_BIT_IN | (HWDEV_IN_BASE << 16)) +#define HWDEV_IN_TTY (HWDEV_BIT_IN | (HWDEV_IN_BASE << 17)) +#define HWDEV_IN_TTY_VCO (HWDEV_BIT_IN | (HWDEV_IN_BASE << 18)) +#define HWDEV_IN_TTY_VCO_AMIC1 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 19)) +#define HWDEV_IN_TTY_VCO_AMIC2 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 20)) +#define HWDEV_IN_TTY_VCO_DUAL_AMIC (HWDEV_BIT_IN | (HWDEV_IN_BASE << 21)) +#define HWDEV_IN_TTY_VCO_DUAL_AMIC_SPK_MODE \ + (HWDEV_BIT_IN | (HWDEV_IN_BASE << 22)) +#define HWDEV_IN_TTY_VCO_DUAL_DMIC1 (HWDEV_BIT_IN | (HWDEV_IN_BASE << 23)) + +// Mic Name, used to parse platform config xml +static char *input_devname[] = { + "AMIC1", "AMIC2", "DMIC1", "DMIC2", "AMIC1_SPK_MODE", "AMIC1_HP_MODE", + "AMIC2_SPK_MODE", "AMIC2_HP_MODE", "DUAL_DMIC1", "HSMIC", "BTMIC_NB", + "BTMIC_NREC_OFF_NB", "BTMIC_WB", "BTMIC_NREC_OFF_WB", "DUAL_AMIC", + "DUAL_AMIC_SPK_MODE", "DUAL_AMIC_HP_MODE"}; + +/* mic mode in marvell settings */ +enum mic_modes { + MIC_MODE_NONE = 0, + MIC_MODE_MIC1, + MIC_MODE_MIC2, + MIC_MODE_DUALMIC +}; + +/* mic mode to dev name conversion table */ +static const char *mic_mode_to_dev_name[] = { + "\0", /*MIC_MODE_NONE=0*/ + "AMIC1", /*MIC_MODE_MIC1=1*/ + "AMIC2", /*MIC_MODE_MIC2=2*/ + "DUAL_AMIC" /*MIC_MODE_DUALMIC=3*/ +}; + +#define RECOGNITION 0x00010000 + +#define MSA_GAIN_DEFAULT 0 + +// path type definition +typedef enum ID_AUDIO_PATH_TYPE { + ID_PTYPE_INVALID, + ID_PTYPE_INTERFACE = 1, // Playback input path and Record output path + ID_PTYPE_DEVICE, // Playback output path and Record input path + ID_PTYPE_VIRTUAL, // Virtual path + ID_PTYPE_MAX +} path_type_t; + +// define both playback input path and record output path +typedef enum ID_INTERFACE_PATH { + ID_IPATH_INVALID = -1, + ID_IPATH_RX_MIN, + ID_IPATH_RX_HIFI_LL = ID_IPATH_RX_MIN, // low latency + ID_IPATH_RX_HIFI_DB, // deep buffer + ID_IPATH_RX_VC, // voice call + ID_IPATH_RX_VC_ST, // sidetone + ID_IPATH_RX_HFP, // HFP + ID_IPATH_RX_FM, // FM + ID_IPATH_RX_MAX = ID_IPATH_RX_FM, + ID_IPATH_TX_MIN, + ID_IPATH_TX_HIFI = ID_IPATH_TX_MIN, // hifi recording + ID_IPATH_TX_VC, // voice call + ID_IPATH_TX_HFP, // HFP + ID_IPATH_TX_FM, // FM recording + ID_IPATH_TX_MAX = ID_IPATH_TX_FM, +} path_interface_t; + +// virtual mode definition, only used for virtual path +typedef enum ID_VIRTUAL_MODE { + V_MODE_INVALID = 0, + V_MODE_HIFI_LL, // both input and output + V_MODE_HIFI_DB, // only have one direction, output + V_MODE_VC, // phone mode in AUDIO_MODE_IN_CALL + V_MODE_VOIP, // AUDIO_MODE_IN_COMMUNICATION + V_MODE_VT, // AUDIO_MODE_IN_VT_CALL + V_MODE_HFP, // HFP mode in AUDIO_MODE_IN_CALL + V_MODE_FM, // when FM_STATUS=on + V_MODE_CP_LOOPBACK, // test cp loopback + V_MODE_HW_LOOPBACK, // test hw loopback + V_MODE_APP_LOOPBACK, // test app loopback + V_MODE_DEF // add for default config to choose HW device +} virtual_mode_t; + +// the priorities of modes configuration +struct vtrl_mode_priority_cfg { + path_interface_t path_interface; + audio_mode_t audio_mode; + virtual_mode_t v_mode; +}; + +// used to parse config xml, must align with ID_VIRTUAL_MODE +// add a new type Default, will be used when nothing matched +static char *vtrl_mode_name[] = {"INVALID", "HIFI_LL", "HIFI_DB", + "VoiceCall", "VoIP", "VT", + "HFP", "FM", "CP_LOOPBACK", + "HW_LOOPBACK", "APP_LOOPBACK", "Default"}; + +/////////////////////////////////////////////////////////// +// definition of Audio Platform config interface +/////////////////////////////////////////////////////////// +unsigned int get_mic_hw_flag(unsigned int hw_dev); +unsigned int get_mic_dev(virtual_mode_t v_mode, unsigned int android_dev); +int init_platform_config(); +void deinit_platform_config(); +uint32_t get_mic_mode(void); + +/////////////////////////////////////////////////////////// +// definition of Audio path interface +/////////////////////////////////////////////////////////// +char *get_vrtl_path(virtual_mode_t mode, unsigned int device, + unsigned int value); + +void route_vrtl_path(virtual_mode_t v_mode, int hw_dev, int enable, + unsigned int flag, unsigned int value); +void route_hw_device(unsigned int hw_dev, int enable, unsigned int value); +void route_interface(path_interface_t path_itf, int enable); +void set_hw_volume(virtual_mode_t v_mode, int hw_dev, unsigned int value); + +void get_msa_gain(unsigned int hw_dev, unsigned char volume, + signed char *msa_gain, signed char *msa_gain_wb, + bool use_extra_vol); + +#endif /* __AUDIO_PATH_MRVL_H__ */ diff --git a/peripheral/audio/driver/audio_profile.h b/peripheral/audio/driver/audio_profile.h new file mode 100644 index 0000000..288291a --- /dev/null +++ b/peripheral/audio/driver/audio_profile.h @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __AUDIO_PROFILE_H__ +#define __AUDIO_PROFILE_H__ + +#include <system/audio.h> +#include "acm_api.h" + +// profile flags +#define PROFILE_BT_WB 0x00000001 +#define PROFILE_BT_NREC_OFF 0x00000002 +#define PROFILE_BT_STEREO 0x00000004 +#define PROFILE_EXTRA_VOL 0x00000008 +#define PROFILE_LOOP 0x00000010 +#define PROFILE_LOOP_ENH_OFF 0x00000020 +#define PROFILE_TTY 0x00000040 +#define PROFILE_HCO 0x00000080 +#define PROFILE_VCO 0x00000100 +#define PROFILE_DUAL_MIC 0x00000200 +#define PROFILE_LTE 0x00000400 + +// Audio profile operation methods +#define PROFILE_ENABLE 0x00000001 +#define PROFILE_DISABLE 0x00000002 + +// definition of Audio Profile +typedef enum { + VC_HANDSET = 0, + VC_HANDSFREE, + VC_HEADSET, + VC_HEADPHONE, + VC_SPEAKERPHONE, + VC_BT_NB, + VC_BT_NREC_OFF_NB, + VC_STEREO_BT_NB, + VC_BT_WB, + VC_BT_NREC_OFF_WB, + VC_STEREO_BT_WB, + VC_HANDSET_EXTRAVOL_ON, + VC_HANDSFREE_EXTRAVOL_ON, + VC_HANDSET_DUALMIC, + VC_HEADSET_DUALMIC, + VC_HANDSFREE_DUALMIC, + + // LOOP means LOOP with enhancement + VC_HANDSET_LOOP, + VC_HANDSFREE_LOOP, + VC_HEADSET_LOOP, + VC_BT_NB_LOOP, + VC_BT_WB_LOOP, + + // LOOP_ENH_OFF means LOOP without enhancement + VC_HANDSET_LOOP_ENH_OFF, + VC_HANDSFREE_LOOP_ENH_OFF, + VC_HEADSET_LOOP_ENH_OFF, + VC_BT_NB_LOOP_ENH_OFF, + VC_BT_WB_LOOP_ENH_OFF, + + VC_TTY, + VC_TTY_HCO_EARPIECE, + VC_TTY_HCO_SPEAKER, + VC_TTY_VCO_EARPIECE, + VC_TTY_VCO_SPEAKER, + VC_TTY_VCO_EARPIECE_DUALMIC, + VC_TTY_VCO_SPEAKER_DUALMIC, + + VOLTE_HANDSET, + VOLTE_HANDSFREE, + VOLTE_HEADSET, + VOLTE_HEADPHONE, + VOLTE_SPEAKERPHONE, + VOLTE_BT_NB, + VOLTE_BT_NREC_OFF_NB, + VOLTE_STEREO_BT_NB, + VOLTE_BT_WB, + VOLTE_BT_NREC_OFF_WB, + VOLTE_STEREO_BT_WB, + VOLTE_HANDSET_EXTRAVOL_ON, + VOLTE_HANDSFREE_EXTRAVOL_ON, + VOLTE_HANDSET_DUALMIC, + VOLTE_HEADSET_DUALMIC, + VOLTE_HANDSFREE_DUALMIC, + + VOIP_HANDSET = 0x50, + VOIP_HANDSFREE, + VOIP_HEADSET, + VOIP_HEADPHONE, + VOIP_SPEAKERPHONE, + VOIP_BT_NB, + VOIP_BT_NREC_OFF_NB, + VOIP_STEREO_BT_NB, + VOIP_BT_WB, + VOIP_BT_NREC_OFF_WB, + VOIP_STEREO_BT_WB, + VOIP_HANDSET_EXTRAVOL_ON, + VOIP_HANDSFREE_EXTRAVOL_ON, + VOIP_HANDSET_DUALMIC, + VOIP_HEADSET_DUALMIC, + VOIP_HANDSFREE_DUALMIC, + VOIP_HANDSET_LOOP, + VOIP_HANDSFREE_LOOP, + VOIP_HEADSET_LOOP, + VOIP_BT_NB_LOOP, + VOIP_BT_WB_LOOP, + + HIFI_SPEAKER = 0x80, + HIFI_HEADSET, + HIFI_MIC, + HIFI_DUAL_MIC, + + VT_HANDSET = 0xA0, + VT_HANDSFREE, + VT_HEADSET, + VT_HEADPHONE, + VT_SPEAKERPHONE, + VT_BT_NB, + VT_BT_NREC_OFF_NB, + VT_STEREO_BT_NB, + VT_BT_WB, + VT_BT_NREC_OFF_WB, + VT_STEREO_BT_WB, + VT_HANDSET_EXTRAVOL_ON, + VT_HANDSFREE_EXTRAVOL_ON, + VT_HANDSET_DUALMIC, + VT_HEADSET_DUALMIC, + VT_HANDSFREE_DUALMIC, + VT_HANDSET_LOOP, + VT_HANDSFREE_LOOP, + VT_HEADSET_LOOP, + VT_BT_NB_LOOP, + VT_BT_WB_LOOP, + + AUDIO_PROFILE_ID_ENUM_32_BIT = 0x7FFFFFFF // 32bit enum compiling enforcement +} AUDIO_PROFILE_ID; + +// define profile table +#define PROFILE_NAME(e) #e +#define MAX_NUM_PROFILE (sizeof(gProfile) / sizeof(gProfile[0])) +static const struct { + audio_mode_t v_mode; + uint32_t out_device; + uint32_t in_device; + uint32_t flag; + char *profile; +} gProfile[] = { + // Voice Call Profile + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, 0, + PROFILE_NAME(VC_HANDSET)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, 0, + PROFILE_NAME(VC_HANDSFREE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADSET, 0, 0, + PROFILE_NAME(VC_HEADSET)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, 0, + PROFILE_NAME(VC_HEADPHONE)}, + {AUDIO_MODE_IN_CALL, + AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, 0, + PROFILE_NAME(VC_SPEAKERPHONE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, 0, + PROFILE_NAME(VC_BT_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_NREC_OFF, PROFILE_NAME(VC_BT_NREC_OFF_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_STEREO, PROFILE_NAME(VC_STEREO_BT_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_WB, PROFILE_NAME(VC_BT_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_NREC_OFF | PROFILE_BT_WB, PROFILE_NAME(VC_BT_NREC_OFF_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_STEREO | PROFILE_BT_WB, PROFILE_NAME(VC_STEREO_BT_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_EXTRA_VOL, + PROFILE_NAME(VC_HANDSET_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_EXTRA_VOL, + PROFILE_NAME(VC_HANDSFREE_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_DUAL_MIC, + PROFILE_NAME(VC_HANDSET_DUALMIC)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_DUAL_MIC, + PROFILE_NAME(VC_HEADSET_DUALMIC)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_DUAL_MIC, + PROFILE_NAME(VC_HANDSFREE_DUALMIC)}, + + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_LOOP, + PROFILE_NAME(VC_HANDSET_LOOP)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_LOOP, + PROFILE_NAME(VC_HANDSFREE_LOOP)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADSET, 0, PROFILE_LOOP, + PROFILE_NAME(VC_HEADSET_LOOP)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP, PROFILE_NAME(VC_BT_NB_LOOP)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP, PROFILE_NAME(VC_BT_WB_LOOP)}, + + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_LOOP_ENH_OFF, + PROFILE_NAME(VC_HANDSET_LOOP_ENH_OFF)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_LOOP_ENH_OFF, + PROFILE_NAME(VC_HANDSFREE_LOOP_ENH_OFF)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADSET, 0, + PROFILE_LOOP_ENH_OFF, PROFILE_NAME(VC_HEADSET_LOOP_ENH_OFF)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP_ENH_OFF, PROFILE_NAME(VC_BT_NB_LOOP_ENH_OFF)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP_ENH_OFF, PROFILE_NAME(VC_BT_WB_LOOP_ENH_OFF)}, + + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_TTY, + PROFILE_NAME(VC_TTY)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_TTY | PROFILE_HCO, PROFILE_NAME(VC_TTY_HCO_EARPIECE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_TTY | PROFILE_HCO, + PROFILE_NAME(VC_TTY_HCO_SPEAKER)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_TTY | PROFILE_VCO, PROFILE_NAME(VC_TTY_VCO_EARPIECE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_TTY | PROFILE_VCO, + PROFILE_NAME(VC_TTY_VCO_SPEAKER)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_TTY | PROFILE_VCO | PROFILE_DUAL_MIC, + PROFILE_NAME(VC_TTY_VCO_EARPIECE_DUALMIC)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, + PROFILE_TTY | PROFILE_VCO | PROFILE_DUAL_MIC, + PROFILE_NAME(VC_TTY_VCO_SPEAKER_DUALMIC)}, + + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_LTE, + PROFILE_NAME(VOLTE_HANDSET)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_LTE, + PROFILE_NAME(VOLTE_HANDSFREE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADSET, 0, PROFILE_LTE, + PROFILE_NAME(VOLTE_HEADSET)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, PROFILE_LTE, + PROFILE_NAME(VOLTE_HEADPHONE)}, + {AUDIO_MODE_IN_CALL, + AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, + PROFILE_LTE, PROFILE_NAME(VOLTE_SPEAKERPHONE)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, PROFILE_LTE, + PROFILE_NAME(VOLTE_BT_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LTE | PROFILE_BT_NREC_OFF, PROFILE_NAME(VOLTE_BT_NREC_OFF_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LTE | PROFILE_BT_STEREO, PROFILE_NAME(VOLTE_STEREO_BT_NB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LTE | PROFILE_BT_WB, PROFILE_NAME(VOLTE_BT_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LTE | PROFILE_BT_NREC_OFF | PROFILE_BT_WB, + PROFILE_NAME(VOLTE_BT_NREC_OFF_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LTE | PROFILE_BT_STEREO | PROFILE_BT_WB, + PROFILE_NAME(VOLTE_STEREO_BT_WB)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_LTE | PROFILE_EXTRA_VOL, PROFILE_NAME(VOLTE_HANDSET_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, + PROFILE_LTE | PROFILE_EXTRA_VOL, + PROFILE_NAME(VOLTE_HANDSFREE_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_LTE | PROFILE_DUAL_MIC, PROFILE_NAME(VOLTE_HANDSET_DUALMIC)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_LTE | PROFILE_DUAL_MIC, PROFILE_NAME(VOLTE_HEADSET_DUALMIC)}, + {AUDIO_MODE_IN_CALL, AUDIO_DEVICE_OUT_SPEAKER, 0, + PROFILE_LTE | PROFILE_DUAL_MIC, PROFILE_NAME(VOLTE_HANDSFREE_DUALMIC)}, + + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, 0, + PROFILE_NAME(VOIP_HANDSET)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_SPEAKER, 0, 0, + PROFILE_NAME(VOIP_HANDSFREE)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_WIRED_HEADSET, 0, 0, + PROFILE_NAME(VOIP_HEADSET)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, 0, + PROFILE_NAME(VOIP_HEADPHONE)}, + {AUDIO_MODE_IN_COMMUNICATION, + AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 0, 0, + PROFILE_NAME(VOIP_SPEAKERPHONE)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, 0, + PROFILE_NAME(VOIP_BT_NB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_NREC_OFF, PROFILE_NAME(VOIP_BT_NREC_OFF_NB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_STEREO, PROFILE_NAME(VOIP_STEREO_BT_NB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_WB, PROFILE_NAME(VOIP_BT_WB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_NREC_OFF | PROFILE_BT_WB, PROFILE_NAME(VOIP_BT_NREC_OFF_WB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_BT_STEREO | PROFILE_BT_WB, PROFILE_NAME(VOIP_STEREO_BT_WB)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_EXTRA_VOL, PROFILE_NAME(VOIP_HANDSET_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_SPEAKER, 0, + PROFILE_EXTRA_VOL, PROFILE_NAME(VOIP_HANDSFREE_EXTRAVOL_ON)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_DUAL_MIC, PROFILE_NAME(VOIP_HANDSET_DUALMIC)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, + PROFILE_DUAL_MIC, PROFILE_NAME(VOIP_HEADSET_DUALMIC)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_DUAL_MIC, + PROFILE_NAME(VOIP_HANDSFREE_DUALMIC)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_LOOP, + PROFILE_NAME(VOIP_HANDSET_LOOP)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_SPEAKER, 0, PROFILE_LOOP, + PROFILE_NAME(VOIP_HANDSFREE_LOOP)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_EARPIECE, 0, PROFILE_LOOP, + PROFILE_NAME(VOIP_HEADSET_LOOP)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP, PROFILE_NAME(VOIP_BT_NB_LOOP)}, + {AUDIO_MODE_IN_COMMUNICATION, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 0, + PROFILE_LOOP, PROFILE_NAME(VOIP_BT_WB_LOOP)}, + + {AUDIO_MODE_NORMAL, AUDIO_DEVICE_OUT_SPEAKER, 0, 0, + PROFILE_NAME(HIFI_SPEAKER)}, + {AUDIO_MODE_NORMAL, AUDIO_DEVICE_OUT_WIRED_HEADSET, + AUDIO_DEVICE_IN_WIRED_HEADSET, 0, PROFILE_NAME(HIFI_HEADSET)}, + {AUDIO_MODE_NORMAL, AUDIO_DEVICE_OUT_SPEAKER, AUDIO_DEVICE_IN_BUILTIN_MIC, + 0, PROFILE_NAME(HIFI_MIC)}, + {AUDIO_MODE_NORMAL, AUDIO_DEVICE_OUT_EARPIECE, AUDIO_DEVICE_IN_BUILTIN_MIC, + PROFILE_DUAL_MIC, PROFILE_NAME(HIFI_DUAL_MIC)}, +}; + +//-------------------------------------------------------------- +//-------- Helper Utilities +//-------------------------------------------------------------- +#define ENUM_STR_MAP_BEGIN_PROFILE(enum_type) \ + static const char *_getStr_profile(int enum_value) { \ + switch (enum_value) { +#define ENUM_STR_MAP_ITEM_PROFILE(e) \ + case e: \ + return #e; +#define ENUM_STR_MAP_END_PROFILE \ + default: \ + return NULL; \ + } \ + } + +#define STR_ENUM_MAP_BEGIN_PROFILE(enum_type) \ + static enum_type _getEnum_profile(char *str) { +#define STR_ENUM_MAP_ITEM_PROFILE(e) \ + if (str != NULL) \ + if (strcmp((char *)str, #e) == 0) return e; +#define STR_ENUM_MAP_END_PROFILE \ + return AUDIO_PROFILE_ID_ENUM_32_BIT; \ + } + +#define ENUM2STR_PROFILE(enum_value) _getStr_profile(enum_value) +#define STR2ENUM_PROFILE(str) _getEnum_profile(str) + +//-------------------------------------------------------------- +//-------- Enum / String Map +//-------------------------------------------------------------- +ENUM_STR_MAP_BEGIN_PROFILE(AUDIO_PROFILE_ID) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSET) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSFREE) +ENUM_STR_MAP_ITEM_PROFILE(VC_HEADSET) +ENUM_STR_MAP_ITEM_PROFILE(VC_HEADPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VC_SPEAKERPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_NREC_OFF_NB) +ENUM_STR_MAP_ITEM_PROFILE(VC_STEREO_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_NREC_OFF_WB) +ENUM_STR_MAP_ITEM_PROFILE(VC_STEREO_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSET_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSFREE_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VC_HEADSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSFREE_DUALMIC) + +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSFREE_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VC_HEADSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_NB_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_WB_LOOP) + +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSET_LOOP_ENH_OFF) +ENUM_STR_MAP_ITEM_PROFILE(VC_HANDSFREE_LOOP_ENH_OFF) +ENUM_STR_MAP_ITEM_PROFILE(VC_HEADSET_LOOP_ENH_OFF) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_NB_LOOP_ENH_OFF) +ENUM_STR_MAP_ITEM_PROFILE(VC_BT_WB_LOOP_ENH_OFF) + +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_HCO_EARPIECE) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_HCO_SPEAKER) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_VCO_EARPIECE) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_VCO_SPEAKER) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_VCO_EARPIECE_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VC_TTY_VCO_SPEAKER_DUALMIC) + +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSET) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSFREE) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HEADSET) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HEADPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_SPEAKERPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_BT_NREC_OFF_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_STEREO_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_BT_NREC_OFF_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_STEREO_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSET_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSFREE_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HEADSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VOLTE_HANDSFREE_DUALMIC) + +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSET) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSFREE) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HEADSET) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HEADPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_SPEAKERPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_NREC_OFF_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_STEREO_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_NREC_OFF_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_STEREO_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSET_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSFREE_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HEADSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSFREE_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HANDSFREE_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_HEADSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_NB_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VOIP_BT_WB_LOOP) + +ENUM_STR_MAP_ITEM_PROFILE(HIFI_SPEAKER) +ENUM_STR_MAP_ITEM_PROFILE(HIFI_HEADSET) +ENUM_STR_MAP_ITEM_PROFILE(HIFI_MIC) +ENUM_STR_MAP_ITEM_PROFILE(HIFI_DUAL_MIC) + +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSET) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSFREE) +ENUM_STR_MAP_ITEM_PROFILE(VT_HEADSET) +ENUM_STR_MAP_ITEM_PROFILE(VT_HEADPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VT_SPEAKERPHONE) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_NREC_OFF_NB) +ENUM_STR_MAP_ITEM_PROFILE(VT_STEREO_BT_NB) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_NREC_OFF_WB) +ENUM_STR_MAP_ITEM_PROFILE(VT_STEREO_BT_WB) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSET_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSFREE_EXTRAVOL_ON) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VT_HEADSET_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSFREE_DUALMIC) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VT_HANDSFREE_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VT_HEADSET_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_NB_LOOP) +ENUM_STR_MAP_ITEM_PROFILE(VT_BT_WB_LOOP) +ENUM_STR_MAP_END_PROFILE + +STR_ENUM_MAP_BEGIN_PROFILE(AUDIO_PROFILE_ID) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSET) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSFREE) +STR_ENUM_MAP_ITEM_PROFILE(VC_HEADSET) +STR_ENUM_MAP_ITEM_PROFILE(VC_HEADPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VC_SPEAKERPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_NREC_OFF_NB) +STR_ENUM_MAP_ITEM_PROFILE(VC_STEREO_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_NREC_OFF_WB) +STR_ENUM_MAP_ITEM_PROFILE(VC_STEREO_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSET_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSFREE_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VC_HEADSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSFREE_DUALMIC) + +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSFREE_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VC_HEADSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_NB_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_WB_LOOP) + +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSET_LOOP_ENH_OFF) +STR_ENUM_MAP_ITEM_PROFILE(VC_HANDSFREE_LOOP_ENH_OFF) +STR_ENUM_MAP_ITEM_PROFILE(VC_HEADSET_LOOP_ENH_OFF) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_NB_LOOP_ENH_OFF) +STR_ENUM_MAP_ITEM_PROFILE(VC_BT_WB_LOOP_ENH_OFF) + +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_HCO_EARPIECE) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_HCO_SPEAKER) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_VCO_EARPIECE) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_VCO_SPEAKER) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_VCO_EARPIECE_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VC_TTY_VCO_SPEAKER_DUALMIC) + +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSET) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSFREE) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HEADSET) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HEADPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_SPEAKERPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_BT_NREC_OFF_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_STEREO_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_BT_NREC_OFF_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_STEREO_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSET_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSFREE_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HEADSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VOLTE_HANDSFREE_DUALMIC) + +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSET) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSFREE) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HEADSET) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HEADPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_SPEAKERPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_NREC_OFF_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_STEREO_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_NREC_OFF_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_STEREO_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSET_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSFREE_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HEADSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSFREE_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HANDSFREE_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_HEADSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_NB_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VOIP_BT_WB_LOOP) + +STR_ENUM_MAP_ITEM_PROFILE(HIFI_SPEAKER) +STR_ENUM_MAP_ITEM_PROFILE(HIFI_HEADSET) +STR_ENUM_MAP_ITEM_PROFILE(HIFI_MIC) +STR_ENUM_MAP_ITEM_PROFILE(HIFI_DUAL_MIC) + +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSET) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSFREE) +STR_ENUM_MAP_ITEM_PROFILE(VT_HEADSET) +STR_ENUM_MAP_ITEM_PROFILE(VT_HEADPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VT_SPEAKERPHONE) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_NREC_OFF_NB) +STR_ENUM_MAP_ITEM_PROFILE(VT_STEREO_BT_NB) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_NREC_OFF_WB) +STR_ENUM_MAP_ITEM_PROFILE(VT_STEREO_BT_WB) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSET_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSFREE_EXTRAVOL_ON) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VT_HEADSET_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSFREE_DUALMIC) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VT_HANDSFREE_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VT_HEADSET_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_NB_LOOP) +STR_ENUM_MAP_ITEM_PROFILE(VT_BT_WB_LOOP) +STR_ENUM_MAP_END_PROFILE + +#endif /* __AUDIO_PROFILE_H__ */ diff --git a/peripheral/audio/driver/include/acm/acm_api.h b/peripheral/audio/driver/include/acm/acm_api.h new file mode 100644 index 0000000..5e7dd90 --- /dev/null +++ b/peripheral/audio/driver/include/acm/acm_api.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __MARVELL_AUDIO_ACM_H__ +#define __MARVELL_AUDIO_ACM_H__ + +#define COUPLING_DC 0x00200000 +#define COUPLING_AC 0x00100000 +#define COUPLING_UNKNOWN 0x00000000 + +#define CONNECT_SINGLE_ENDED 0x00080000 +#define CONNECT_QUASI_DIFF 0x00040000 +#define CONNECT_DIFF 0x00020000 +#define CONNECT_UNKNOWN 0x00000000 + +#define CONNECT_MONO_HEADSET_L 0x00400000 +#define CONNECT_MONO_HEADSET_R 0x00800000 +#define CONNECT_STEREO_HEADSET 0x01000000 +#define CONNECT_HEADSET_MASK \ + (CONNECT_MONO_HEADSET_L | CONNECT_MONO_HEADSET_R | CONNECT_STEREO_HEADSET) + +#define VOLUME_MASK 0x0000ffff +#define PARAM_MASK 0xffff0000 +// gain register in low priority path share value with which is in the highest +// priority path +#define SHARED_GAIN 0x80000000 + +// connectivity and coupling type +typedef enum { + ACM_MIC_TYPE_AC_DIFF = COUPLING_AC | CONNECT_DIFF, + ACM_MIC_TYPE_AC_QUASI_DIFF = COUPLING_AC | CONNECT_QUASI_DIFF, + ACM_MIC_TYPE_AC_SINGLE_ENDED = COUPLING_AC | CONNECT_SINGLE_ENDED, + ACM_MIC_TYPE_DC_DIFF = COUPLING_DC | CONNECT_DIFF, + ACM_MIC_TYPE_DC_QUASI_DIFF = COUPLING_DC | CONNECT_QUASI_DIFF, + ACM_MIC_TYPE_DC_SINGLE_ENDED = COUPLING_DC | CONNECT_SINGLE_ENDED, + ACM_MIC_TYPE_MASK = COUPLING_AC | COUPLING_DC | CONNECT_DIFF | + CONNECT_QUASI_DIFF | CONNECT_SINGLE_ENDED +} ACM_MicType; + +typedef enum { + ACM_RC_OK, + ACM_RC_INVALID_PATH, + ACM_RC_PATH_CONFIG_NOT_EXIST, + ACM_RC_PATH_ALREADY_ENABLED, + ACM_RC_PATH_ALREADY_DISABLED, + ACM_RC_PATH_NOT_ENABLED, + ACM_RC_NO_MUTE_CHANGE_NEEDED, + ACM_RC_INVALID_VOLUME_CHANGE, + ACM_RC_INVALID_PARAMETER, + ACM_RC_INVALID_MIC_TYPE, + ACM_RC_INVALID_HEADSET_TYPE, + ACM_RC_FORCE_DELAY +} ACM_ReturnCode; + +void ACMInit(void); +void ACMDeInit(void); +ACM_ReturnCode ACMAudioPathEnable(const char *path, unsigned int value); +// deprecate ACMAudioPathDisable API, suggest to use ACMAudioPathHotDisable +// instead +ACM_ReturnCode ACMAudioPathDisable(const char *path); +// if value = 1, will disable the path but not turn off standby components +// if value = 0, will disable the path and turn off standby components, as +// former ACMAudioPathDisable do +ACM_ReturnCode ACMAudioPathHotDisable(const char *path, unsigned int value); +ACM_ReturnCode ACMAudioPathSwitch(const char *path_old, const char *path_new, + unsigned int value); +ACM_ReturnCode ACMAudioPathMute(const char *path, unsigned int value); +ACM_ReturnCode ACMAudioPathVolumeSet(const char *path, unsigned int value); +ACM_ReturnCode ACMGetParameter(unsigned int param_id, void *param, + unsigned int *size); +ACM_ReturnCode ACMSetParameter(unsigned int param_id, void *param, + unsigned int size); + +#endif diff --git a/peripheral/audio/driver/include/acm/acm_param.h b/peripheral/audio/driver/include/acm/acm_param.h new file mode 100644 index 0000000..a7d28b0 --- /dev/null +++ b/peripheral/audio/driver/include/acm/acm_param.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005. Marvell International Ltd + * + * 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 __MARVELL_AUDIO_ACM_PARAM_H__ +#define __MARVELL_AUDIO_ACM_PARAM_H__ + +#define PATH_LEN 30 + +typedef enum { ACM_MSA_GAIN, ACM_MSA_GAIN_MODE } ACM_ParameterId; + +typedef enum { + MSA_GAIN_NORMAL_MODE = 0, + MSA_GAIN_EXTRA_MODE = 1 +} ACM_MsaGainMode; + +typedef struct tag_Msa_Gain { + const unsigned char *path_name; + unsigned char volume; + signed char gain, wbGain; + unsigned char path_direction; +} ACM_MsaGain; + +#endif diff --git a/soc/iap140/modules/audio_hal_module.mk b/soc/iap140/modules/audio_hal_module.mk index d88c954..a9c40e3 100644 --- a/soc/iap140/modules/audio_hal_module.mk +++ b/soc/iap140/modules/audio_hal_module.mk @@ -14,17 +14,25 @@ # limitations under the License. # -# audio modules -PRODUCT_COPY_FILES += \ - $(TOP)/vendor/bsp/marvell/device/abox_edge/hal/audio/libacm.so:system/lib/libacm.so \ - $(TOP)/vendor/bsp/marvell/device/abox_edge/hal/audio/libxml2.so:system/lib/libxml2.so \ - $(TOP)/vendor/bsp/marvell/device/abox_edge/hal/audio/audio.primary.mrvl.so:system/lib/hw/audio.primary.mrvl.so +# Audio Component Config +BOARD_AUDIO_COMPONENT_APU := MAP-LITE +BOARD_ENABLE_ADVANCED_AUDIO := false + +# Audio Config +BOARD_WITH_STEREO_SPKR := false +# Route audio speaker to headset output +BOARD_WITH_HEADSET_OUTPUT_ONLY := true + +# Audio modules PRODUCT_COPY_FILES += \ $(TOP)/hardware/bsp/marvell/peripheral/audio/audio_policy.conf:/system/etc/audio_policy.conf \ $(TOP)/hardware/bsp/marvell/peripheral/audio/platform_audio_config.xml:/system/etc/platform_audio_config.xml PRODUCT_PACKAGES += \ - libaudioutils + libaudioutils + +DEVICE_PACKAGES += \ + audio.primary.mrvl -include hardware/bsp/marvell/peripheral/audio/audio_xml/iap140.mk |