| /* |
| * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
| * Not a Contribution. |
| * |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "audio_hw_extn" |
| /*#define LOG_NDEBUG 0*/ |
| #define LOG_NDDEBUG 0 |
| |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <cutils/properties.h> |
| #include <cutils/log.h> |
| |
| #include "audio_hw.h" |
| #include "audio_extn.h" |
| |
| #define MAX_SLEEP_RETRY 100 |
| #define WIFI_INIT_WAIT_SLEEP 50 |
| |
| struct audio_extn_module { |
| bool anc_enabled; |
| bool aanc_enabled; |
| bool custom_stereo_enabled; |
| uint32_t proxy_channel_num; |
| }; |
| |
| static struct audio_extn_module aextnmod = { |
| .anc_enabled = 0, |
| .aanc_enabled = 0, |
| .custom_stereo_enabled = 0, |
| .proxy_channel_num = 2, |
| }; |
| |
| #define AUDIO_PARAMETER_KEY_ANC "anc_enabled" |
| #define AUDIO_PARAMETER_KEY_WFD "wfd_channel_cap" |
| #define AUDIO_PARAMETER_CAN_OPEN_PROXY "can_open_proxy" |
| #define AUDIO_PARAMETER_CUSTOM_STEREO "stereo_as_dual_mono" |
| |
| #ifndef FM_ENABLED |
| #define audio_extn_fm_set_parameters(adev, parms) (0) |
| #else |
| void audio_extn_fm_set_parameters(struct audio_device *adev, |
| struct str_parms *parms); |
| #endif |
| #ifndef HFP_ENABLED |
| void audio_extn_hfp_set_parameters(adev, parms) (0) |
| #else |
| void audio_extn_hfp_set_parameters(struct audio_device *adev, |
| struct str_parms *parms); |
| #endif |
| |
| #ifndef CUSTOM_STEREO_ENABLED |
| #define audio_extn_customstereo_set_parameters(adev, parms) (0) |
| #else |
| void audio_extn_customstereo_set_parameters(struct audio_device *adev, |
| struct str_parms *parms) |
| { |
| int ret = 0; |
| char value[32]={0}; |
| bool custom_stereo_state = false; |
| const char *mixer_ctl_name = "Set Custom Stereo OnOff"; |
| struct mixer_ctl *ctl; |
| |
| ALOGV("%s", __func__); |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_CUSTOM_STEREO, value, |
| sizeof(value)); |
| if (ret >= 0) { |
| if (!strncmp("true", value, sizeof("true")) || atoi(value)) |
| custom_stereo_state = true; |
| |
| if (custom_stereo_state == aextnmod.custom_stereo_enabled) |
| return; |
| |
| ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", |
| __func__, mixer_ctl_name); |
| return; |
| } |
| if (mixer_ctl_set_value(ctl, 0, custom_stereo_state) < 0) { |
| ALOGE("%s: Could not set custom stereo state %d", |
| __func__, custom_stereo_state); |
| return; |
| } |
| aextnmod.custom_stereo_enabled = custom_stereo_state; |
| ALOGV("%s: Setting custom stereo state success", __func__); |
| } |
| } |
| #endif /* CUSTOM_STEREO_ENABLED */ |
| |
| #ifndef ANC_HEADSET_ENABLED |
| #define audio_extn_set_anc_parameters(adev, parms) (0) |
| #else |
| bool audio_extn_get_anc_enabled(void) |
| { |
| ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled); |
| return (aextnmod.anc_enabled ? true: false); |
| } |
| |
| bool audio_extn_should_use_handset_anc(int in_channels) |
| { |
| char prop_aanc[PROPERTY_VALUE_MAX] = "false"; |
| |
| property_get("persist.aanc.enable", prop_aanc, "0"); |
| if (!strncmp("true", prop_aanc, 4)) { |
| ALOGD("%s: AANC enabled in the property", __func__); |
| aextnmod.aanc_enabled = 1; |
| } |
| |
| return (aextnmod.aanc_enabled && aextnmod.anc_enabled |
| && (in_channels == 1)); |
| } |
| |
| bool audio_extn_should_use_fb_anc(void) |
| { |
| char prop_anc[PROPERTY_VALUE_MAX] = "feedforward"; |
| |
| property_get("persist.headset.anc.type", prop_anc, "0"); |
| if (!strncmp("feedback", prop_anc, sizeof("feedback"))) { |
| ALOGD("%s: FB ANC headset type enabled\n", __func__); |
| return true; |
| } |
| return false; |
| } |
| |
| void audio_extn_set_anc_parameters(struct audio_device *adev, |
| struct str_parms *parms) |
| { |
| int ret; |
| char value[32] ={0}; |
| struct listnode *node; |
| struct audio_usecase *usecase; |
| |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ANC, value, |
| sizeof(value)); |
| if (ret >= 0) { |
| if (strcmp(value, "true") == 0) |
| aextnmod.anc_enabled = true; |
| else |
| aextnmod.anc_enabled = false; |
| |
| list_for_each(node, &adev->usecase_list) { |
| usecase = node_to_item(node, struct audio_usecase, list); |
| if (usecase->type == PCM_PLAYBACK) { |
| if (usecase->stream.out->devices == \ |
| AUDIO_DEVICE_OUT_WIRED_HEADPHONE || |
| usecase->stream.out->devices == \ |
| AUDIO_DEVICE_OUT_WIRED_HEADSET) { |
| select_devices(adev, usecase->id); |
| ALOGV("%s: switching device", __func__); |
| break; |
| } |
| } |
| } |
| } |
| |
| ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled); |
| } |
| #endif /* ANC_HEADSET_ENABLED */ |
| |
| #ifndef AFE_PROXY_ENABLED |
| #define audio_extn_set_afe_proxy_parameters(parms) (0) |
| #define audio_extn_get_afe_proxy_parameters(query, reply) (0) |
| #else |
| /* Front left channel. */ |
| #define PCM_CHANNEL_FL 1 |
| |
| /* Front right channel. */ |
| #define PCM_CHANNEL_FR 2 |
| |
| /* Front center channel. */ |
| #define PCM_CHANNEL_FC 3 |
| |
| /* Left surround channel.*/ |
| #define PCM_CHANNEL_LS 4 |
| |
| /* Right surround channel.*/ |
| #define PCM_CHANNEL_RS 5 |
| |
| /* Low frequency effect channel. */ |
| #define PCM_CHANNEL_LFE 6 |
| |
| /* Left back channel; Rear left channel. */ |
| #define PCM_CHANNEL_LB 8 |
| |
| /* Right back channel; Rear right channel. */ |
| #define PCM_CHANNEL_RB 9 |
| |
| static int32_t afe_proxy_set_channel_mapping(struct audio_device *adev, |
| int channel_count) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Playback Channel Map"; |
| int set_values[8] = {0}; |
| int ret; |
| ALOGV("%s channel_count:%d",__func__, channel_count); |
| |
| switch (channel_count) { |
| case 6: |
| set_values[0] = PCM_CHANNEL_FL; |
| set_values[1] = PCM_CHANNEL_FR; |
| set_values[2] = PCM_CHANNEL_FC; |
| set_values[3] = PCM_CHANNEL_LFE; |
| set_values[4] = PCM_CHANNEL_LS; |
| set_values[5] = PCM_CHANNEL_RS; |
| break; |
| case 8: |
| set_values[0] = PCM_CHANNEL_FL; |
| set_values[1] = PCM_CHANNEL_FR; |
| set_values[2] = PCM_CHANNEL_FC; |
| set_values[3] = PCM_CHANNEL_LFE; |
| set_values[4] = PCM_CHANNEL_LS; |
| set_values[5] = PCM_CHANNEL_RS; |
| set_values[6] = PCM_CHANNEL_LB; |
| set_values[7] = PCM_CHANNEL_RB; |
| break; |
| default: |
| ALOGE("unsupported channels(%d) for setting channel map", |
| channel_count); |
| return -EINVAL; |
| } |
| |
| ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", |
| __func__, mixer_ctl_name); |
| return -EINVAL; |
| } |
| ALOGV("AFE: set mapping(%d %d %d %d %d %d %d %d) for channel:%d", |
| set_values[0], set_values[1], set_values[2], set_values[3], set_values[4], |
| set_values[5], set_values[6], set_values[7], channel_count); |
| ret = mixer_ctl_set_array(ctl, set_values, channel_count); |
| return ret; |
| } |
| |
| int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev) |
| { |
| int32_t ret = 0; |
| const char *channel_cnt_str = NULL; |
| struct mixer_ctl *ctl = NULL; |
| const char *mixer_ctl_name = "PROXY_RX Channels"; |
| |
| ALOGD("%s: entry", __func__); |
| /* use the existing channel count set by hardware params to |
| configure the back end for stereo as usb/a2dp would be |
| stereo by default */ |
| ALOGD("%s: channels = %d", __func__, |
| aextnmod.proxy_channel_num); |
| switch (aextnmod.proxy_channel_num) { |
| 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; |
| default: channel_cnt_str = "Two"; break; |
| } |
| |
| if(aextnmod.proxy_channel_num >= 2 && aextnmod.proxy_channel_num < 8) { |
| ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: could not get ctl for mixer cmd - %s", |
| __func__, mixer_ctl_name); |
| return -EINVAL; |
| } |
| } |
| mixer_ctl_set_enum_by_string(ctl, channel_cnt_str); |
| |
| if (aextnmod.proxy_channel_num == 6 || |
| aextnmod.proxy_channel_num == 8) |
| ret = afe_proxy_set_channel_mapping(adev, |
| aextnmod.proxy_channel_num); |
| |
| ALOGD("%s: exit", __func__); |
| return ret; |
| } |
| |
| void audio_extn_set_afe_proxy_parameters(struct str_parms *parms) |
| { |
| int ret, val; |
| char value[32]={0}; |
| |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_WFD, value, |
| sizeof(value)); |
| if (ret >= 0) { |
| val = atoi(value); |
| aextnmod.proxy_channel_num = val; |
| ALOGD("%s: channel capability set to: %d", __func__, |
| aextnmod.proxy_channel_num); |
| } |
| } |
| |
| int audio_extn_get_afe_proxy_parameters(struct str_parms *query, |
| struct str_parms *reply) |
| { |
| int ret, val; |
| char value[32]={0}; |
| char *str = NULL; |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_CAN_OPEN_PROXY, value, |
| sizeof(value)); |
| if (ret >= 0) { |
| if (audio_extn_usb_is_proxy_inuse()) |
| val = 0; |
| else |
| val = 1; |
| str_parms_add_int(reply, AUDIO_PARAMETER_CAN_OPEN_PROXY, val); |
| } |
| |
| return 0; |
| } |
| |
| /* must be called with hw device mutex locked */ |
| int32_t audio_extn_read_afe_proxy_channel_masks(struct stream_out *out) |
| { |
| int ret = 0; |
| int channels = aextnmod.proxy_channel_num; |
| |
| switch (channels) { |
| /* |
| * Do not handle stereo output in Multi-channel cases |
| * Stereo case is handled in normal playback path |
| */ |
| case 6: |
| ALOGV("%s: AFE PROXY supports 5.1", __func__); |
| out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; |
| break; |
| case 8: |
| ALOGV("%s: AFE PROXY supports 5.1 and 7.1 channels", __func__); |
| out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; |
| out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1; |
| break; |
| default: |
| ALOGE("AFE PROXY does not support multi channel playback"); |
| ret = -ENOSYS; |
| break; |
| } |
| return ret; |
| } |
| #endif /* AFE_PROXY_ENABLED */ |
| |
| void audio_extn_set_parameters(struct audio_device *adev, |
| struct str_parms *parms) |
| { |
| audio_extn_set_anc_parameters(adev, parms); |
| audio_extn_set_afe_proxy_parameters(parms); |
| audio_extn_fm_set_parameters(adev, parms); |
| audio_extn_listen_set_parameters(adev, parms); |
| audio_extn_hfp_set_parameters(adev, parms); |
| audio_extn_ddp_set_parameters(adev, parms); |
| audio_extn_customstereo_set_parameters(adev, parms); |
| } |
| |
| void audio_extn_get_parameters(const struct audio_device *adev, |
| struct str_parms *query, |
| struct str_parms *reply) |
| { |
| char *kv_pairs = NULL; |
| audio_extn_get_afe_proxy_parameters(query, reply); |
| |
| kv_pairs = str_parms_to_str(reply); |
| ALOGD_IF(kv_pairs != NULL, "%s: returns %s", __func__, kv_pairs); |
| free(kv_pairs); |
| } |
| |
| #ifdef AUXPCM_BT_ENABLED |
| int32_t audio_extn_read_xml(struct audio_device *adev, uint32_t mixer_card, |
| const char* mixer_xml_path, |
| const char* mixer_xml_path_auxpcm) |
| { |
| char bt_soc[128]; |
| bool wifi_init_complete = false; |
| int sleep_retry = 0; |
| |
| while (!wifi_init_complete && sleep_retry < MAX_SLEEP_RETRY) { |
| property_get("qcom.bluetooth.soc", bt_soc, NULL); |
| if (strncmp(bt_soc, "unknown", sizeof("unknown"))) { |
| wifi_init_complete = true; |
| } else { |
| usleep(WIFI_INIT_WAIT_SLEEP*1000); |
| sleep_retry++; |
| } |
| } |
| |
| if (!strncmp(bt_soc, "ath3k", sizeof("ath3k"))) |
| adev->audio_route = audio_route_init(mixer_card, mixer_xml_path_auxpcm); |
| else |
| adev->audio_route = audio_route_init(mixer_card, mixer_xml_path); |
| |
| return 0; |
| } |
| #endif /* AUXPCM_BT_ENABLED */ |