From: Christopher N. Hesse Date: Sat, 25 Feb 2017 00:37:56 +0000 (+0100) Subject: audio: Add support for Audience EarSmart ICs X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=0fdef0cbd60037a476c3770726091eb8e5a5ce2b;p=GitHub%2FLineageOS%2Fandroid_hardware_samsung.git audio: Add support for Audience EarSmart ICs Some devices (often T-Mobile variants) have extra hardware for incall audio processing. Audio must be routed to the ES IC properly so it can be passed to ALSA, otherwise the RX/TX streams are lost. Change-Id: Ib29c747d5728a09726e14bab00f26ad273400aba --- diff --git a/audio/Android.mk b/audio/Android.mk index d31b1d9..9ececd2 100644 --- a/audio/Android.mk +++ b/audio/Android.mk @@ -21,6 +21,7 @@ include $(CLEAR_VARS) LOCAL_ARM_MODE := arm LOCAL_SRC_FILES := \ + audience.c \ audio_hw.c \ compress_offload.c \ ril_interface.c \ diff --git a/audio/audience.c b/audio/audience.c new file mode 100644 index 0000000..afd7fa2 --- /dev/null +++ b/audio/audience.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2017 Christopher N. Hesse + * + * 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_audience" +#define LOG_NDEBUG 0 + +#include +#include +#include + +#include +#include + +#include "audience.h" + +/* + * Writes an Integer to a file. + * + * @param path The absolute path string. + * @param value The Integer value to be written. + * @return 0 on success, -1 or errno on error. + */ +static int write_int(char const *path, const int value) +{ + int fd; + static int already_warned; + + already_warned = 0; + + ALOGV("write_int: path %s, value %d", path, value); + fd = open(path, O_RDWR); + + if (fd >= 0) { + char buffer[20]; + int bytes = sprintf(buffer, "%d\n", value); + int amt = write(fd, buffer, bytes); + close(fd); + return amt == -1 ? -errno : 0; + } else { + if (already_warned == 0) { + ALOGE("write_int failed to open %s\n", path); + already_warned = 1; + } + return -errno; + } +} + +/* + * Writes the route value to sysfs. + * + * @param value The long Integer value to be written. + * @return 0 on success, -1 or errno on error. + */ +static int es_route_value_set(int value) +{ + return write_int(SYSFS_PATH_PRESET, value); +} + +/* + * Writes the veq control to sysfs. + * + * @param value The Integer value to be written. + * @return 0 on success, -1 or errno on error. + */ +static int es_veq_control_set(int value) +{ + return write_int(SYSFS_PATH_VEQ, value); +} + +/* + * Writes the extra volume to sysfs. + * + * @param value The Integer value to be written. + * @return 0 on success, -1 or errno on error. + */ +static int es_extra_volume_set(int value) +{ + return write_int(SYSFS_PATH_EXTRAVOLUME, value); +} + +/* + * Convertes an out_device from the session to an earSmart compatible route. + * + * @param out_device The output device to be converted. + * @return Audience earSmart route, coded as long Integer. + */ +static long es_device_to_route(struct voice_session *session) +{ + long ret; + long nb_route; + long wb_route; + + switch(session->out_device) { + case AUDIO_DEVICE_OUT_EARPIECE: + nb_route = Call_CT_NB; + wb_route = Call_CT_WB; + break; + case AUDIO_DEVICE_OUT_SPEAKER: + nb_route = Call_FT_NB; + wb_route = Call_FT_WB; + break; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + nb_route = Call_HS_NB; + wb_route = Call_HS_WB; + break; + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + nb_route = Call_BT_NB; + wb_route = Call_BT_WB; + break; + default: + /* if output device isn't supported, use earpiece by default */ + nb_route = Call_CT_NB; + wb_route = Call_CT_WB; + break; + } + + /* TODO: Handle wb_amr=2 */ + if (session->wb_amr_type >= 1) { + ret = wb_route; + } else { + ret = nb_route; + } + + ALOGV("%s: converting out_device=%d to %s route: %ld", __func__, session->out_device, + ret == wb_route ? "Wide Band" : "Narrow Band", ret); + return ret; +} + +/* + * Configures and enables the Audience earSmart IC. + * + * @param session Reference to the active voice call session. + * @return @return 0 on success, -1 or errno on error. + */ +int es_start_voice_session(struct voice_session *session) +{ + int ret; + long es_route = es_device_to_route(session); + + /* TODO: Calculate these */ + int extra_volume = 0; + int veq_control = 4; + + /* + * The control flow for audience earSmart chip is as follows: + * + * route_value >> power_control(internal) >> extra_volume >> veq_control + */ + ret = es_route_value_set(es_route); + if (ret != 0) { + ALOGE("%s: es_route_value_set(%ld) failed with code: %d", __func__, es_route, ret); + goto exit; + } + ret = es_extra_volume_set(extra_volume); + if (ret != 0) { + ALOGE("%s: es_extra_volume_set(%d) failed with code: %d", __func__, extra_volume, ret); + goto exit; + } + ret = es_veq_control_set(veq_control); + if (ret != 0) { + ALOGE("%s: es_veq_control_set(%d) failed with code: %d", __func__, veq_control, ret); + goto exit; + } + +exit: + return ret; +} + +/* + * Disables the Audience earSmart IC. + */ +void es_stop_voice_session() +{ + /* This will cancel any pending workers, stop the stream and send the IC to sleep */ + es_route_value_set(AUDIENCE_SLEEP); +} diff --git a/audio/audience.h b/audio/audience.h new file mode 100644 index 0000000..5200a3f --- /dev/null +++ b/audio/audience.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Christopher N. Hesse + * + * 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. + */ + +#include "audio_hw.h" +#include "voice.h" + +enum es_power_state { + ES_POWER_FW_LOAD, + ES_POWER_SLEEP, + ES_POWER_SLEEP_PENDING, + ES_POWER_AWAKE, + ES_MAX = ES_POWER_AWAKE +}; + +int es_start_voice_session(struct voice_session *session); +void es_stop_voice_session(); diff --git a/audio/include/audience-routes.h b/audio/include/audience-routes.h new file mode 100644 index 0000000..6536195 --- /dev/null +++ b/audio/include/audience-routes.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Christopher N. Hesse + * + * 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. + */ + +/* + * NOTICE + * + * This must be kept in sync with the kernel API, for exmaple + * "es705-routes.h" for the Galaxy Note 5 (N920T) with its ES804 IC. + */ + +#define SYSFS_PATH_POWERCTRL "/sys/class/earsmart/control/power_control_set" +#define SYSFS_PATH_PRESET "/sys/class/earsmart/control/route_value" +#define SYSFS_PATH_VEQ "/sys/class/earsmart/control/veq_control_set" +#define SYSFS_PATH_EXTRAVOLUME "/sys/class/earsmart/control/extra_volume" +#define Call_HS_NB 0 /* Call, Headset, Narrow Band */ +#define Call_FT_NB 1 /* Call, Far Talk, Narrow Band */ +#define Call_CT_NB 2 /* Call, Close Talk, Narrow Band */ +#define Call_FT_NB_NR_OFF 3 /* Call, Far Talk, NB, NR off */ +#define Call_CT_NB_NR_OFF 4 /* Call, Close Talk, NB, NR off */ +#define Call_BT_NB 10 /* Call, BT, NB */ +#define Call_TTY_VCO 11 /* Call, TTY HCO NB */ +#define Call_TTY_HCO 12 /* Call, TTY VCO NB */ +#define Call_TTY_FULL 13 /* Call, TTY FULL NB */ +#define Call_FT_EVS 14 /* Call, Far Talk, EVS */ +#define Call_CT_EVS 15 /* Call, Close Talk, EVS */ +#define LOOPBACK_CT 17 /* Loopback, Close Talk */ +#define LOOPBACK_FT 18 /* Loopback, Far Talk */ +#define LOOPBACK_HS 19 /* Loopback, Headset */ +#define Call_BT_WB 20 /* Call, BT, WB */ +#define Call_HS_WB 21 /* Call, Headset, Wide Band */ +#define Call_FT_WB 22 /* Call, Far Talk, Wide Band */ +#define Call_CT_WB 23 /* Call, Close Talk, Wide Band */ +#define Call_FT_WB_NR_OFF 24 /* Call, Far Talk, WB, NR off */ +#define Call_CT_WB_NR_OFF 25 /* Call, Close Talk, WB, NR off */ +#define AUDIENCE_SLEEP 40 /* Route none, Audience Sleep State */ diff --git a/audio/include/samsung_audio.h b/audio/include/samsung_audio.h index c1504f7..7731296 100644 --- a/audio/include/samsung_audio.h +++ b/audio/include/samsung_audio.h @@ -83,4 +83,12 @@ */ /* #define DSP_POWEROFF_DELAY 0 */ +/* + * Some device variants (often T-Mobile) have a separate voice processing IC + * (Audience EarSmart xxx). + * This hooks into the voice call session and enables, configures and disables + * this extra firmware so RX/TX streams can be routed by the driver. + */ +/* #define AUDIENCE_EARSMART_IC */ + #endif // SAMSUNG_AUDIO_H diff --git a/audio/voice.c b/audio/voice.c index 6de05fa..824724a 100644 --- a/audio/voice.c +++ b/audio/voice.c @@ -34,6 +34,10 @@ #include "audio_hw.h" #include "voice.h" +#ifdef AUDIENCE_EARSMART_IC +#include "audience.h" +#endif + static struct pcm_config pcm_config_voicecall = { .channels = 2, .rate = 8000, @@ -249,6 +253,11 @@ int start_voice_session(struct voice_session *session) start_voice_session_bt_sco(session); } +#ifdef AUDIENCE_EARSMART_IC + ALOGV("%s: Enabling Audience IC", __func__); + es_start_voice_session(session); +#endif + if (session->two_mic_control) { ALOGV("%s: enabling two mic control", __func__); ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON); @@ -290,6 +299,10 @@ void stop_voice_session(struct voice_session *session) stop_voice_session_bt_sco(session); } +#ifdef AUDIENCE_EARSMART_IC + ALOGV("%s: Disabling Audience IC", __func__); + es_stop_voice_session(); +#endif session->out_device = AUDIO_DEVICE_NONE;