audio: Add support for Audience EarSmart ICs
authorChristopher N. Hesse <raymanfx@gmail.com>
Sat, 25 Feb 2017 00:37:56 +0000 (01:37 +0100)
committerChristopher N. Hesse <raymanfx@gmail.com>
Fri, 3 Mar 2017 21:49:59 +0000 (21:49 +0000)
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

audio/Android.mk
audio/audience.c [new file with mode: 0644]
audio/audience.h [new file with mode: 0644]
audio/include/audience-routes.h [new file with mode: 0644]
audio/include/samsung_audio.h
audio/voice.c

index d31b1d9d17db5752ed80fa087fffb8fcec174781..9ececd2275783456d193eb551cfa0e4630567abf 100644 (file)
@@ -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 (file)
index 0000000..afd7fa2
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <cutils/log.h>
+#include <audience-routes.h>
+
+#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 (file)
index 0000000..5200a3f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
+ *
+ * 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 (file)
index 0000000..6536195
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
+ *
+ * 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 */
index c1504f7219fd33fb8879ec3192686fe0dc52b81c..7731296874846a75b3945b37ed1bd2ca3ceea35f 100644 (file)
  */
 /* #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
index 6de05fa35831681ce81fbbd2c4429800568b1958..824724a01de9f4d13f18fcd39d446d7751465e88 100644 (file)
 #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;