diff options
-rw-r--r-- | hal/audio_extn/audio_extn.c | 6 | ||||
-rw-r--r-- | hal/audio_extn/audio_extn.h | 25 | ||||
-rw-r--r-- | hal/audio_extn/usb.c | 1224 | ||||
-rw-r--r-- | hal/audio_hw.c | 35 | ||||
-rw-r--r-- | hal/msm8916/platform.c | 33 | ||||
-rw-r--r-- | hal/msm8916/platform.h | 1 | ||||
-rw-r--r-- | hal/msm8974/platform.c | 33 | ||||
-rw-r--r-- | hal/msm8974/platform.h | 1 |
8 files changed, 748 insertions, 610 deletions
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c index 8b8632ea..49e649c6 100644 --- a/hal/audio_extn/audio_extn.c +++ b/hal/audio_extn/audio_extn.c @@ -677,11 +677,7 @@ int audio_extn_get_afe_proxy_parameters(const struct audio_device *adev, ret = str_parms_get_str(query, AUDIO_PARAMETER_CAN_OPEN_PROXY, value, sizeof(value)); if (ret >= 0) { - if (audio_extn_usb_is_proxy_inuse() || - !adev->allow_afe_proxy_usage) - val = 0; - else - val = 1; + val = (adev->allow_afe_proxy_usage ? 1: 0); str_parms_add_int(reply, AUDIO_PARAMETER_CAN_OPEN_PROXY, val); } ALOGV("%s: called ... can_use_proxy %d", __func__, val); diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h index 3ce83b19..0ef17838 100644 --- a/hal/audio_extn/audio_extn.h +++ b/hal/audio_extn/audio_extn.h @@ -151,24 +151,19 @@ int32_t audio_extn_get_afe_proxy_channel_count(); #endif #ifndef USB_HEADSET_ENABLED -#define audio_extn_usb_init(adev) (0) -#define audio_extn_usb_deinit() (0) -#define audio_extn_usb_start_playback(adev) (0) -#define audio_extn_usb_stop_playback() (0) -#define audio_extn_usb_start_capture(adev) (0) -#define audio_extn_usb_stop_capture() (0) -#define audio_extn_usb_set_proxy_sound_card(sndcard_idx) (0) -#define audio_extn_usb_is_proxy_inuse() (0) +#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) (0) #else -void initPlaybackVolume(); void audio_extn_usb_init(void *adev); void audio_extn_usb_deinit(); -void audio_extn_usb_start_playback(void *adev); -void audio_extn_usb_stop_playback(); -void audio_extn_usb_start_capture(void *adev); -void audio_extn_usb_stop_capture(); -void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx); -bool audio_extn_usb_is_proxy_inuse(); +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); #endif #ifndef SSR_ENABLED diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c index 13e3138b..141ef377 100644 --- a/hal/audio_extn/usb.c +++ b/hal/audio_extn/usb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. * Not a Contribution. * * Copyright (C) 2013 The Android Open Source Project @@ -32,148 +32,181 @@ #include <system/audio.h> #include <tinyalsa/asoundlib.h> +#include <audio_hw.h> +#include <cutils/properties.h> #ifdef USB_HEADSET_ENABLED -#define USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE 512 -#define USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT 8 -#define USB_DEFAULT_OUTPUT_SAMPLING_RATE 48000 - -#define USB_PROXY_DEFAULT_SAMPLING_RATE 48000 -#define USB_PROXY_OPEN_RETRY_COUNT 100 -#define USB_PROXY_OPEN_WAIT_TIME 20 -#define USB_PROXY_PERIOD_SIZE 3072 -#define USB_PROXY_RATE_8000 8000 -#define USB_PROXY_RATE_16000 16000 -#define USB_PROXY_RATE_48000 48000 -#define USB_PERIOD_SIZE 2048 -#define USB_BUFF_SIZE 2048 -#define AFE_PROXY_PERIOD_COUNT 32 -#define AFE_PROXY_PLAYBACK_DEVICE 8 -#define AFE_PROXY_CAPTURE_DEVICE 7 +#define USB_BUFF_SIZE 2048 +#define CHANNEL_NUMBER_STR "Channels: " +#define PLAYBACK_PROFILE_STR "Playback:" +#define CAPTURE_PROFILE_STR "Capture:" +#define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A))) +#define SAMPLE_RATE_8000 8000 +#define SAMPLE_RATE_11025 11025 + +// Supported sample rates for USB +static uint32_t supported_sample_rates[] = + {44100, 48000, 64000, 88200, 96000, 176400, 192000}; + +#define MAX_SAMPLE_RATE_SIZE sizeof(supported_sample_rates)/sizeof(supported_sample_rates[0]) + +enum usb_usecase_type{ + USB_PLAYBACK = 0, + USB_CAPTURE, +}; + +struct usb_device_config { + struct listnode list; + unsigned int bit_width; + unsigned int channels; + 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 usb_module { - uint32_t usb_card; - uint32_t proxy_card; - uint32_t usb_device_id; - uint32_t proxy_device_id; - - int32_t channels_playback; - int32_t sample_rate_playback; - int32_t channels_record; - int32_t sample_rate_record; - - bool is_playback_running; - bool is_record_running; - - pthread_t usb_playback_thr; - pthread_t usb_record_thr; - pthread_mutex_t usb_playback_lock; - pthread_mutex_t usb_record_lock; - - struct pcm *proxy_pcm_playback_handle; - struct pcm *usb_pcm_playback_handle; - struct pcm *proxy_pcm_record_handle; - struct pcm *usb_pcm_record_handle; + struct listnode usb_card_conf_list; struct audio_device *adev; }; static struct usb_module *usbmod = NULL; -static pthread_once_t alloc_usbmod_once_ctl = PTHREAD_ONCE_INIT; - -struct pcm_config pcm_config_usbmod = { - .channels = 2, - .rate = USB_DEFAULT_OUTPUT_SAMPLING_RATE, - .period_size = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE, - .period_count = USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT, - .format = PCM_FORMAT_S16_LE, - .start_threshold = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, - .stop_threshold = INT_MAX, - .avail_min = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, -}; +static bool usb_audio_debug_enable = false; -static void usb_alloc() +static int usb_set_channel_mixer_ctl(int channel, + char *ch_mixer_ctl_name) { - usbmod = calloc(1, sizeof(struct usb_module)); -} - -// Some USB audio accessories have a really low default volume set. Look for a suitable -// volume control and set the volume to default volume level. -static void initPlaybackVolume() { - ALOGD("initPlaybackVolume"); - struct mixer *usbMixer = mixer_open(1); - - if (usbMixer) { - struct mixer_ctl *ctl = NULL; - unsigned int usbPlaybackVolume; - unsigned int i; - unsigned int num_ctls = mixer_get_num_ctls(usbMixer); - - // Look for the first control named ".*Playback Volume" that isn't for a microphone - for (i = 0; i < num_ctls; i++) { - ctl = mixer_get_ctl(usbMixer, i); - if ((ctl) && (strstr((const char *)mixer_ctl_get_name(ctl), "Playback Volume") && - !strstr((const char *)mixer_ctl_get_name(ctl), "Mic"))) { - break; - } - } - if (ctl != NULL) { - ALOGD("Found a volume control for USB: %s", mixer_ctl_get_name(ctl) ); - usbPlaybackVolume = mixer_ctl_get_value(ctl, 0); - ALOGD("Value got from mixer_ctl_get is:%u", usbPlaybackVolume); - if (mixer_ctl_set_value(ctl,0,usbPlaybackVolume) < 0) { - ALOGE("Failed to set volume; default volume might be used"); - } - } else { - ALOGE("No playback volume control found; default volume will be used"); - } - mixer_close(usbMixer); - } else { - ALOGE("Failed to open mixer for card 1"); + struct mixer_ctl *ctl; + + ctl = mixer_get_ctl_by_name(usbmod->adev->mixer, ch_mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, ch_mixer_ctl_name); + return -EINVAL; + } + switch (channel) { + case 1: + mixer_ctl_set_enum_by_string(ctl, "One"); + break; + case 2: + mixer_ctl_set_enum_by_string(ctl, "Two"); + break; + default: + ALOGV("%s: channel(%d) not supported, set as default 2 channels", + __func__, channel); + mixer_ctl_set_enum_by_string(ctl, "Two"); + break; } + return 0; } -static int usb_get_numof_rates(char *rates_str) +static int usb_set_dev_id_mixer_ctl(unsigned int usb_usecase_type, int card, + char *dev_mixer_ctl_name) { - int i, size = 0; - char *next_sr_string, *temp_ptr; - next_sr_string = strtok_r(rates_str, " ,", &temp_ptr); + struct mixer_ctl *ctl; + unsigned int dev_token; + 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); - if (next_sr_string == NULL) { - ALOGE("%s: get_numof_rates: could not find rates string", __func__); - return 0; + 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); - for (i = 1; next_sr_string != NULL; i++) { - size ++; + 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); + if (strstr(rates_str, "continuous") != NULL) { + next_sr_string = strtok_r(rates_str, " ,", &temp_ptr); + if (next_sr_string == NULL) { + ALOGE("%s: could not find min rates string", __func__); + return -EINVAL; + } + min_sr = 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 = 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]; + } else { + next_sr_string = strtok_r(rates_str, " ,", &temp_ptr); + if (next_sr_string == NULL) { + ALOGE("%s: get_numof_rates: could not find rates string", __func__); + return -EINVAL; + } + 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); } - return size; + config->rate_size = sr_size; + return 0; } -static int usb_get_capability(char *type, int32_t *channels, - int32_t *sample_rate) +static int usb_get_capability(int type, + struct usb_card_config *usb_card_info, + int card) { - ALOGD("%s: for %s", __func__, type); - long unsigned file_size; - FILE *fp; - char *buffer; int32_t err = 1; int32_t size = 0; - int32_t fd=-1, i, channels_playback; - char *str_start, *channel_start, *rates_str_start, *next_sr_str, - *next_sr_string, *temp_ptr; - struct stat st; + int32_t fd=-1; + int32_t altset_index = 1; + int32_t channels_no; + char *str_start, *channel_start, *bit_width_start, *rates_str_start, + *target; char *read_buf = NULL; char *rates_str = NULL; - char *rates_str_for_val = NULL; - int *rates_supported = NULL; - char path[128]; + char path[128], altset[9]; int ret = 0; + char *bit_width_str = NULL; + struct usb_device_config * usb_device_info; + + ALOGV("%s: for %s", __func__, (type == USB_PLAYBACK) ? + PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR); - memset(&st, 0x0, sizeof(struct stat)); - *sample_rate = 0; snprintf(path, sizeof(path), "/proc/asound/card%u/stream0", - usbmod->usb_card); + card); fd = open(path, O_RDONLY); if (fd <0) { @@ -183,15 +216,6 @@ static int usb_get_capability(char *type, int32_t *channels, goto done; } - if (fstat(fd, &st) < 0) { - ALOGE("%s: error failed to stat %s error %d\n", - __func__, path, errno); - ret = -EINVAL; - goto done; - } - - file_size = st.st_size; - read_buf = (char *)calloc(1, USB_BUFF_SIZE + 1); if (!read_buf) { @@ -201,530 +225,600 @@ static int usb_get_capability(char *type, int32_t *channels, } err = read(fd, read_buf, USB_BUFF_SIZE); - str_start = strstr(read_buf, type); + 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); - ret = -EINVAL; - goto done; - } - - channel_start = strstr(str_start, "Channels:"); - if (channel_start == NULL) { - ALOGE("%s: error could not find Channels information", __func__); - ret = -EINVAL; - goto done; - } - - channel_start = strstr(channel_start, " "); - if (channel_start == NULL) { - ALOGE("%s: error channel section not found in usb config file", - __func__); - ret = -EINVAL; - goto done; - } - - channels_playback = atoi(channel_start); - if (channels_playback == 1) { - *channels = 1; - } else { - *channels = 2; - } - - ALOGD("%s: channels supported by device: %d", __func__, *channels); - rates_str_start = strstr(str_start, "Rates:"); - if (rates_str_start == NULL) { - ALOGE("%s: error cant find rates information", __func__); - ret = -EINVAL; - goto done; - } - - rates_str_start = strstr(rates_str_start, " "); - if (rates_str_start == NULL) { - ALOGE("%s: error channel section not found in usb config file", - __func__); - ret = -EINVAL; - goto done; - } - - char *target = strchr(rates_str_start, '\n'); - if (target == NULL) { - ALOGE("%s: error end of line not found", __func__); - ret = -EINVAL; - goto done; - } - - size = target - rates_str_start; - if ((rates_str = (char *)malloc(size + 1)) == NULL) { - ALOGE("%s: error unable to allocate memory to hold sample rate strings", - __func__); - ret = -EINVAL; - goto done; - } - - if ((rates_str_for_val = (char *)malloc(size + 1)) == NULL) { - ALOGE("%s: error unable to allocate memory to hold sample rate string", - __func__); - ret = -EINVAL; - goto done; - } - - memcpy(rates_str, rates_str_start, size); - memcpy(rates_str_for_val, rates_str_start, size); - rates_str[size] = '\0'; - rates_str_for_val[size] = '\0'; - - size = usb_get_numof_rates(rates_str); - if (!size) { - ALOGE("%s: error could not get rate size, returning", __func__); + __func__, ((type == USB_PLAYBACK) ? + PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR)); ret = -EINVAL; goto done; } - - rates_supported = (int *)malloc(sizeof(int) * size); - - if (!rates_supported) { - ALOGE("couldn't allocate mem for rates_supported"); - ret = -EINVAL; - goto done; - } - - next_sr_string = strtok_r(rates_str_for_val, " ,", &temp_ptr); - if (next_sr_string == NULL) { - ALOGE("%s: error could not get first rate val", __func__); - ret = -EINVAL; - goto done; - } - - rates_supported[0] = atoi(next_sr_string); - ALOGD("%s: rates_supported[0] for playback: %d", - __func__, rates_supported[0]); - for (i = 1; i<size; i++) { - next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr); - if (next_sr_string == NULL) { - rates_supported[i] = -1; // fill in an invalid sr for the rest + ALOGV("%s: usb_config = %s\n", __func__, str_start); + + while (str_start != NULL) { + sprintf(altset, "Altset %d", altset_index); + ALOGV("%s: altset_index %d\n", __func__, altset_index); + str_start = strstr(str_start, altset); + if (str_start == NULL) { + if (altset_index == 1) { + ALOGE("%s: error %s section not found in usb config file", + __func__, (type == USB_PLAYBACK) ? + PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR); + ret = -EINVAL; + } + break; + } + 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; + } + altset_index++; + /* 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; } - rates_supported[i] = atoi(next_sr_string); - ALOGD("rates_supported[%d] for playback: %d",i, rates_supported[i]); - } - - for (i = 0; i<size; i++) { - if ((rates_supported[i] > *sample_rate) && - (rates_supported[i] <= 48000)) { - /* Sample Rate should be one of the proxy supported rates only - This is because proxy port is used to read from/write to DSP */ - if ((rates_supported[i] == USB_PROXY_RATE_8000) || - (rates_supported[i] == USB_PROXY_RATE_16000) || - (rates_supported[i] == USB_PROXY_RATE_48000)) { - *sample_rate = rates_supported[i]; - } + 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->channels = 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) { + ALOGI("%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); } - ALOGD("%s: sample_rate: %d", __func__, *sample_rate); done: if (fd >= 0) close(fd); - if (rates_str_for_val) free(rates_str_for_val); - if (rates_str) free(rates_str); - if (rates_supported) free(rates_supported); if (read_buf) free(read_buf); return ret; } -static int32_t usb_playback_entry(void *adev) +static int usb_get_device_pb_config(struct usb_card_config *usb_card_info, + int card) { - unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; - int32_t ret, bytes, proxy_open_retry_count; - - ALOGD("%s: entry", __func__); - /* update audio device pointer */ - usbmod->adev = (struct audio_device*)adev; - proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; + int ret; + struct listnode *node_d; + struct usb_device_config *dev_info; /* get capabilities */ - pthread_mutex_lock(&usbmod->usb_playback_lock); - ret = usb_get_capability((char *)"Playback:", - &usbmod->channels_playback, &usbmod->sample_rate_playback); - if (ret) { - ALOGE("%s: could not get playback capabilities from usb device", + if ((ret = usb_get_capability(USB_PLAYBACK, usb_card_info, card))) { + ALOGE("%s: could not get Playback capabilities from usb device", __func__); - pthread_mutex_unlock(&usbmod->usb_playback_lock); - return -EINVAL; + goto exit; } - /* update config for usb - 1 pcm frame(sample)= 4 bytes since two channels*/ - pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; - pcm_config_usbmod.channels = usbmod->channels_playback; - pcm_config_usbmod.rate = usbmod->sample_rate_playback; - ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, - pcm_config_usbmod.period_size, pcm_config_usbmod.channels, - pcm_config_usbmod.rate); - - usbmod->usb_pcm_playback_handle = pcm_open(usbmod->usb_card, \ - usbmod->usb_device_id, PCM_OUT | - PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); - - if ((usbmod->usb_pcm_playback_handle \ - && !pcm_is_ready(usbmod->usb_pcm_playback_handle)) - || (!usbmod->is_playback_running)) { - ALOGE("%s: failed: %s", __func__, - pcm_get_error(usbmod->usb_pcm_playback_handle)); - pcm_close(usbmod->usb_pcm_playback_handle); - usbmod->usb_pcm_playback_handle = NULL; - pthread_mutex_unlock(&usbmod->usb_playback_lock); - return -ENOMEM; - } - ALOGD("%s: USB configured for playback", __func__); - - /* update config for proxy*/ - pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/3; - pcm_config_usbmod.rate = usbmod->sample_rate_playback; - pcm_config_usbmod.channels = usbmod->channels_playback; - pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT; - usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; - ALOGD("%s: proxy device %u:period %u:channels %u:sample", __func__, - pcm_config_usbmod.period_size, pcm_config_usbmod.channels, - pcm_config_usbmod.rate); - - while(proxy_open_retry_count){ - usbmod->proxy_pcm_playback_handle = pcm_open(usbmod->proxy_card, - usbmod->proxy_device_id, PCM_IN | - PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); - if(usbmod->proxy_pcm_playback_handle - && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)){ - pcm_close(usbmod->proxy_pcm_playback_handle); - proxy_open_retry_count--; - usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); - ALOGE("%s: pcm_open for proxy failed retrying = %d", - __func__, proxy_open_retry_count); - } - else{ - break; - } - } - - if ((usbmod->proxy_pcm_playback_handle - && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)) - || (!usbmod->is_playback_running)) { - ALOGE("%s: failed: %s", __func__, - pcm_get_error(usbmod->proxy_pcm_playback_handle)); - pcm_close(usbmod->proxy_pcm_playback_handle); - usbmod->proxy_pcm_playback_handle = NULL; - pthread_mutex_unlock(&usbmod->usb_playback_lock); - return -ENOMEM; - } - ALOGD("%s: PROXY configured for playback", __func__); - pthread_mutex_unlock(&usbmod->usb_playback_lock); - - ALOGD("Init USB volume"); - initPlaybackVolume(); - /* main loop to read from proxy and write to usb */ - while (usbmod->is_playback_running) { - /* read data from proxy */ - ret = pcm_mmap_read(usbmod->proxy_pcm_playback_handle, - (void *)usbbuf, USB_PROXY_PERIOD_SIZE); - /* Write to usb */ - ret = pcm_mmap_write(usbmod->usb_pcm_playback_handle, - (void *)usbbuf, USB_PROXY_PERIOD_SIZE); - if(!usbmod->is_playback_running) + /* Currently only use the first profile using to configure channel for simplification */ + list_for_each(node_d, &usb_card_info->usb_device_conf_list) { + dev_info = node_to_item(node_d, struct usb_device_config, list); + if (dev_info != NULL) { + usb_set_channel_mixer_ctl(dev_info->channels, "USB_AUDIO_RX Channels"); break; + } + } + usb_set_dev_id_mixer_ctl(USB_PLAYBACK, card, "USB_AUDIO_RX dev_token"); - memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); - } /* main loop end */ - - ALOGD("%s: exiting USB playback thread",__func__); - return 0; -} - -static void* usb_playback_launcher(void *adev) -{ - int32_t ret; - - usbmod->is_playback_running = true; - ret = usb_playback_entry(adev); +exit: - if (ret) { - ALOGE("%s: failed with err:%d", __func__, ret); - usbmod->is_playback_running = false; - } - return NULL; + return ret; } -static int32_t usb_record_entry(void *adev) +static int usb_get_device_cap_config(struct usb_card_config *usb_card_info, + int card) { - unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; - int32_t ret, bytes, proxy_open_retry_count; - ALOGD("%s: entry", __func__); - - /* update audio device pointer */ - usbmod->adev = (struct audio_device*)adev; - proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; + int ret; + struct listnode *node_d; + struct usb_device_config *dev_info; /* get capabilities */ - pthread_mutex_lock(&usbmod->usb_record_lock); - ret = usb_get_capability((char *)"Capture:", - &usbmod->channels_record, &usbmod->sample_rate_record); - if (ret) { - ALOGE("%s: could not get capture capabilities from usb device", + if ((ret = usb_get_capability(USB_CAPTURE, usb_card_info, card))) { + ALOGE("%s: could not get Playback capabilities from usb device", __func__); - pthread_mutex_unlock(&usbmod->usb_record_lock); - return -EINVAL; - } - /* update config for usb - 1 pcm frame(sample)= 4 bytes since two channels*/ - pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; - pcm_config_usbmod.channels = usbmod->channels_record; - pcm_config_usbmod.rate = usbmod->sample_rate_record; - ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, - pcm_config_usbmod.period_size, pcm_config_usbmod.channels, - pcm_config_usbmod.rate); - - usbmod->usb_pcm_record_handle = pcm_open(usbmod->usb_card, \ - usbmod->usb_device_id, PCM_IN | - PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); - - if ((usbmod->usb_pcm_record_handle \ - && !pcm_is_ready(usbmod->usb_pcm_record_handle)) - || (!usbmod->is_record_running)) { - ALOGE("%s: failed: %s", __func__, - pcm_get_error(usbmod->usb_pcm_record_handle)); - pcm_close(usbmod->usb_pcm_record_handle); - usbmod->usb_pcm_record_handle = NULL; - pthread_mutex_unlock(&usbmod->usb_record_lock); - return -ENOMEM; - } - ALOGD("%s: USB configured for capture", __func__); - - /* update config for proxy*/ - pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/4; - pcm_config_usbmod.rate = usbmod->sample_rate_record; - pcm_config_usbmod.channels = usbmod->channels_record; - pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT * 2; - usbmod->proxy_device_id = AFE_PROXY_CAPTURE_DEVICE; - ALOGV("%s: proxy device %u:period %u:channels %u:sample", __func__, - pcm_config_usbmod.period_size, pcm_config_usbmod.channels, - pcm_config_usbmod.rate); - - while(proxy_open_retry_count){ - usbmod->proxy_pcm_record_handle = pcm_open(usbmod->proxy_card, - usbmod->proxy_device_id, PCM_OUT | - PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); - if(usbmod->proxy_pcm_record_handle - && !pcm_is_ready(usbmod->proxy_pcm_record_handle)){ - pcm_close(usbmod->proxy_pcm_record_handle); - proxy_open_retry_count--; - usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); - ALOGE("%s: pcm_open for proxy(recording) failed retrying = %d", - __func__, proxy_open_retry_count); - } - else{ - break; - } + goto exit; } - if ((usbmod->proxy_pcm_record_handle - && !pcm_is_ready(usbmod->proxy_pcm_record_handle)) - || (!usbmod->is_record_running)) { - ALOGE("%s: failed: %s", __func__, - pcm_get_error(usbmod->proxy_pcm_record_handle)); - pcm_close(usbmod->proxy_pcm_record_handle); - usbmod->proxy_pcm_record_handle = NULL; - pthread_mutex_unlock(&usbmod->usb_record_lock); - return -ENOMEM; - } - ALOGD("%s: PROXY configured for capture", __func__); - pthread_mutex_unlock(&usbmod->usb_record_lock); - - /* main loop to read from usb and write to proxy */ - while (usbmod->is_record_running) { - /* read data from usb */ - ret = pcm_mmap_read(usbmod->usb_pcm_record_handle, - (void *)usbbuf, USB_PROXY_PERIOD_SIZE); - /* Write to proxy */ - ret = pcm_mmap_write(usbmod->proxy_pcm_record_handle, - (void *)usbbuf, USB_PROXY_PERIOD_SIZE); - if(!usbmod->is_record_running) + /* Currently only use the first profile using to configure channel for simplification */ + list_for_each(node_d, &usb_card_info->usb_device_conf_list) { + dev_info = node_to_item(node_d, struct usb_device_config, list); + if (dev_info != NULL) { + usb_set_channel_mixer_ctl(dev_info->channels, "USB_AUDIO_TX Channels"); break; + } + } + usb_set_dev_id_mixer_ctl(USB_CAPTURE, card, "USB_AUDIO_TX dev_token"); - memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); - } /* main loop end */ - - ALOGD("%s: exiting USB capture thread",__func__); - return 0; +exit: + return ret; } -static void* usb_capture_launcher(void *adev) +static bool usb_valid_device(int device) { - int32_t ret; - - usbmod->is_record_running = true; - ret = usb_record_entry(adev); + bool is_usb_device = false; + if ((device & AUDIO_DEVICE_OUT_USB_DEVICE) || + (device & AUDIO_DEVICE_IN_USB_DEVICE)) + is_usb_device = true; + return is_usb_device; +} - if (ret) { - ALOGE("%s: failed with err:%d", __func__, ret); - usbmod->is_record_running = 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->channels); + for (i = 0; i < dev_info->rate_size; i++) + ALOGI("%s: rate %d", __func__, dev_info->rates[i]); + } } - return NULL; } -void audio_extn_usb_init(void *adev) +static bool usb_get_best_match_for_bit_width( + struct listnode *dev_list, + unsigned int stream_bit_width, + unsigned int *bit_width) { - pthread_once(&alloc_usbmod_once_ctl, usb_alloc); - - usbmod->is_playback_running = false; - usbmod->is_record_running = false; - - usbmod->usb_pcm_playback_handle = NULL; - usbmod->proxy_pcm_playback_handle = NULL; - - usbmod->usb_pcm_record_handle = NULL; - usbmod->proxy_pcm_record_handle = NULL; + 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; +} - usbmod->usb_card = 1; - usbmod->usb_device_id = 0; - usbmod->proxy_card = 0; - usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; - usbmod->adev = (struct audio_device*)adev; +static bool usb_get_best_match_for_channels( + struct listnode *dev_list, + unsigned int bit_width, + unsigned int stream_ch, + unsigned int *ch) +{ + 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->channels, dev_info->bit_width, + stream_ch, bit_width, candidate); + if (dev_info->bit_width != bit_width) + continue; + if (dev_info->channels== stream_ch) { + *ch = dev_info->channels; + ALOGV("%s: Found match channels (%d)", + __func__, dev_info->channels); + goto exit; + } else if (candidate == 0) + candidate = dev_info->channels; + /* + * 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->channels) < + ABS_SUB(stream_ch, candidate)) { + candidate = dev_info->channels; + } else if ((ABS_SUB(stream_ch, dev_info->channels) == + ABS_SUB(stream_ch, candidate)) && + (dev_info->channels > candidate)) { + candidate = dev_info->channels; + } + } + ALOGV("%s: No match found, use the best candidate ch(%d)", + __func__, candidate); + *ch = candidate; +exit: + return true; - pthread_mutex_init(&usbmod->usb_playback_lock, - (const pthread_mutexattr_t *) NULL); - pthread_mutex_init(&usbmod->usb_record_lock, - (const pthread_mutexattr_t *) NULL); } -void audio_extn_usb_deinit() +static bool usb_sample_rate_multiple( + unsigned int stream_sample_rate, + unsigned int base) { - if (NULL != usbmod){ - free(usbmod); - usbmod = NULL; - } + return (((stream_sample_rate / base) * base) == stream_sample_rate); } -void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx) +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) { - /* Proxy port and USB headset are related to two different sound cards */ - if (sndcard_idx == usbmod->usb_card) { - usbmod->usb_card = usbmod->proxy_card; + /* 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; + } } - - usbmod->proxy_card = sndcard_idx; + return true; } -void audio_extn_usb_start_playback(void *adev) +static bool usb_get_best_match_for_sample_rate( + struct listnode *dev_list, + unsigned int bit_width, + unsigned int ch, + unsigned int stream_sample_rate, + unsigned int *sr) { - int32_t ret; + 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__, ch, 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->channels, dev_info->bit_width, + ch, bit_width, stream_sample_rate, candidate); + if ((dev_info->bit_width != bit_width) && dev_info->channels != ch) + continue; - if (NULL == usbmod){ - ALOGE("%s: USB device object is NULL", __func__); - return; + 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->channels, + dev_info->bit_width, dev_info->rates[i], + ch, 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; +} - if (usbmod->is_playback_running){ - ALOGE("%s: USB playback thread already running", __func__); - return; - } - ALOGD("%s: creating USB playback thread", __func__); - ret = pthread_create(&usbmod->usb_playback_thr, NULL, - usb_playback_launcher, (void*)adev); - if (ret) - ALOGE("%s: failed to create USB playback thread with err:%d", - __func__, ret); +static bool usb_audio_backend_apply_policy(struct listnode *dev_list, + unsigned int *bit_width, + unsigned int *sample_rate, + unsigned int ch) +{ + unsigned int channel; + bool is_usb_supported = true; + + ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) channels (%d)", + __func__, *bit_width, *sample_rate, ch); + if (list_empty(dev_list)) { + *sample_rate = 48000; + *bit_width = 16; + channel = 2; + ALOGI("%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, + ch, + &channel); + usb_get_best_match_for_sample_rate(dev_list, + *bit_width, + channel, + *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); + usb_set_channel_mixer_ctl(channel, "USB_AUDIO_RX Channels"); + return is_usb_supported; } -void audio_extn_usb_stop_playback() +bool audio_extn_usb_is_config_supported(unsigned int *bit_width, + unsigned int *sample_rate, + unsigned int ch) { - int32_t ret; - ALOGD("%s: entry", __func__); - - usbmod->is_playback_running = false; - if (NULL != usbmod->proxy_pcm_playback_handle) - pcm_stop(usbmod->proxy_pcm_playback_handle); - - if (NULL != usbmod->usb_pcm_playback_handle) - pcm_stop(usbmod->usb_pcm_playback_handle); - - if(usbmod->usb_playback_thr) { - ret = pthread_join(usbmod->usb_playback_thr,NULL); - ALOGE("%s: return for pthread_join = %d", __func__, ret); - usbmod->usb_playback_thr = (pthread_t)NULL; - } - - pthread_mutex_lock(&usbmod->usb_playback_lock); - if (NULL != usbmod->usb_pcm_playback_handle){ - pcm_close(usbmod->usb_pcm_playback_handle); - usbmod->usb_pcm_playback_handle = NULL; - } - - if (NULL != usbmod->proxy_pcm_playback_handle){ - pcm_close(usbmod->proxy_pcm_playback_handle); - usbmod->proxy_pcm_playback_handle = NULL; + struct listnode *node_i; + struct usb_card_config *card_info; + bool is_usb_supported = false; + + ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) ch(%d)", + __func__, *bit_width, *sample_rate, ch); + 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 (card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) { + is_usb_supported = usb_audio_backend_apply_policy( + &card_info->usb_device_conf_list, + bit_width, + sample_rate, + ch); + break; + } } - pthread_mutex_unlock(&usbmod->usb_playback_lock); + ALOGV("%s: updated: bit-width(%d) sample_rate(%d)", + __func__, *bit_width, *sample_rate); - ALOGD("%s: exiting",__func__); + return is_usb_supported; } -void audio_extn_usb_start_capture(void *adev) +void audio_extn_usb_add_device(audio_devices_t device, int card) { - int32_t ret; + struct usb_card_config *usb_card_info; + char check_debug_enable[PROPERTY_VALUE_MAX]; - if (NULL == usbmod){ + 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__); - return; + goto exit; } - if (usbmod->is_record_running){ - ALOGE("%s: USB capture thread already running", __func__); - return; + if (!(usb_valid_device(device)) || (card < 0)) { + ALOGE("%s:device(0x%x), card(%d)", + __func__, device, card); + goto exit; } - ALOGD("%s: creating USB capture thread", __func__); - ret = pthread_create(&usbmod->usb_record_thr, NULL, - usb_capture_launcher, (void*)adev); - if (ret) - ALOGE("%s: failed to create USB capture thread with err:%d", - __func__, ret); + 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_pb_config(usb_card_info, card)){ + usb_card_info->usb_card = card; + usb_card_info->usb_device_type = AUDIO_DEVICE_OUT_USB_DEVICE; + 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_cap_config(usb_card_info, card)) { + usb_card_info->usb_card = card; + usb_card_info->usb_device_type = AUDIO_DEVICE_IN_USB_DEVICE; + 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_stop_capture() +void audio_extn_usb_remove_device(audio_devices_t device, int card) { - int32_t ret; - ALOGD("%s: entry", __func__); + 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; - usbmod->is_record_running = false; - if (NULL != usbmod->proxy_pcm_record_handle) - pcm_stop(usbmod->proxy_pcm_record_handle); + ALOGV("%s: device(0x%x), card(%d)", + __func__, device, card); - if (NULL != usbmod->usb_pcm_record_handle) - pcm_stop(usbmod->usb_pcm_record_handle); - - if(usbmod->usb_record_thr) { - ret = pthread_join(usbmod->usb_record_thr,NULL); - ALOGE("%s: return for pthread_join = %d", __func__, ret); - usbmod->usb_record_thr = (pthread_t)NULL; + 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->channels); + 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)); + } } +exit: + if (usb_audio_debug_enable) + usb_print_active_device(); - pthread_mutex_lock(&usbmod->usb_record_lock); - if (NULL != usbmod->usb_pcm_record_handle){ - pcm_close(usbmod->usb_pcm_record_handle); - usbmod->usb_pcm_record_handle = NULL; - } + return; +} - if (NULL != usbmod->proxy_pcm_record_handle){ - pcm_close(usbmod->proxy_pcm_record_handle); - usbmod->proxy_pcm_record_handle = NULL; +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; + } } - pthread_mutex_unlock(&usbmod->usb_record_lock); - - ALOGD("%s: exiting",__func__); + list_init(&usbmod->usb_card_conf_list); + usbmod->adev = (struct audio_device*)adev; +exit: + return; } -bool audio_extn_usb_is_proxy_inuse() +void audio_extn_usb_deinit(void) { - if( usbmod->is_record_running || usbmod->is_playback_running) - return true; - else - return false; + 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 aabb656f..55b0cc75 100644 --- a/hal/audio_hw.c +++ b/hal/audio_hw.c @@ -537,14 +537,7 @@ int enable_snd_device(struct audio_device *adev, if (audio_extn_spkr_prot_is_enabled()) audio_extn_spkr_prot_calib_cancel(adev); - /* start usb playback thread */ - if(SND_DEVICE_OUT_USB_HEADSET == snd_device || - SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device) - audio_extn_usb_start_playback(adev); - /* start usb capture thread */ - if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device) - audio_extn_usb_start_capture(adev); if (platform_can_enable_spkr_prot_on_device(snd_device) && audio_extn_spkr_prot_is_enabled()) { @@ -553,7 +546,7 @@ int enable_snd_device(struct audio_device *adev, return -EINVAL; } audio_extn_dev_arbi_acquire(snd_device); - if (audio_extn_spkr_prot_start_processing(snd_device)) { + if (audio_extn_spkr_prot_start_processing(snd_device)) { ALOGE("%s: spkr_start_processing failed", __func__); audio_extn_dev_arbi_release(snd_device); return -EINVAL; @@ -621,15 +614,6 @@ int disable_snd_device(struct audio_device *adev, if (adev->snd_dev_ref_cnt[snd_device] == 0) { ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name); - /* exit usb play back thread */ - if(SND_DEVICE_OUT_USB_HEADSET == snd_device || - SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device) - audio_extn_usb_stop_playback(); - - /* exit usb capture thread */ - if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device) - audio_extn_usb_stop_capture(); - if (platform_can_enable_spkr_prot_on_device(snd_device) && audio_extn_spkr_prot_is_enabled()) { audio_extn_spkr_prot_stop_processing(snd_device); @@ -1157,8 +1141,7 @@ int select_devices(struct audio_device *adev, audio_usecase_t uc_id) /* Enable new sound devices */ if (out_snd_device != SND_DEVICE_NONE) { - if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) - check_usecases_codec_backend(adev, usecase, out_snd_device); + check_usecases_codec_backend(adev, usecase, out_snd_device); enable_snd_device(adev, out_snd_device); } @@ -3819,7 +3802,8 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) { ALOGV("cache new edid"); platform_cache_edid(adev->platform); - } else if (val & AUDIO_DEVICE_OUT_USB_DEVICE) { + } else if ((val & AUDIO_DEVICE_OUT_USB_DEVICE) || + (val & AUDIO_DEVICE_IN_USB_DEVICE)) { /* * Do not allow AFE proxy port usage by WFD source when USB headset is connected. * Per AudioPolicyManager, USB device is higher priority than WFD. @@ -3827,6 +3811,10 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) * If WFD use case occupies AFE proxy, it may result unintended behavior while * starting voice call on USB */ + ret = str_parms_get_str(parms, "card", value, sizeof(value)); + if (ret >= 0) { + audio_extn_usb_add_device(val, atoi(value)); + } ALOGV("detected USB connect .. disable proxy"); adev->allow_afe_proxy_usage = false; } @@ -3838,7 +3826,12 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) { ALOGV("invalidate cached edid"); platform_invalidate_edid(adev->platform); - } else if (val & AUDIO_DEVICE_OUT_USB_DEVICE) { + } else if ((val & AUDIO_DEVICE_OUT_USB_DEVICE) || + (val & AUDIO_DEVICE_IN_USB_DEVICE)) { + ret = str_parms_get_str(parms, "card", value, sizeof(value)); + if (ret >= 0) { + audio_extn_usb_remove_device(val, atoi(value)); + } ALOGV("detected USB disconnect .. enable proxy"); adev->allow_afe_proxy_usage = true; } diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c index b39b6687..0703b813 100644 --- a/hal/msm8916/platform.c +++ b/hal/msm8916/platform.c @@ -1837,8 +1837,6 @@ acdb_init_fail: /* init usb */ audio_extn_usb_init(adev); - /* update sound cards appropriately */ - audio_extn_usb_set_proxy_sound_card(adev->snd_card); /* Read one time ssr property */ audio_extn_ssr_update_enabled(); @@ -1899,6 +1897,11 @@ acdb_init_fail: strdup("MI2S_TX SampleRate"); } + my_data->current_backend_cfg[USB_AUDIO_RX_BACKEND].bitwidth_mixer_ctl = + strdup("USB_AUDIO_RX Format"); + my_data->current_backend_cfg[USB_AUDIO_RX_BACKEND].samplerate_mixer_ctl = + strdup("USB_AUDIO_RX SampleRate"); + ret = audio_extn_utils_get_codec_version(snd_card_name, my_data->adev->snd_card, my_data->codec_version); @@ -2403,6 +2406,8 @@ static int platform_get_backend_index(snd_device_t snd_device) 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) + port = USB_AUDIO_RX_BACKEND; } } else { ALOGV("%s:napb: Invalid device - %d ", __func__, snd_device); @@ -2763,6 +2768,12 @@ bool platform_can_split_snd_device(void *platform, new_snd_devices[1] = SND_DEVICE_OUT_HDMI; status = true; + } 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; + status = true; } ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__, @@ -2832,6 +2843,9 @@ snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *o } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | AUDIO_DEVICE_OUT_SPEAKER)) { snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET; + } 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; @@ -2970,6 +2984,8 @@ snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *o ALOGD("%s: setting USB hadset channel capability(2) for Proxy", __func__); audio_extn_set_afe_proxy_channel_mixer(adev, 2); snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_USB_DEVICE) { + snd_device = SND_DEVICE_OUT_USB_HEADSET; } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { @@ -3329,6 +3345,8 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; } else if (in_device & AUDIO_DEVICE_IN_FM_TUNER) { snd_device = SND_DEVICE_IN_CAPTURE_FM; + } 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__); @@ -3364,6 +3382,8 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d } else if (out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_USB_DEVICE) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; } else { ALOGE("%s: Unknown output device(s) %#x", __func__, out_device); ALOGW("%s: Using default handset-mic", __func__); @@ -4021,6 +4041,8 @@ static int platform_set_codec_backend_cfg(struct audio_device* adev, mixer_ctl_set_enum_by_string(ctl, "S24_3LE"); else mixer_ctl_set_enum_by_string(ctl, "S24_LE"); + } else if (bit_width == 32) { + mixer_ctl_set_enum_by_string(ctl, "S24_LE"); } else { mixer_ctl_set_enum_by_string(ctl, "S16_LE"); } @@ -4236,6 +4258,13 @@ static bool platform_check_codec_backend_cfg(struct audio_device* adev, } } + if (backend_idx == USB_AUDIO_RX_BACKEND) { + unsigned int channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask); + audio_extn_usb_is_config_supported(&bit_width, &sample_rate, channels); + ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)", + __func__, bit_width, sample_rate, channels); + } + ALOGI("%s:becf: afe: Codec selected backend: %d updated bit width: %d and sample rate: %d", __func__, backend_idx, bit_width, sample_rate); diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h index aef905dd..7ff9c46f 100644 --- a/hal/msm8916/platform.h +++ b/hal/msm8916/platform.h @@ -211,6 +211,7 @@ enum { HEADPHONE_BACKEND, SLIMBUS_6_RX = HEADPHONE_BACKEND, HDMI_RX_BACKEND, + USB_AUDIO_RX_BACKEND, MAX_CODEC_BACKENDS }; #define AUDIO_PARAMETER_KEY_NATIVE_AUDIO "audio.nat.codec.enabled" diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c index 49e8b5e2..b7d326c2 100644 --- a/hal/msm8974/platform.c +++ b/hal/msm8974/platform.c @@ -1675,8 +1675,6 @@ acdb_init_fail: /* init usb */ audio_extn_usb_init(adev); - /* update sound cards appropriately */ - audio_extn_usb_set_proxy_sound_card(adev->snd_card); /* init dap hal */ audio_extn_dap_hal_init(adev->snd_card); @@ -1738,6 +1736,11 @@ acdb_init_fail: my_data->current_backend_cfg[HEADPHONE_BACKEND].samplerate_mixer_ctl = strdup("SLIM_6_RX SampleRate"); + my_data->current_backend_cfg[USB_AUDIO_RX_BACKEND].bitwidth_mixer_ctl = + strdup("USB_AUDIO_RX Format"); + my_data->current_backend_cfg[USB_AUDIO_RX_BACKEND].samplerate_mixer_ctl = + strdup("USB_AUDIO_RX SampleRate"); + my_data->edid_info = NULL; free(snd_card_name); free(snd_card_name_t); @@ -2213,6 +2216,8 @@ static int platform_get_backend_index(snd_device_t snd_device) 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) + port = USB_AUDIO_RX_BACKEND; } } else { ALOGV("%s:napb: Invalid device - %d ", __func__, snd_device); @@ -2568,6 +2573,12 @@ bool platform_can_split_snd_device(void *platform, new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER; new_snd_devices[1] = SND_DEVICE_OUT_HDMI; status = true; + } 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; + status = true; } ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__, @@ -2625,6 +2636,9 @@ snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *o } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | AUDIO_DEVICE_OUT_SPEAKER)) { snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET; + } 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; @@ -2736,6 +2750,8 @@ snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *o ALOGD("%s: setting USB hadset channel capability(2) for Proxy", __func__); audio_extn_set_afe_proxy_channel_mixer(adev, 2); snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_USB_DEVICE) { + snd_device = SND_DEVICE_OUT_USB_HEADSET; } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { @@ -3070,6 +3086,8 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; } else if (in_device & AUDIO_DEVICE_IN_FM_TUNER) { snd_device = SND_DEVICE_IN_CAPTURE_FM; + } 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__); @@ -3113,6 +3131,8 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d } else if (out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_USB_DEVICE) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; } else { ALOGE("%s: Unknown output device(s) %#x", __func__, out_device); ALOGW("%s: Using default handset-mic", __func__); @@ -3997,6 +4017,8 @@ static int platform_set_codec_backend_cfg(struct audio_device* adev, mixer_ctl_set_enum_by_string(ctl, "S24_3LE"); else mixer_ctl_set_enum_by_string(ctl, "S24_LE"); + } else if (bit_width == 32) { + mixer_ctl_set_enum_by_string(ctl, "S24_LE"); } else { mixer_ctl_set_enum_by_string(ctl, "S16_LE"); } @@ -4178,6 +4200,13 @@ static bool platform_check_codec_backend_cfg(struct audio_device* adev, __func__); } + if (backend_idx == USB_AUDIO_RX_BACKEND) { + unsigned int channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask); + audio_extn_usb_is_config_supported(&bit_width, &sample_rate, channels); + ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)", + __func__, bit_width, sample_rate, channels); + } + ALOGI("%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 diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h index 52f0bac6..eb041096 100644 --- a/hal/msm8974/platform.h +++ b/hal/msm8974/platform.h @@ -201,6 +201,7 @@ enum { HEADPHONE_BACKEND, SLIMBUS_6_RX = HEADPHONE_BACKEND, HDMI_RX_BACKEND, + USB_AUDIO_RX_BACKEND, MAX_CODEC_BACKENDS }; |