hal: add usb audio tunnel support
This patch imports the USB audio tunnel feature from QCOM software release
AU_LINUX_ANDROID_LA.UM.5.7.R1.07.00.00.253.
Bug: 33030406
Test: playback, record and path switching
Change-Id: I2479f984c0d72b2f4e9b6a7db22eb4616855b7e7
Signed-off-by: David Lin <[email protected]>
diff --git a/hal/Android.mk b/hal/Android.mk
index c2801db..062e3d3 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -74,6 +74,11 @@
LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/hw_info.c
endif
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_TUNNEL)),true)
+ LOCAL_CFLAGS += -DUSB_TUNNEL_ENABLED
+ LOCAL_SRC_FILES += audio_extn/usb.c
+endif
+
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index f53c9f2..b734cb6 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -70,6 +70,31 @@
#endif
+#ifndef USB_TUNNEL_ENABLED
+#define audio_extn_usb_init(adev) (0)
+#define audio_extn_usb_deinit() (0)
+#define audio_extn_usb_add_device(device, card) (0)
+#define audio_extn_usb_remove_device(device, card) (0)
+#define audio_extn_usb_is_config_supported(bit_width, sample_rate, ch, pb) (false)
+#define audio_extn_usb_enable_sidetone(device, enable) (0)
+#define audio_extn_usb_set_sidetone_gain(parms, value, len) (0)
+#define audio_extn_usb_is_capture_supported() (false)
+#else
+void audio_extn_usb_init(void *adev);
+void audio_extn_usb_deinit();
+void audio_extn_usb_add_device(audio_devices_t device, int card);
+void audio_extn_usb_remove_device(audio_devices_t device, int card);
+bool audio_extn_usb_is_config_supported(unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *ch,
+ bool is_playback);
+int audio_extn_usb_enable_sidetone(int device, bool enable);
+int audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
+ char *value, int len);
+bool audio_extn_usb_is_capture_supported();
+#endif
+
+
#ifndef SOUND_TRIGGER_ENABLED
#define audio_extn_sound_trigger_init(adev) (0)
#define audio_extn_sound_trigger_deinit(adev) (0)
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
new file mode 100644
index 0000000..3747318
--- /dev/null
+++ b/hal/audio_extn/usb.c
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "audio_hw_usb"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <system/audio.h>
+#include <tinyalsa/asoundlib.h>
+#include <audio_hw.h>
+#include <cutils/properties.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef USB_TUNNEL_ENABLED
+#define USB_BUFF_SIZE 2048
+#define CHANNEL_NUMBER_STR "Channels: "
+#define PLAYBACK_PROFILE_STR "Playback:"
+#define CAPTURE_PROFILE_STR "Capture:"
+#define USB_SIDETONE_GAIN_STR "usb_sidetone_gain"
+#define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A)))
+#define SAMPLE_RATE_8000 8000
+#define SAMPLE_RATE_11025 11025
+/* TODO: dynamically populate supported sample rates */
+static uint32_t supported_sample_rates[] =
+ {44100, 48000, 64000, 88200, 96000, 176400, 192000, 384000};
+
+#define MAX_SAMPLE_RATE_SIZE sizeof(supported_sample_rates)/sizeof(supported_sample_rates[0])
+
+enum usb_usecase_type{
+ USB_PLAYBACK = 0,
+ USB_CAPTURE,
+};
+
+enum {
+ USB_SIDETONE_ENABLE_INDEX = 0,
+ USB_SIDETONE_VOLUME_INDEX,
+ USB_SIDETONE_MAX_INDEX,
+};
+
+struct usb_device_config {
+ struct listnode list;
+ unsigned int bit_width;
+ unsigned int channel_count;
+ unsigned int rate_size;
+ unsigned int rates[MAX_SAMPLE_RATE_SIZE];
+};
+
+struct usb_card_config {
+ struct listnode list;
+ audio_devices_t usb_device_type;
+ int usb_card;
+ struct listnode usb_device_conf_list;
+ struct mixer *usb_snd_mixer;
+ int usb_sidetone_index[USB_SIDETONE_MAX_INDEX];
+ int usb_sidetone_vol_min;
+ int usb_sidetone_vol_max;
+};
+
+struct usb_module {
+ struct listnode usb_card_conf_list;
+ struct audio_device *adev;
+ int sidetone_gain;
+ bool is_capture_supported;
+};
+
+static struct usb_module *usbmod = NULL;
+static bool usb_audio_debug_enable = false;
+static int usb_sidetone_gain = 0;
+
+static const char * const usb_sidetone_enable_str[] = {
+ "Sidetone Playback Switch",
+ "Mic Playback Switch",
+};
+
+static const char * const usb_sidetone_volume_str[] = {
+ "Sidetone Playback Volume",
+ "Mic Playback Volume",
+};
+
+static void usb_mixer_print_enum(struct mixer_ctl *ctl)
+{
+ unsigned int num_enums;
+ unsigned int i;
+ const char *string;
+
+ num_enums = mixer_ctl_get_num_enums(ctl);
+
+ for (i = 0; i < num_enums; i++) {
+ string = mixer_ctl_get_enum_string(ctl, i);
+ ALOGI("\t%s%s", mixer_ctl_get_value(ctl, 0) == (int)i ? ">" : "", string);
+ }
+}
+
+static void usb_soundcard_detail_control(struct mixer *mixer, const char *control)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+ int min, max;
+
+ if (isdigit(control[0]))
+ ctl = mixer_get_ctl(mixer, atoi(control));
+ else
+ ctl = mixer_get_ctl_by_name(mixer, control);
+
+ if (!ctl) {
+ fprintf(stderr, "Invalid mixer control\n");
+ return;
+ }
+
+ type = mixer_ctl_get_type(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+
+ ALOGV("%s:", mixer_ctl_get_name(ctl));
+
+ for (i = 0; i < num_values; i++) {
+ switch (type) {
+ case MIXER_CTL_TYPE_INT:
+ ALOGV(" %d", mixer_ctl_get_value(ctl, i));
+ break;
+ case MIXER_CTL_TYPE_BOOL:
+ ALOGV(" %s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ usb_mixer_print_enum(ctl);
+ break;
+ case MIXER_CTL_TYPE_BYTE:
+ ALOGV(" 0x%02x", mixer_ctl_get_value(ctl, i));
+ break;
+ default:
+ ALOGV(" unknown");
+ break;
+ }
+ }
+
+ if (type == MIXER_CTL_TYPE_INT) {
+ min = mixer_ctl_get_range_min(ctl);
+ max = mixer_ctl_get_range_max(ctl);
+ ALOGV(" (range %d->%d)", min, max);
+ }
+}
+
+static void usb_soundcard_list_controls(struct mixer *mixer)
+{
+ struct mixer_ctl *ctl;
+ const char *name, *type;
+ unsigned int num_ctls, num_values;
+ unsigned int i;
+
+ num_ctls = mixer_get_num_ctls(mixer);
+
+ ALOGV("Number of controls: %d\n", num_ctls);
+
+ ALOGV("ctl\ttype\tnum\t%-40s value\n", "name");
+ for (i = 0; i < num_ctls; i++) {
+ ctl = mixer_get_ctl(mixer, i);
+ if (ctl != NULL) {
+ name = mixer_ctl_get_name(ctl);
+ type = mixer_ctl_get_type_string(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+ ALOGV("%d\t%s\t%d\t%-40s", i, type, num_values, name);
+ if (name != NULL)
+ usb_soundcard_detail_control(mixer, name);
+ }
+ }
+}
+
+static int usb_set_dev_id_mixer_ctl(unsigned int usb_usecase_type, int card,
+ char *dev_mixer_ctl_name)
+{
+ struct mixer_ctl *ctl;
+ unsigned int dev_token;
+ const unsigned int pcm_device_number = 0;
+
+ /*
+ * usb_dev_token_id is 32 bit number and is defined as below:
+ * usb_sound_card_idx(31:16) | usb PCM device ID(15:8) | usb_usecase_type(7:0)
+ */
+ dev_token = (card << 16 ) |
+ (pcm_device_number << 8) | (usb_usecase_type & 0xFF);
+
+ ctl = mixer_get_ctl_by_name(usbmod->adev->mixer, dev_mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, dev_mixer_ctl_name);
+ return -EINVAL;
+ }
+ mixer_ctl_set_value(ctl, 0, dev_token);
+
+ return 0;
+}
+
+static int usb_get_sample_rates(char *rates_str,
+ struct usb_device_config *config)
+{
+ uint32_t i;
+ char *next_sr_string, *temp_ptr;
+ uint32_t sr, min_sr, max_sr, sr_size = 0;
+
+ /* Sample rate string can be in any of the folloing two bit_widthes:
+ * Rates: 8000 - 48000 (continuous)
+ * Rates: 8000, 44100, 48000
+ * Support both the bit_widths
+ */
+ ALOGV("%s: rates_str %s", __func__, rates_str);
+ next_sr_string = strtok_r(rates_str, "Rates: ", &temp_ptr);
+ if (next_sr_string == NULL) {
+ ALOGE("%s: could not find min rates string", __func__);
+ return -EINVAL;
+ }
+ if (strstr(rates_str, "continuous") != NULL) {
+ min_sr = (uint32_t)atoi(next_sr_string);
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ if (next_sr_string == NULL) {
+ ALOGE("%s: could not find max rates string", __func__);
+ return -EINVAL;
+ }
+ max_sr = (uint32_t)atoi(next_sr_string);
+
+ for (i = 0; i < MAX_SAMPLE_RATE_SIZE; i++) {
+ if (supported_sample_rates[i] >= min_sr &&
+ supported_sample_rates[i] <= max_sr) {
+ config->rates[sr_size++] = supported_sample_rates[i];
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: continuous sample rate supported_sample_rates[%d] %d",
+ __func__, i, supported_sample_rates[i]);
+ }
+ }
+ } else {
+ do {
+ sr = (uint32_t)atoi(next_sr_string);
+ for (i = 0; i < MAX_SAMPLE_RATE_SIZE; i++) {
+ if (supported_sample_rates[i] == sr) {
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: sr %d, supported_sample_rates[%d] %d -> matches!!",
+ __func__, sr, i, supported_sample_rates[i]);
+ config->rates[sr_size++] = supported_sample_rates[i];
+ }
+ }
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ } while (next_sr_string != NULL);
+ }
+ config->rate_size = sr_size;
+ return 0;
+}
+
+static int usb_get_capability(int type,
+ struct usb_card_config *usb_card_info,
+ int card)
+{
+ int32_t size = 0;
+ int32_t fd=-1;
+ int32_t channels_no;
+ char *str_start = NULL;
+ char *str_end = NULL;
+ char *channel_start = NULL;
+ char *bit_width_start = NULL;
+ char *rates_str_start = NULL;
+ char *target = NULL;
+ char *read_buf = NULL;
+ char *rates_str = NULL;
+ char path[128];
+ int ret = 0;
+ char *bit_width_str = NULL;
+ struct usb_device_config * usb_device_info;
+ bool check = false;
+
+ memset(path, 0, sizeof(path));
+ ALOGV("%s: for %s", __func__, (type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR);
+
+ /* TODO: convert the below to using alsa_utils */
+ ret = snprintf(path, sizeof(path), "/proc/asound/card%u/stream0",
+ card);
+ if(ret < 0) {
+ ALOGE("%s: failed on snprintf (%d) to path %s\n",
+ __func__, ret, path);
+ goto done;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd <0) {
+ ALOGE("%s: error failed to open config file %s error: %d\n",
+ __func__, path, errno);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ read_buf = (char *)calloc(1, USB_BUFF_SIZE + 1);
+
+ if (!read_buf) {
+ ALOGE("Failed to create read_buf");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if(read(fd, read_buf, USB_BUFF_SIZE) < 0) {
+ ALOGE("file read error\n");
+ goto done;
+ }
+ str_start = strstr(read_buf, ((type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR));
+ if (str_start == NULL) {
+ ALOGE("%s: error %s section not found in usb config file",
+ __func__, ((type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR));
+ ret = -EINVAL;
+ goto done;
+ }
+ str_end = strstr(read_buf, ((type == USB_PLAYBACK) ?
+ CAPTURE_PROFILE_STR : PLAYBACK_PROFILE_STR));
+ if (str_end > str_start)
+ check = true;
+
+ ALOGV("%s: usb_config = %s, check %d\n", __func__, str_start, check);
+
+ while (str_start != NULL) {
+ str_start = strstr(str_start, "Altset");
+ if ((str_start == NULL) || (check && (str_start >= str_end))) {
+ ALOGV("%s: done parsing %s\n", __func__, str_start);
+ break;
+ }
+ ALOGV("%s: remaining string %s\n", __func__, str_start);
+ str_start += sizeof("Altset");
+ usb_device_info = calloc(1, sizeof(struct usb_device_config));
+ if (usb_device_info == NULL) {
+ ALOGE("%s: error unable to allocate memory",
+ __func__);
+ ret = -ENOMEM;
+ break;
+ }
+ /* Bit bit_width parsing */
+ bit_width_start = strstr(str_start, "Format: ");
+ if (bit_width_start == NULL) {
+ ALOGI("%s: Could not find bit_width string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ target = strchr(bit_width_start, '\n');
+ if (target == NULL) {
+ ALOGI("%s:end of line not found", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ size = target - bit_width_start;
+ if ((bit_width_str = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: unable to allocate memory to hold bit width strings",
+ __func__);
+ ret = -EINVAL;
+ free(usb_device_info);
+ break;
+ }
+ memcpy(bit_width_str, bit_width_start, size);
+ bit_width_str[size] = '\0';
+ if (strstr(bit_width_str, "S16_LE"))
+ usb_device_info->bit_width = 16;
+ else if (strstr(bit_width_str, "S24_LE"))
+ usb_device_info->bit_width = 24;
+ else if (strstr(bit_width_str, "S24_3LE"))
+ usb_device_info->bit_width = 24;
+ else if (strstr(bit_width_str, "S32_LE"))
+ usb_device_info->bit_width = 32;
+
+ if (bit_width_str)
+ free(bit_width_str);
+
+ /* channels parsing */
+ channel_start = strstr(str_start, CHANNEL_NUMBER_STR);
+ if (channel_start == NULL) {
+ ALOGI("%s: could not find Channels string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ channels_no = atoi(channel_start + strlen(CHANNEL_NUMBER_STR));
+ usb_device_info->channel_count = channels_no;
+
+ /* Sample rates parsing */
+ rates_str_start = strstr(str_start, "Rates: ");
+ if (rates_str_start == NULL) {
+ ALOGI("%s: cant find rates string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ target = strchr(rates_str_start, '\n');
+ if (target == NULL) {
+ ALOGI("%s: end of line not found", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ size = target - rates_str_start;
+ if ((rates_str = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: unable to allocate memory to hold sample rate strings",
+ __func__);
+ ret = -EINVAL;
+ free(usb_device_info);
+ break;
+ }
+ memcpy(rates_str, rates_str_start, size);
+ rates_str[size] = '\0';
+ ret = usb_get_sample_rates(rates_str, usb_device_info);
+ if (rates_str)
+ free(rates_str);
+ if (ret < 0) {
+ ALOGE("%s: error unable to get sample rate values",
+ __func__);
+ free(usb_device_info);
+ continue;
+ }
+ /* Add to list if every field is valid */
+ list_add_tail(&usb_card_info->usb_device_conf_list,
+ &usb_device_info->list);
+ }
+
+done:
+ if (fd >= 0) close(fd);
+ if (read_buf) free(read_buf);
+ return ret;
+}
+
+static int usb_get_device_playback_config(struct usb_card_config *usb_card_info,
+ int card)
+{
+ int ret;
+
+ /* get capabilities */
+ if ((ret = usb_get_capability(USB_PLAYBACK, usb_card_info, card))) {
+ ALOGE("%s: could not get Playback capabilities from usb device",
+ __func__);
+ goto exit;
+ }
+ usb_set_dev_id_mixer_ctl(USB_PLAYBACK, card, "USB_AUDIO_RX dev_token");
+
+exit:
+
+ return ret;
+}
+
+static int usb_get_device_capture_config(struct usb_card_config *usb_card_info,
+ int card)
+{
+ int ret;
+
+ /* get capabilities */
+ if ((ret = usb_get_capability(USB_CAPTURE, usb_card_info, card))) {
+ ALOGE("%s: could not get Playback capabilities from usb device",
+ __func__);
+ goto exit;
+ }
+ usb_set_dev_id_mixer_ctl(USB_CAPTURE, card, "USB_AUDIO_TX dev_token");
+
+exit:
+ return ret;
+}
+
+static void usb_get_sidetone_mixer(struct usb_card_config *usb_card_info)
+{
+ struct mixer_ctl *ctl;
+ unsigned int index;
+
+ for (index = 0; index < USB_SIDETONE_MAX_INDEX; index++)
+ usb_card_info->usb_sidetone_index[index] = -1;
+
+ usb_card_info->usb_snd_mixer = mixer_open(usb_card_info->usb_card);
+ for (index = 0;
+ index < sizeof(usb_sidetone_enable_str)/sizeof(usb_sidetone_enable_str[0]);
+ index++) {
+ ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,
+ usb_sidetone_enable_str[index]);
+ if (ctl) {
+ usb_card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX] = index;
+ /* Disable device sidetone by default */
+ mixer_ctl_set_value(ctl, 0, false);
+ break;
+ }
+ }
+ for (index = 0;
+ index < sizeof(usb_sidetone_volume_str)/sizeof(usb_sidetone_volume_str[0]);
+ index++) {
+ ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,
+ usb_sidetone_volume_str[index]);
+ if (ctl) {
+ usb_card_info->usb_sidetone_index[USB_SIDETONE_VOLUME_INDEX] = index;
+ usb_card_info->usb_sidetone_vol_min = mixer_ctl_get_range_min(ctl);
+ usb_card_info->usb_sidetone_vol_max = mixer_ctl_get_range_max(ctl);
+ break;
+ }
+ }
+
+ if ((usb_card_info->usb_snd_mixer != NULL) && (usb_audio_debug_enable))
+ usb_soundcard_list_controls(usb_card_info->usb_snd_mixer);
+
+ return;
+}
+
+static bool usb_valid_device(audio_devices_t device)
+{
+ if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_USB_DEVICE))
+ return true;
+
+ if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_USB_DEVICE) != 0)
+ return true;
+ }
+
+ return false;
+}
+
+static void usb_print_active_device(void){
+ struct listnode *node_i, *node_j;
+ struct usb_device_config *dev_info;
+ struct usb_card_config *card_info;
+ unsigned int i;
+
+ ALOGI("%s", __func__);
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ list_for_each(node_j, &card_info->usb_device_conf_list) {
+ dev_info = node_to_item(node_j, struct usb_device_config, list);
+ ALOGI("%s: bit-width(%d) channel(%d)",
+ __func__, dev_info->bit_width, dev_info->channel_count);
+ for (i = 0; i < dev_info->rate_size; i++)
+ ALOGI("%s: rate %d", __func__, dev_info->rates[i]);
+ }
+ }
+}
+
+static bool usb_get_best_match_for_bit_width(
+ struct listnode *dev_list,
+ unsigned int stream_bit_width,
+ unsigned int *bit_width)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 0;
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB bw(%d), stream bw(%d), candidate(%d)",
+ __func__, dev_info->bit_width,
+ stream_bit_width, candidate);
+ if (dev_info->bit_width == stream_bit_width) {
+ *bit_width = dev_info->bit_width;
+ ALOGV("%s: Found match bit-width (%d)",
+ __func__, dev_info->bit_width);
+ goto exit;
+ } else if (candidate == 0) {
+ candidate = dev_info->bit_width;
+ }
+ /*
+ * If stream bit is 24, USB supports both 16 bit and 32 bit, then
+ * higher bit width 32 is picked up instead of 16-bit
+ */
+ else if (ABS_SUB(stream_bit_width, dev_info->bit_width) <
+ ABS_SUB(stream_bit_width, candidate)) {
+ candidate = dev_info->bit_width;
+ }
+ else if ((ABS_SUB(stream_bit_width, dev_info->bit_width) ==
+ ABS_SUB(stream_bit_width, candidate)) &&
+ (dev_info->bit_width > candidate)) {
+ candidate = dev_info->bit_width;
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate bw(%d)",
+ __func__, candidate);
+ *bit_width = candidate;
+exit:
+ return true;
+}
+
+static bool usb_get_best_match_for_channels(
+ struct listnode *dev_list,
+ unsigned int bit_width,
+ unsigned int stream_ch,
+ unsigned int *channel_count)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 0;
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d), stream ch(%d)bw(%d), candidate(%d)",
+ __func__, dev_info->channel_count, dev_info->bit_width,
+ stream_ch, bit_width, candidate);
+ if (dev_info->bit_width != bit_width)
+ continue;
+ if (dev_info->channel_count== stream_ch) {
+ *channel_count = dev_info->channel_count;
+ ALOGV("%s: Found match channels (%d)",
+ __func__, dev_info->channel_count);
+ goto exit;
+ } else if (candidate == 0)
+ candidate = dev_info->channel_count;
+ /*
+ * If stream channel is 4, USB supports both 3 and 5, then
+ * higher channel 5 is picked up instead of 3
+ */
+ else if (ABS_SUB(stream_ch, dev_info->channel_count) <
+ ABS_SUB(stream_ch, candidate)) {
+ candidate = dev_info->channel_count;
+ } else if ((ABS_SUB(stream_ch, dev_info->channel_count) ==
+ ABS_SUB(stream_ch, candidate)) &&
+ (dev_info->channel_count > candidate)) {
+ candidate = dev_info->channel_count;
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate ch(%d)",
+ __func__, candidate);
+ *channel_count = candidate;
+exit:
+ return true;
+
+}
+
+static bool usb_sample_rate_multiple(
+ unsigned int stream_sample_rate,
+ unsigned int base)
+{
+ return (((stream_sample_rate / base) * base) == stream_sample_rate);
+}
+
+static bool usb_find_sample_rate_candidate(unsigned int base,
+ unsigned stream_rate,
+ unsigned int usb_rate,
+ unsigned int cur_candidate,
+ unsigned int *update_candidate)
+{
+ /* For sample rate, we should consider fracational sample rate as high priority.
+ * For example, if the stream is 88.2kHz and USB device support both 44.1kH and
+ * 48kHz sample rate, we should pick 44.1kHz instead of 48kHz
+ */
+ if (!usb_sample_rate_multiple(cur_candidate, base) &&
+ usb_sample_rate_multiple(usb_rate, base)) {
+ *update_candidate = usb_rate;
+ } else if (usb_sample_rate_multiple(cur_candidate, base) &&
+ usb_sample_rate_multiple(usb_rate, base)) {
+ if (ABS_SUB(stream_rate, usb_rate) <
+ ABS_SUB(stream_rate, cur_candidate)) {
+ *update_candidate = usb_rate;
+ } else if ((ABS_SUB(stream_rate, usb_rate) ==
+ ABS_SUB(stream_rate, cur_candidate)) &&
+ (usb_rate > cur_candidate)) {
+ *update_candidate = usb_rate;
+ }
+ } else if (!usb_sample_rate_multiple(cur_candidate, base) &&
+ !usb_sample_rate_multiple(usb_rate, base)) {
+ if (ABS_SUB(stream_rate, usb_rate) <
+ ABS_SUB(stream_rate, cur_candidate)) {
+ *update_candidate = usb_rate;
+ } else if ((ABS_SUB(stream_rate, usb_rate) ==
+ ABS_SUB(stream_rate, cur_candidate)) &&
+ (usb_rate > cur_candidate)) {
+ *update_candidate = usb_rate;
+ }
+ }
+ return true;
+}
+
+static bool usb_get_best_match_for_sample_rate(
+ struct listnode *dev_list,
+ unsigned int bit_width,
+ unsigned int channel_count,
+ unsigned int stream_sample_rate,
+ unsigned int *sr)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 48000;
+ unsigned int base = SAMPLE_RATE_8000;
+ bool multiple_8k = usb_sample_rate_multiple(stream_sample_rate, base);
+ unsigned int i;
+
+ ALOGV("%s: stm ch(%d)bw(%d)sr(%d), stream sample multiple of 8kHz(%d)",
+ __func__, channel_count, bit_width, stream_sample_rate, multiple_8k);
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
+ __func__, dev_info->channel_count, dev_info->bit_width,
+ channel_count, bit_width, stream_sample_rate, candidate);
+ if ((dev_info->bit_width != bit_width) || dev_info->channel_count != channel_count)
+ continue;
+
+ candidate = 0;
+ for (i = 0; i < dev_info->rate_size; i++) {
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d)sr(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
+ __func__, dev_info->channel_count,
+ dev_info->bit_width, dev_info->rates[i],
+ channel_count, bit_width, stream_sample_rate, candidate);
+ if (stream_sample_rate == dev_info->rates[i]) {
+ *sr = dev_info->rates[i];
+ ALOGV("%s: Found match sample rate (%d)",
+ __func__, dev_info->rates[i]);
+ goto exit;
+ } else if (candidate == 0) {
+ candidate = dev_info->rates[i];
+ /*
+ * For sample rate, we should consider fracational sample rate as high priority.
+ * For example, if the stream is 88.2kHz and USB device support both 44.1kH and
+ * 48kHz sample rate, we should pick 44.1kHz instead of 48kHz
+ */
+ } else if (multiple_8k) {
+ usb_find_sample_rate_candidate(SAMPLE_RATE_8000,
+ stream_sample_rate,
+ dev_info->rates[i],
+ candidate,
+ &candidate);
+ } else {
+ usb_find_sample_rate_candidate(SAMPLE_RATE_11025,
+ stream_sample_rate,
+ dev_info->rates[i],
+ candidate,
+ &candidate);
+ }
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate sr(%d)",
+ __func__, candidate);
+ *sr = candidate;
+exit:
+ return true;
+}
+
+static bool usb_audio_backend_apply_policy(struct listnode *dev_list,
+ unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *channel_count)
+{
+ ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) channels (%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+ if (list_empty(dev_list)) {
+ *sample_rate = 48000;
+ *bit_width = 16;
+ *channel_count = 2;
+ ALOGE("%s: list is empty,fall back to default setting", __func__);
+ goto exit;
+ }
+ usb_get_best_match_for_bit_width(dev_list, *bit_width, bit_width);
+ usb_get_best_match_for_channels(dev_list,
+ *bit_width,
+ *channel_count,
+ channel_count);
+ usb_get_best_match_for_sample_rate(dev_list,
+ *bit_width,
+ *channel_count,
+ *sample_rate,
+ sample_rate);
+exit:
+ ALOGV("%s: Updated sample rate per profile: bit-width(%d) rate(%d) chs(%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+ return true;
+}
+
+static int usb_get_sidetone_gain(struct usb_card_config *card_info)
+{
+ int gain = card_info->usb_sidetone_vol_min + usbmod->sidetone_gain;
+ if (gain > card_info->usb_sidetone_vol_max)
+ gain = card_info->usb_sidetone_vol_max;
+ return gain;
+}
+
+void audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
+ char *value, int len)
+{
+ int err;
+
+ err = str_parms_get_str(parms, USB_SIDETONE_GAIN_STR,
+ value, len);
+ if (err >= 0) {
+ usb_sidetone_gain = pow(10.0, (float)(atoi(value))/10.0);
+ ALOGV("%s: sidetone gain(%s) decimal %d",
+ __func__, value, usb_sidetone_gain);
+ str_parms_del(parms, USB_SIDETONE_GAIN_STR);
+ }
+ return;
+}
+
+int audio_extn_usb_enable_sidetone(int device, bool enable)
+{
+ int ret = -ENODEV;
+ struct listnode *node_i;
+ struct usb_card_config *card_info;
+ int i;
+ ALOGV("%s: card_dev_type (0x%x), sidetone enable(%d)",
+ __func__, device, enable);
+
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGV("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ if (card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if ((i = card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX]) != -1) {
+ struct mixer_ctl *ctl = mixer_get_ctl_by_name(
+ card_info->usb_snd_mixer,
+ usb_sidetone_enable_str[i]);
+ if (ctl)
+ mixer_ctl_set_value(ctl, 0, enable);
+ else
+ break;
+
+ if ((i = card_info->usb_sidetone_index[USB_SIDETONE_VOLUME_INDEX]) != -1) {
+ ctl = mixer_get_ctl_by_name(
+ card_info->usb_snd_mixer,
+ usb_sidetone_volume_str[i]);
+ if (ctl == NULL)
+ ALOGV("%s: sidetone gain mixer command is not found",
+ __func__);
+ else if (enable)
+ mixer_ctl_set_value(ctl, 0,
+ usb_get_sidetone_gain(card_info));
+ }
+ ret = 0;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool audio_extn_usb_is_config_supported(unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *channel_count,
+ bool is_playback)
+{
+ struct listnode *node_i;
+ struct usb_card_config *card_info;
+
+ ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) ch(%d) is_playback(%d)",
+ __func__, *bit_width, *sample_rate, *channel_count, is_playback);
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ /* Currently only apply the first playback sound card configuration */
+ if ((is_playback && card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) ||
+ ((!is_playback) && card_info->usb_device_type == AUDIO_DEVICE_IN_USB_DEVICE)){
+ usb_audio_backend_apply_policy(&card_info->usb_device_conf_list,
+ bit_width,
+ sample_rate,
+ channel_count);
+ break;
+ }
+ }
+ ALOGV("%s: updated: bit-width(%d) sample_rate(%d) channels (%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+
+ return true;
+}
+
+bool audio_extn_usb_is_capture_supported()
+{
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ return false;
+ }
+ ALOGV("%s: capture_supported %d",__func__,usbmod->is_capture_supported);
+ return usbmod->is_capture_supported;
+}
+
+void audio_extn_usb_add_device(audio_devices_t device, int card)
+{
+ struct usb_card_config *usb_card_info;
+ char check_debug_enable[PROPERTY_VALUE_MAX];
+ struct listnode *node_i;
+
+ property_get("audio.usb.enable.debug", check_debug_enable, NULL);
+ if (atoi(check_debug_enable)) {
+ usb_audio_debug_enable = true;
+ }
+
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: parameters device(0x%x), card(%d)",
+ __func__, device, card);
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ goto exit;
+ }
+
+ if (!(usb_valid_device(device)) || (card < 0)) {
+ ALOGE("%s:device(0x%x), card(%d)",
+ __func__, device, card);
+ goto exit;
+ }
+
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ usb_card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: list has capability for card_dev_type (0x%x), card_no(%d)",
+ __func__, usb_card_info->usb_device_type, usb_card_info->usb_card);
+ /* If we have cached the capability */
+ if ((usb_card_info->usb_device_type == device) && (usb_card_info->usb_card == card)) {
+ ALOGV("%s: capability for device(0x%x), card(%d) is cached, no need to update",
+ __func__, device, card);
+ goto exit;
+ }
+ }
+ usb_card_info = calloc(1, sizeof(struct usb_card_config));
+ if (usb_card_info == NULL) {
+ ALOGE("%s: error unable to allocate memory",
+ __func__);
+ goto exit;
+ }
+ list_init(&usb_card_info->usb_device_conf_list);
+ if (device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (!usb_get_device_playback_config(usb_card_info, card)){
+ usb_card_info->usb_card = card;
+ usb_card_info->usb_device_type = AUDIO_DEVICE_OUT_USB_DEVICE;
+ usb_get_sidetone_mixer(usb_card_info);
+ list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
+ goto exit;
+ }
+ } else if (device & AUDIO_DEVICE_IN_USB_DEVICE) {
+ if (!usb_get_device_capture_config(usb_card_info, card)) {
+ usb_card_info->usb_card = card;
+ usb_card_info->usb_device_type = AUDIO_DEVICE_IN_USB_DEVICE;
+ usbmod->is_capture_supported = true;
+ list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
+ goto exit;
+ }
+ }
+ /* free memory in error case */
+ if (usb_card_info != NULL)
+ free(usb_card_info);
+exit:
+ if (usb_audio_debug_enable)
+ usb_print_active_device();
+ return;
+}
+
+void audio_extn_usb_remove_device(audio_devices_t device, int card)
+{
+ struct listnode *node_i, *temp_i;
+ struct listnode *node_j, *temp_j;
+ struct usb_device_config *dev_info;
+ struct usb_card_config *card_info;
+ unsigned int i;
+
+ ALOGV("%s: device(0x%x), card(%d)",
+ __func__, device, card);
+
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ goto exit;
+ }
+
+ if (!(usb_valid_device(device)) || (card < 0)) {
+ ALOGE("%s: Invalid parameters device(0x%x), card(%d)",
+ __func__, device, card);
+ goto exit;
+ }
+ list_for_each_safe(node_i, temp_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGV("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ if ((device == card_info->usb_device_type) && (card == card_info->usb_card)){
+ list_for_each_safe(node_j, temp_j, &card_info->usb_device_conf_list) {
+ dev_info = node_to_item(node_j, struct usb_device_config, list);
+ ALOGV("%s: bit-width(%d) channel(%d)",
+ __func__, dev_info->bit_width, dev_info->channel_count);
+ for (i = 0; i < dev_info->rate_size; i++)
+ ALOGV("%s: rate %d", __func__, dev_info->rates[i]);
+
+ list_remove(node_j);
+ free(node_to_item(node_j, struct usb_device_config, list));
+ }
+ list_remove(node_i);
+ free(node_to_item(node_i, struct usb_card_config, list));
+ }
+ }
+ usbmod->is_capture_supported = false;
+exit:
+ if (usb_audio_debug_enable)
+ usb_print_active_device();
+
+ return;
+}
+
+void audio_extn_usb_init(void *adev)
+{
+ if (usbmod == NULL) {
+ usbmod = calloc(1, sizeof(struct usb_module));
+ if (usbmod == NULL) {
+ ALOGE("%s: error unable to allocate memory", __func__);
+ goto exit;
+ }
+ } else {
+ memset(usbmod, 0, sizeof(*usbmod));
+ }
+
+ list_init(&usbmod->usb_card_conf_list);
+ usbmod->adev = (struct audio_device*)adev;
+ usbmod->sidetone_gain = usb_sidetone_gain;
+ usbmod->is_capture_supported = false;
+exit:
+ return;
+}
+
+void audio_extn_usb_deinit(void)
+{
+ if (NULL != usbmod){
+ free(usbmod);
+ usbmod = NULL;
+ }
+}
+#endif /*USB_HEADSET_ENABLED end*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 85216bc..4af1e9c 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -855,6 +855,8 @@
bool switch_device[AUDIO_USECASE_MAX];
int i, num_uc_to_switch = 0;
+ platform_check_and_set_playback_backend_cfg(adev, uc_info, snd_device);
+
/*
* This function is to make sure that all the usecases that are active on
* the hardware codec backend are always routed to any one device that is
@@ -1199,7 +1201,8 @@
/* Enable new sound devices */
if (out_snd_device != SND_DEVICE_NONE) {
- if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
+ if ((usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
+ (usecase->devices & AUDIO_DEVICE_OUT_USB_DEVICE))
check_and_route_playback_usecases(adev, usecase, out_snd_device);
enable_snd_device(adev, out_snd_device);
}
@@ -3555,6 +3558,43 @@
adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
}
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+ if (device == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_add_device(AUDIO_DEVICE_OUT_USB_DEVICE, card);
+ }
+ } else if (device == AUDIO_DEVICE_IN_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_add_device(AUDIO_DEVICE_IN_USB_DEVICE, card);
+ }
+ }
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+ if (device == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+
+ audio_extn_usb_remove_device(AUDIO_DEVICE_OUT_USB_DEVICE, card);
+ }
+ } else if (device == AUDIO_DEVICE_IN_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_remove_device(AUDIO_DEVICE_IN_USB_DEVICE, card);
+ }
+ }
+ }
+
audio_extn_hfp_set_parameters(adev, parms);
done:
str_parms_destroy(parms);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index b0a6282..35b4bdd 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -49,6 +49,8 @@
#define ACDB_DEV_TYPE_IN 2
#define MAX_SUPPORTED_CHANNEL_MASKS 2
+#define MAX_SUPPORTED_FORMATS 15
+#define MAX_SUPPORTED_SAMPLE_RATES 7
#define DEFAULT_HDMI_OUT_CHANNELS 2
#define ERROR_LOG_ENTRIES 16
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index f7953cc..65600ee 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -1093,6 +1093,13 @@
return 0;
}
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev __unused,
+ struct audio_usecase *usecase __unused,
+ snd_device_t snd_device __unused)
+{
+ return false;
+}
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev __unused,
struct audio_usecase *usecase __unused,
snd_device_t snd_device __unused)
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index eafb64a..072a1e4 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1104,6 +1104,13 @@
return 0;
}
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev __unused,
+ struct audio_usecase *usecase __unused,
+ snd_device_t snd_device __unused)
+{
+ return false;
+}
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev __unused,
struct audio_usecase *usecase __unused)
{
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 3deac03..9c81f97 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -138,6 +138,7 @@
struct csd_data *csd;
char ec_ref_mixer_path[64];
+ codec_backend_cfg_t current_backend_cfg[MAX_CODEC_BACKENDS];
char *snd_card_name;
int max_vol_index;
int max_mic_count;
@@ -227,6 +228,9 @@
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
[SND_DEVICE_OUT_VOICE_TX] = "voice-tx",
+ [SND_DEVICE_OUT_USB_HEADSET] = "usb-headset",
+ [SND_DEVICE_OUT_USB_HEADPHONES] = "usb-headphones",
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones",
[SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected",
[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = "voice-speaker-hfp",
@@ -278,6 +282,7 @@
[SND_DEVICE_IN_VOICE_REC_MIC_AEC_NS] = "voice-rec-mic",
[SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = "voice-rec-dmic-ef",
[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic",
[SND_DEVICE_IN_VOICE_REC_HEADSET_MIC] = "headset-mic",
[SND_DEVICE_IN_UNPROCESSED_MIC] = "unprocessed-mic",
@@ -324,6 +329,9 @@
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
[SND_DEVICE_OUT_VOICE_TX] = 45,
+ [SND_DEVICE_OUT_USB_HEADSET] = 45,
+ [SND_DEVICE_OUT_USB_HEADPHONES] = 45,
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14,
[SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124,
[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101,
[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = ACDB_ID_VOICE_SPEAKER,
@@ -383,7 +391,7 @@
[SND_DEVICE_IN_UNPROCESSED_QUAD_MIC] = 125,
[SND_DEVICE_IN_VOICE_RX] = 44,
-
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = 44,
[SND_DEVICE_IN_THREE_MIC] = 46,
[SND_DEVICE_IN_QUAD_MIC] = 46,
[SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102,
@@ -393,6 +401,9 @@
[SND_DEVICE_IN_HANDSET_QMIC_AEC] = 125, /* override this for new target to 140 */
};
+// Platform specific backend bit width table
+static int backend_bit_width_table[SND_DEVICE_MAX] = {0};
+
struct name_to_index {
char name[100];
unsigned int index;
@@ -427,6 +438,9 @@
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADSET)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADPHONES)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)},
/* in */
{TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
@@ -478,6 +492,7 @@
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_HEADSET_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_USB_HEADSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_UNPROCESSED_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_UNPROCESSED_HEADSET_MIC)},
@@ -898,6 +913,10 @@
operator_specific_device_table[dev] = NULL;
}
+ for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+ backend_bit_width_table[dev] = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ }
+
// To overwrite these go to the audio_platform_info.xml file.
backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
@@ -910,6 +929,11 @@
backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
backend_tag_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+ backend_tag_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headset");
+ backend_tag_table[SND_DEVICE_OUT_USB_HEADPHONES] = strdup("usb-headphones");
+ backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
+ strdup("speaker-and-usb-headphones");
+ backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
hw_interface_table[SND_DEVICE_OUT_HANDSET] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("SLIMBUS_0_RX");
@@ -933,6 +957,9 @@
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("USB_AUDIO_RX");
+ hw_interface_table[SND_DEVICE_OUT_USB_HEADPHONES] = strdup("USB_AUDIO_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = strdup("SLIMBUS_0_RX-and-USB_AUDIO_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
@@ -1000,6 +1027,57 @@
return 0;
}
+static void
+platform_backend_config_init(struct platform_data *pdata)
+{
+ int i;
+
+ /* initialize backend config */
+ for (i = 0; i < MAX_CODEC_BACKENDS; i++) {
+ pdata->current_backend_cfg[i].sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ pdata->current_backend_cfg[i].bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ pdata->current_backend_cfg[i].channels = CODEC_BACKEND_DEFAULT_CHANNELS;
+
+ if (i > MAX_RX_CODEC_BACKENDS)
+ pdata->current_backend_cfg[i].channels = CODEC_BACKEND_DEFAULT_TX_CHANNELS;
+
+ pdata->current_backend_cfg[i].bitwidth_mixer_ctl = NULL;
+ pdata->current_backend_cfg[i].samplerate_mixer_ctl = NULL;
+ pdata->current_backend_cfg[i].channels_mixer_ctl = NULL;
+ }
+
+ pdata->current_backend_cfg[DEFAULT_CODEC_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_0_RX Format");
+ pdata->current_backend_cfg[DEFAULT_CODEC_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_0_RX SampleRate");
+
+ pdata->current_backend_cfg[DEFAULT_CODEC_TX_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_0_TX Format");
+ pdata->current_backend_cfg[DEFAULT_CODEC_TX_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_0_TX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].bitwidth_mixer_ctl =
+ strdup("USB_AUDIO_TX Format");
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].samplerate_mixer_ctl =
+ strdup("USB_AUDIO_TX SampleRate");
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].channels_mixer_ctl =
+ strdup("USB_AUDIO_TX Channels");
+
+ pdata->current_backend_cfg[HEADPHONE_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_6_RX Format");
+ pdata->current_backend_cfg[HEADPHONE_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_6_RX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].bitwidth_mixer_ctl =
+ strdup("USB_AUDIO_RX Format");
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].samplerate_mixer_ctl =
+ strdup("USB_AUDIO_RX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].channels = 1;
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].channels_mixer_ctl =
+ strdup("USB_AUDIO_RX Channels");
+}
+
void *platform_init(struct audio_device *adev)
{
char value[PROPERTY_VALUE_MAX];
@@ -1298,6 +1376,9 @@
platform_acdb_init(my_data);
}
+ /* init usb */
+ audio_extn_usb_init(adev);
+
audio_extn_spkr_prot_init(adev);
audio_extn_hwdep_cal_send(adev->snd_card, my_data->acdb_handle);
@@ -1305,6 +1386,8 @@
/* load csd client */
platform_csd_init(my_data);
+ platform_backend_config_init(my_data);
+
return my_data;
init_failed:
@@ -1356,6 +1439,9 @@
}
free(platform);
+
+ /* deinit usb */
+ audio_extn_usb_deinit();
}
const char *platform_get_snd_device_name(snd_device_t snd_device)
@@ -1556,6 +1642,38 @@
return acdb_device_table[snd_device];
}
+static int platform_get_backend_index(snd_device_t snd_device)
+{
+ int32_t port = DEFAULT_CODEC_BACKEND;
+
+ if (snd_device >= SND_DEVICE_OUT_BEGIN && snd_device < SND_DEVICE_OUT_END) {
+ if (backend_tag_table[snd_device] != NULL) {
+ if (strncmp(backend_tag_table[snd_device], "headphones",
+ sizeof("headphones")) == 0)
+ port = HEADPHONE_BACKEND;
+ else if (strcmp(backend_tag_table[snd_device], "hdmi") == 0)
+ port = HDMI_RX_BACKEND;
+ else if ((strcmp(backend_tag_table[snd_device], "usb-headphones") == 0) ||
+ (strcmp(backend_tag_table[snd_device], "usb-headset") == 0))
+ port = USB_AUDIO_RX_BACKEND;
+ }
+ } else if (snd_device >= SND_DEVICE_IN_BEGIN && snd_device < SND_DEVICE_IN_END) {
+ port = DEFAULT_CODEC_TX_BACKEND;
+ if (backend_tag_table[snd_device] != NULL) {
+ if (strcmp(backend_tag_table[snd_device], "usb-headset-mic") == 0)
+ port = USB_AUDIO_TX_BACKEND;
+ else if (strstr(backend_tag_table[snd_device], "bt-sco") != NULL)
+ port = BT_SCO_TX_BACKEND;
+ }
+ } else {
+ ALOGW("%s:napb: Invalid device - %d ", __func__, snd_device);
+ }
+
+ ALOGV("%s:napb: backend port - %d device - %d ", __func__, port, snd_device);
+
+ return port;
+}
+
int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
{
struct platform_data *my_data = (struct platform_data *)platform;
@@ -1919,6 +2037,12 @@
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
new_snd_devices[1] = SND_DEVICE_OUT_LINE;
ret = 0;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_USB_HEADSET)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
+ ret = 0;
}
return ret;
}
@@ -1957,6 +2081,9 @@
} else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
+ } else if (devices == (AUDIO_DEVICE_OUT_USB_DEVICE |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET;
} else {
ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
goto exit;
@@ -2037,7 +2164,12 @@
}
} else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_OUT_HDMI ;
- } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
+ } else if (devices & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (audio_extn_usb_is_capture_supported())
+ snd_device = SND_DEVICE_OUT_USB_HEADSET;
+ else
+ snd_device = SND_DEVICE_OUT_USB_HEADPHONES;
+ }else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
/*HAC support for voice-ish audio (eg visual voicemail)*/
if(adev->voice.hac)
snd_device = SND_DEVICE_OUT_VOICE_HAC_HANDSET;
@@ -2336,6 +2468,8 @@
}
} else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_USB_DEVICE ) {
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
} else {
ALOGE("%s: Unknown input device(s) %#x", __func__, in_device);
ALOGW("%s: Using default handset-mic", __func__);
@@ -2376,6 +2510,11 @@
}
} else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (audio_extn_usb_is_capture_supported())
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
+ else
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC;
} else {
ALOGE("%s: Unknown output device(s) %#x", __func__, out_device);
ALOGW("%s: Using default handset-mic", __func__);
@@ -2636,27 +2775,6 @@
}
}
-bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
- struct audio_usecase *usecase __unused,
- snd_device_t snd_device __unused)
-{
- enum pcm_format in_pcm_format = PCM_FORMAT_S16_LE;
-
- if (adev && adev->active_input)
- in_pcm_format = adev->active_input->config.format;
-
- // allow 24 bit recording only if voice call is not active
- if (!voice_is_in_call(adev) &&
- adev->mode != AUDIO_MODE_IN_COMMUNICATION &&
- in_pcm_format == PCM_FORMAT_S24_LE) {
- audio_route_apply_and_update_path(adev->audio_route, "set-capture-format-24le");
- } else {
- audio_route_apply_and_update_path(adev->audio_route, "set-capture-format-default");
- }
-
- return true;
-}
-
int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
const char * hw_interface)
{
@@ -2879,3 +2997,464 @@
}
return 0;
}
+
+/*
+ * configures afe with bit width and Sample Rate
+ */
+static int platform_set_backend_cfg(const struct audio_device* adev,
+ snd_device_t snd_device,
+ const struct audio_backend_cfg *backend_cfg)
+{
+
+ int ret = 0;
+ const int backend_idx = platform_get_backend_index(snd_device);
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ const unsigned int bit_width = backend_cfg->bit_width;
+ const unsigned int sample_rate = backend_cfg->sample_rate;
+ const unsigned int channels = backend_cfg->channels;
+ const audio_format_t format = backend_cfg->format;
+ const bool passthrough_enabled = backend_cfg->passthrough_enabled;
+
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d device (%s)", __func__, bit_width,
+ sample_rate, channels, backend_idx,
+ platform_get_snd_device_name(snd_device));
+
+ if ((my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl) &&
+ (bit_width != my_data->current_backend_cfg[backend_idx].bit_width)) {
+
+ struct mixer_ctl *ctl = NULL;
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl);
+ if (!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl);
+ return -EINVAL;
+ }
+
+ if (bit_width == 24) {
+ if (format == AUDIO_FORMAT_PCM_24_BIT_PACKED)
+ ret = mixer_ctl_set_enum_by_string(ctl, "S24_3LE");
+ else
+ ret = mixer_ctl_set_enum_by_string(ctl, "S24_LE");
+ } else if (bit_width == 32) {
+ ret = mixer_ctl_set_enum_by_string(ctl, "S32_LE");
+ } else {
+ ret = mixer_ctl_set_enum_by_string(ctl, "S16_LE");
+ }
+ if ( ret < 0) {
+ ALOGE("%s:becf: afe: fail for %s mixer set to %d bit for %x format", __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl, bit_width, format);
+ } else {
+ my_data->current_backend_cfg[backend_idx].bit_width = bit_width;
+ ALOGD("%s:becf: afe: %s mixer set to %d bit for %x format", __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl, bit_width, format);
+ }
+ /* set the ret as 0 and not pass back to upper layer */
+ ret = 0;
+ }
+
+ if (passthrough_enabled || ((my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl) &&
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate))) {
+ char *rate_str = NULL;
+ struct mixer_ctl *ctl = NULL;
+
+ switch (sample_rate) {
+ case 32000:
+ if (passthrough_enabled) {
+ rate_str = "KHZ_32";
+ break;
+ }
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 48000:
+ rate_str = "KHZ_48";
+ break;
+ case 44100:
+ rate_str = "KHZ_44P1";
+ break;
+ case 64000:
+ case 96000:
+ rate_str = "KHZ_96";
+ break;
+ case 88200:
+ rate_str = "KHZ_88P2";
+ break;
+ case 176400:
+ rate_str = "KHZ_176P4";
+ break;
+ case 192000:
+ rate_str = "KHZ_192";
+ break;
+ case 352800:
+ rate_str = "KHZ_352P8";
+ break;
+ case 384000:
+ rate_str = "KHZ_384";
+ break;
+ case 144000:
+ if (passthrough_enabled) {
+ rate_str = "KHZ_144";
+ break;
+ }
+ default:
+ rate_str = "KHZ_48";
+ break;
+ }
+
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl);
+ if(!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl);
+ return -EINVAL;
+ }
+
+ ALOGD("%s:becf: afe: %s set to %s", __func__,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl, rate_str);
+ mixer_ctl_set_enum_by_string(ctl, rate_str);
+ my_data->current_backend_cfg[backend_idx].sample_rate = sample_rate;
+ }
+ if ((my_data->current_backend_cfg[backend_idx].channels_mixer_ctl) &&
+ (channels != my_data->current_backend_cfg[backend_idx].channels)) {
+ struct mixer_ctl *ctl = NULL;
+ char *channel_cnt_str = NULL;
+
+ switch (channels) {
+ case 8:
+ channel_cnt_str = "Eight"; break;
+ case 7:
+ channel_cnt_str = "Seven"; break;
+ case 6:
+ channel_cnt_str = "Six"; break;
+ case 5:
+ channel_cnt_str = "Five"; break;
+ case 4:
+ channel_cnt_str = "Four"; break;
+ case 3:
+ channel_cnt_str = "Three"; break;
+ case 1:
+ channel_cnt_str = "One"; break;
+ case 2:
+ default:
+ channel_cnt_str = "Two"; break;
+ }
+
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl);
+ if (!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl);
+ return -EINVAL;
+ }
+ mixer_ctl_set_enum_by_string(ctl, channel_cnt_str);
+ my_data->current_backend_cfg[backend_idx].channels = channels;
+
+ // skip EDID configuration for HDMI backend
+
+ ALOGD("%s:becf: afe: %s set to %s", __func__,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl,
+ channel_cnt_str);
+ }
+
+ // skip set ext_display format mixer control
+ return ret;
+}
+
+static int platform_get_snd_device_bit_width(snd_device_t snd_device)
+{
+ if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+ ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+ return CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ }
+
+ return backend_bit_width_table[snd_device];
+}
+
+/*
+ * return backend_idx on which voice call is active
+ */
+static int platform_get_voice_call_backend(struct audio_device* adev)
+{
+ struct audio_usecase *uc = NULL;
+ struct listnode *node;
+ snd_device_t out_snd_device = SND_DEVICE_NONE;
+
+ int backend_idx = -1;
+
+ if (voice_is_in_call(adev) || adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+ list_for_each(node, &adev->usecase_list) {
+ uc = node_to_item(node, struct audio_usecase, list);
+ if (uc && uc->type == VOICE_CALL && uc->stream.out) {
+ out_snd_device = platform_get_output_snd_device(adev->platform,
+ uc->stream.out->devices);
+ backend_idx = platform_get_backend_index(out_snd_device);
+ break;
+ }
+ }
+ }
+ return backend_idx;
+}
+
+/*
+ * goes through all the current usecases and picks the highest
+ * bitwidth & samplerate
+ */
+static bool platform_check_capture_backend_cfg(struct audio_device* adev,
+ int backend_idx,
+ struct audio_backend_cfg *backend_cfg)
+{
+ bool backend_change = false;
+ unsigned int bit_width;
+ unsigned int sample_rate;
+ unsigned int channels;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ bit_width = backend_cfg->bit_width;
+ sample_rate = backend_cfg->sample_rate;
+ channels = backend_cfg->channels;
+
+ ALOGV("%s:txbecf: afe: Codec selected backend: %d current bit width: %d and "
+ "sample rate: %d, channels %d",__func__,backend_idx, bit_width,
+ sample_rate, channels);
+
+ // For voice calls use default configuration i.e. 16b/48K, only applicable to
+ // default backend
+ // force routing is not required here, caller will do it anyway
+ if (voice_is_in_call(adev) || adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+ ALOGW("%s:txbecf: afe: Use default bw and sr for voice/voip calls and "
+ "for unprocessed/camera source", __func__);
+ bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ }
+
+ if (backend_idx == USB_AUDIO_TX_BACKEND) {
+ audio_extn_usb_is_config_supported(&bit_width, &sample_rate, &channels, false);
+ ALOGV("%s:txbecf: afe: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
+ __func__, bit_width, sample_rate, channels);
+ }
+
+ ALOGV("%s:txbecf: afe: Codec selected backend: %d updated bit width: %d and "
+ "sample rate: %d", __func__, backend_idx, bit_width, sample_rate);
+
+ // Force routing if the expected bitwdith or samplerate
+ // is not same as current backend comfiguration
+ if ((bit_width != my_data->current_backend_cfg[backend_idx].bit_width) ||
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate) ||
+ (channels != my_data->current_backend_cfg[backend_idx].channels)) {
+ backend_cfg->bit_width = bit_width;
+ backend_cfg->sample_rate= sample_rate;
+ backend_cfg->channels = channels;
+ backend_change = true;
+ ALOGI("%s:txbecf: afe: Codec backend needs to be updated. new bit width: %d "
+ "new sample rate: %d new channel: %d",
+ __func__, backend_cfg->bit_width,
+ backend_cfg->sample_rate, backend_cfg->channels);
+ }
+
+ return backend_change;
+}
+
+static bool platform_check_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase* usecase,
+ snd_device_t snd_device,
+ struct audio_backend_cfg *backend_cfg)
+{
+ bool backend_change = false;
+ struct listnode *node;
+ unsigned int bit_width;
+ unsigned int sample_rate;
+ unsigned int channels;
+ bool passthrough_enabled = false;
+ int backend_idx = DEFAULT_CODEC_BACKEND;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ bool channels_updated = false;
+
+ if (snd_device == SND_DEVICE_OUT_BT_SCO ||
+ snd_device == SND_DEVICE_OUT_BT_SCO_WB) {
+ backend_change = false;
+ return backend_change;
+ }
+
+ backend_idx = platform_get_backend_index(snd_device);
+ bit_width = backend_cfg->bit_width;
+ sample_rate = backend_cfg->sample_rate;
+ channels = backend_cfg->channels;
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__, bit_width,
+ sample_rate, channels, backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (backend_idx == platform_get_voice_call_backend(adev)) {
+ ALOGW("%s:becf: afe:Use default bw and sr for voice/voip calls ",
+ __func__);
+ bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ channels = CODEC_BACKEND_DEFAULT_CHANNELS;
+ } else {
+ /*
+ * The backend should be configured at highest bit width and/or
+ * sample rate amongst all playback usecases.
+ * If the selected sample rate and/or bit width differ with
+ * current backend sample rate and/or bit width, then, we set the
+ * backend re-configuration flag.
+ *
+ * Exception: 16 bit playbacks is allowed through 16 bit/48/44.1 khz backend only
+ */
+
+ int i =0;
+ list_for_each(node, &adev->usecase_list) {
+ struct audio_usecase *uc;
+ uc = node_to_item(node, struct audio_usecase, list);
+ struct stream_out *out = (struct stream_out*) uc->stream.out;
+ if (uc->type == PCM_PLAYBACK && out && usecase != uc) {
+ unsigned int out_channels = audio_channel_count_from_out_mask(out->channel_mask);
+
+ ALOGD("%s:napb: (%d) - (%s)id (%d) sr %d bw "
+ "(%d) ch (%d) device %s", __func__, i++, use_case_table[uc->id],
+ uc->id, out->sample_rate,
+ pcm_format_to_bits(out->config.format), out_channels,
+ platform_get_snd_device_name(uc->out_snd_device));
+
+ if (platform_check_backends_match(snd_device, uc->out_snd_device)) {
+ if (bit_width < pcm_format_to_bits(out->config.format))
+ bit_width = pcm_format_to_bits(out->config.format);
+ if (sample_rate < out->sample_rate)
+ sample_rate = out->sample_rate;
+ if (out->sample_rate < OUTPUT_SAMPLING_RATE_44100)
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ if (channels < out_channels)
+ channels = out_channels;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check if the device is speaker or handset,assumption handset shares
+ * backend with speaker, and these devices are restricited to 48kHz.
+ */
+ if (platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, snd_device)) {
+
+ if (bit_width >= 24) {
+ bit_width = platform_get_snd_device_bit_width(SND_DEVICE_OUT_SPEAKER);
+ ALOGD("%s:becf: afe: reset bitwidth to %d (based on supported"
+ " value for this platform)", __func__, bit_width);
+ }
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ ALOGD("%s:becf: afe: playback on codec device not supporting native playback set "
+ "default Sample Rate(48k)", __func__);
+ }
+
+ if (backend_idx == USB_AUDIO_RX_BACKEND) {
+ audio_extn_usb_is_config_supported(&bit_width, &sample_rate, &channels, true);
+ ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
+ __func__, bit_width, sample_rate, channels);
+ if (channels != my_data->current_backend_cfg[backend_idx].channels)
+ channels_updated = true;
+ }
+
+ ALOGV("%s:becf: afe: Codec selected backend: %d updated bit width: %d and sample rate: %d",
+ __func__, backend_idx , bit_width, sample_rate);
+
+ // Force routing if the expected bitwdith or samplerate
+ // is not same as current backend comfiguration
+ if ((bit_width != my_data->current_backend_cfg[backend_idx].bit_width) ||
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate) ||
+ passthrough_enabled || channels_updated) {
+ backend_cfg->bit_width = bit_width;
+ backend_cfg->sample_rate = sample_rate;
+ backend_cfg->channels = channels;
+ backend_cfg->passthrough_enabled = passthrough_enabled;
+ backend_change = true;
+ ALOGV("%s:becf: afe: Codec backend needs to be updated. new bit width: %d"
+ "new sample rate: %d new channels: %d",
+ __func__, backend_cfg->bit_width, backend_cfg->sample_rate, backend_cfg->channels);
+ }
+
+ return backend_change;
+}
+
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device)
+{
+ int backend_idx = DEFAULT_CODEC_BACKEND;
+ int new_snd_devices[SND_DEVICE_OUT_END];
+ int i, num_devices = 1;
+ bool ret = false;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ struct audio_backend_cfg backend_cfg;
+
+ backend_idx = platform_get_backend_index(snd_device);
+
+ backend_cfg.bit_width = pcm_format_to_bits(usecase->stream.out->config.format);
+ backend_cfg.sample_rate = usecase->stream.out->sample_rate;
+ backend_cfg.format = usecase->stream.out->format;
+ backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+ /*this is populated by check_codec_backend_cfg hence set default value to false*/
+ backend_cfg.passthrough_enabled = false;
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__, backend_cfg.bit_width,
+ backend_cfg.sample_rate, backend_cfg.channels, backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices) < 0)
+ new_snd_devices[0] = snd_device;
+
+ for (i = 0; i < num_devices; i++) {
+ ALOGV("%s: new_snd_devices[%d] is %d", __func__, i, new_snd_devices[i]);
+ if ((platform_check_playback_backend_cfg(adev, usecase, new_snd_devices[i],
+ &backend_cfg))) {
+ platform_set_backend_cfg(adev, new_snd_devices[i],
+ &backend_cfg);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device)
+{
+ int backend_idx = platform_get_backend_index(snd_device);
+ int ret = 0;
+ struct audio_backend_cfg backend_cfg;
+
+ backend_cfg.passthrough_enabled = false;
+
+ if(usecase->type == PCM_CAPTURE) {
+ backend_cfg.format= usecase->stream.in->format;
+ backend_cfg.channels = audio_channel_count_from_in_mask(usecase->stream.in->channel_mask);
+ } else {
+ backend_cfg.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ backend_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ backend_cfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ backend_cfg.channels = 1;
+ }
+
+ ALOGV("%s:txbecf: afe: bitwidth %d, samplerate %d, channel %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__,
+ backend_cfg.bit_width,
+ backend_cfg.sample_rate,
+ backend_cfg.channels,
+ backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (platform_check_capture_backend_cfg(adev, backend_idx, &backend_cfg)) {
+ ret = platform_set_backend_cfg(adev, snd_device,
+ &backend_cfg);
+ if(!ret)
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index e6aee08..cc75384 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -41,6 +41,15 @@
AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | \
AUDIO_DEVICE_OUT_LINE)
+/*
+ * Below are the input devices for which back end is same, SLIMBUS_0_TX.
+ * All these devices are handled by the internal HW codec. We can
+ * enable any one of these devices at any time
+ */
+#define AUDIO_DEVICE_IN_ALL_CODEC_BACKEND \
+ (AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BACK_MIC | \
+ AUDIO_DEVICE_IN_WIRED_HEADSET | AUDIO_DEVICE_IN_VOICE_CALL) & ~AUDIO_DEVICE_BIT_IN
+
/* Sound devices specific to the platform
* The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
* devices to enable corresponding mixer paths
@@ -78,6 +87,9 @@
SND_DEVICE_OUT_SPEAKER_PROTECTED,
SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
SND_DEVICE_OUT_VOICE_SPEAKER_HFP,
+ SND_DEVICE_OUT_USB_HEADSET,
+ SND_DEVICE_OUT_USB_HEADPHONES,
+ SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET,
SND_DEVICE_OUT_END,
/*
@@ -142,6 +154,7 @@
SND_DEVICE_IN_VOICE_RX,
+ SND_DEVICE_IN_USB_HEADSET_MIC,
SND_DEVICE_IN_THREE_MIC,
SND_DEVICE_IN_QUAD_MIC,
SND_DEVICE_IN_CAPTURE_VI_FEEDBACK,
@@ -155,6 +168,24 @@
SND_DEVICE_MAX = SND_DEVICE_IN_END,
};
+#define DEFAULT_OUTPUT_SAMPLING_RATE 48000
+#define OUTPUT_SAMPLING_RATE_44100 44100
+enum {
+ DEFAULT_CODEC_BACKEND,
+ SLIMBUS_0_RX = DEFAULT_CODEC_BACKEND,
+ HEADPHONE_BACKEND,
+ SLIMBUS_6_RX = HEADPHONE_BACKEND,
+ HDMI_RX_BACKEND,
+ USB_AUDIO_RX_BACKEND,
+ MAX_RX_CODEC_BACKENDS = USB_AUDIO_RX_BACKEND,
+ /* TX BE follows RX BE */
+ SLIMBUS_0_TX,
+ DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
+ USB_AUDIO_TX_BACKEND,
+ BT_SCO_TX_BACKEND,
+ MAX_CODEC_BACKENDS
+};
+
#define DEVICE_NAME_MAX_SIZE 128
#define HW_INFO_ARRAY_MAX_SIZE 32
@@ -316,6 +347,23 @@
get_sample_rate_t get_sample_rate;
};
+struct audio_backend_cfg {
+ unsigned int sample_rate;
+ unsigned int channels;
+ unsigned int bit_width;
+ bool passthrough_enabled;
+ audio_format_t format;
+};
+
+typedef struct codec_backend_cfg {
+ uint32_t sample_rate;
+ uint32_t bit_width;
+ uint32_t channels;
+ char *bitwidth_mixer_ctl;
+ char *samplerate_mixer_ctl;
+ char *channels_mixer_ctl;
+} codec_backend_cfg_t;
+
#define PLATFORM_INFO_XML_PATH "/system/etc/audio_platform_info.xml"
#define PLATFORM_INFO_XML_BASE_STRING "/system/etc/audio_platform_info"
#endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 83554b0..59ad4b1 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -20,6 +20,12 @@
#include "voice.h"
#define MAX_VOLUME_CAL_STEPS 15
#define CODEC_BACKEND_DEFAULT_SAMPLE_RATE 48000
+#define CODEC_BACKEND_DEFAULT_BIT_WIDTH 16
+#define CODEC_BACKEND_DEFAULT_CHANNELS 2
+#define CODEC_BACKEND_DEFAULT_TX_CHANNELS 1
+#define SAMPLE_RATE_8000 8000
+#define SAMPLE_RATE_11025 11025
+#define sample_rate_multiple(sr, base) ((sr % base)== 0?true:false)
struct amp_db_and_gain_table {
float amp;
@@ -112,6 +118,9 @@
int platform_set_parameters(void *platform, struct str_parms *parms);
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device);
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
struct audio_usecase *usecase, snd_device_t snd_device);