hal: Add support for In-Car Communication usecase
* Add ICC library functionality in icc.c
* Add support in audio_extn to open and initialize icc library
* Add platform changes for device selection in ICC usecase
* Add support for sending ICC audio calibration/app type cfg
Suggested-by: Tahir Dawson <dawson@qti.qualcomm.com>
Change-Id: I26937da282fcdd31d59a54b180dca5d7740fbfb0
diff --git a/hal/audio_extn/icc.c b/hal/audio_extn/icc.c
new file mode 100644
index 0000000..a38080f
--- /dev/null
+++ b/hal/audio_extn/icc.c
@@ -0,0 +1,353 @@
+/* icc.c
+Copyright (c) 2012-2015, 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
+
+#define LOG_TAG "audio_hw_icc"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <stdlib.h>
+#include <cutils/str_parms.h>
+#include "audio_extn.h"
+
+#define AUDIO_PARAMETER_ICC_ENABLE "conversation_mode_state"
+#define AUDIO_PARAMETER_ICC_SET_SAMPLING_RATE "icc_set_sampling_rate"
+#define AUDIO_PARAMETER_KEY_ICC_VOLUME "icc_volume"
+
+#ifdef PLATFORM_AUTO
+#define ICC_RX_VOLUME "Playback 33 Volume"
+#else
+#define ICC_RX_VOLUME "NULL"
+#endif
+
+static int32_t start_icc(struct audio_device *adev,
+ struct str_parms *parms);
+
+static int32_t stop_icc(struct audio_device *adev);
+
+struct icc_module {
+ struct pcm *icc_pcm_rx;
+ struct pcm *icc_pcm_tx;
+ bool is_icc_running;
+ float icc_volume;
+ audio_usecase_t ucid;
+};
+
+static struct icc_module iccmod = {
+ .icc_pcm_rx = NULL,
+ .icc_pcm_tx = NULL,
+ .icc_volume = 0,
+ .is_icc_running = 0,
+ .ucid = USECASE_ICC_CALL,
+};
+static struct pcm_config pcm_config_icc = {
+ .channels = 4,
+ .rate = 16000,
+ .period_size = 240,
+ .period_count = 2,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .stop_threshold = INT_MAX,
+ .avail_min = 0,
+};
+
+static fp_platform_get_pcm_device_id_t fp_platform_get_pcm_device_id;
+static fp_platform_set_echo_reference_t fp_platform_set_echo_reference;
+static fp_select_devices_t fp_select_devices;
+static fp_audio_extn_ext_hw_plugin_usecase_start_t fp_audio_extn_ext_hw_plugin_usecase_start;
+static fp_audio_extn_ext_hw_plugin_usecase_stop_t fp_audio_extn_ext_hw_plugin_usecase_stop;
+static fp_get_usecase_from_list_t fp_get_usecase_from_list;
+static fp_disable_audio_route_t fp_disable_audio_route;
+static fp_disable_snd_device_t fp_disable_snd_device;
+
+static int32_t icc_set_volume(struct audio_device *adev, float value)
+{
+ int32_t ret = 0, vol = 0;
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = ICC_RX_VOLUME;
+
+ ALOGD("%s: enter", __func__);
+ ALOGD("%s: (%f)", __func__, value);
+
+ iccmod.icc_volume = value;
+ if (value < 0.0) {
+ ALOGW("%s: (%f) Under 0.0, assuming 0.0", __func__, value);
+ value = 0.0;
+ } else {
+ value = ((value > 15.000000) ? 1.0 : (value / 15));
+ ALOGW("%s: Volume brought with in range (%f)", __func__, value);
+ }
+ vol = lrint((value * 0x2000) + 0.5);
+
+ if(!iccmod.is_icc_running) {
+ ALOGV("%s: ICC not active, ignoring icc_set_volume call", __func__);
+ return -EIO;
+ }
+
+ ALOGD("%s: Setting ICC Volume to %d", __func__, vol);
+ 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;
+ }
+ if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
+ ALOGE("%s: Couldn't set ICC Volume [%d]", __func__, vol);
+ return -EINVAL;
+ }
+
+ ALOGD("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+static int32_t start_icc(struct audio_device *adev,
+ struct str_parms *parms __unused)
+{
+ int32_t ret = 0;
+ struct audio_usecase *uc_info;
+ int32_t pcm_dev_rx_id, pcm_dev_tx_id;
+
+ ALOGD("%s: enter", __func__);
+
+ uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+
+ if (!uc_info)
+ return -ENOMEM;
+
+ uc_info->id = iccmod.ucid;
+ uc_info->type = ICC_CALL;
+ uc_info->stream.out = adev->primary_output;
+ list_init(&uc_info->device_list);
+ assign_devices(&uc_info->device_list, &adev->primary_output->device_list);
+ uc_info->in_snd_device = SND_DEVICE_NONE;
+ uc_info->out_snd_device = SND_DEVICE_NONE;
+
+ list_add_tail(&adev->usecase_list, &uc_info->list);
+
+ fp_select_devices(adev, iccmod.ucid);
+
+ if ((uc_info->out_snd_device != SND_DEVICE_NONE) ||
+ (uc_info->in_snd_device != SND_DEVICE_NONE)) {
+ if (fp_audio_extn_ext_hw_plugin_usecase_start(adev->ext_hw_plugin, uc_info))
+ ALOGE("%s: failed to start ext hw plugin", __func__);
+ }
+
+ pcm_dev_rx_id = fp_platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+ pcm_dev_tx_id = fp_platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+ if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ) {
+ ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
+ __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+ ret = -EIO;
+ goto exit;
+ }
+
+ ALOGV("%s: ICC PCM devices (ICC pcm rx: %d pcm tx: %d) for the usecase(%d)",
+ __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+
+ iccmod.icc_pcm_rx = pcm_open(adev->snd_card,
+ pcm_dev_rx_id,
+ PCM_OUT, &pcm_config_icc);
+ if (iccmod.icc_pcm_rx && !pcm_is_ready(iccmod.icc_pcm_rx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(iccmod.icc_pcm_rx));
+ ret = -EIO;
+ goto exit;
+ }
+
+ iccmod.icc_pcm_tx = pcm_open(adev->snd_card,
+ pcm_dev_tx_id,
+ PCM_IN, &pcm_config_icc);
+ if (iccmod.icc_pcm_tx && !pcm_is_ready(iccmod.icc_pcm_tx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(iccmod.icc_pcm_tx));
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (pcm_start(iccmod.icc_pcm_rx) < 0) {
+ ALOGE("%s: pcm start for icc pcm rx failed", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (pcm_start(iccmod.icc_pcm_tx) < 0) {
+ ALOGE("%s: pcm start for icc pcm tx failed", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ iccmod.is_icc_running = true;
+ icc_set_volume(adev, iccmod.icc_volume);
+
+ ALOGD("%s: exit: status(%d)", __func__, ret);
+ return 0;
+
+exit:
+ stop_icc(adev);
+ ALOGE("%s: Problem in ICC start: status(%d)", __func__, ret);
+ return ret;
+}
+
+static int32_t stop_icc(struct audio_device *adev)
+{
+ int32_t ret = 0;
+ struct audio_usecase *uc_info;
+
+ ALOGD("%s: enter", __func__);
+ iccmod.is_icc_running = false;
+
+ /* 1. Close the PCM devices */
+
+ if (iccmod.icc_pcm_rx) {
+ pcm_close(iccmod.icc_pcm_rx);
+ iccmod.icc_pcm_rx = NULL;
+ }
+ if (iccmod.icc_pcm_tx) {
+ pcm_close(iccmod.icc_pcm_tx);
+ iccmod.icc_pcm_tx = NULL;
+ }
+
+ uc_info = fp_get_usecase_from_list(adev, iccmod.ucid);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__, iccmod.ucid);
+ return -EINVAL;
+ }
+
+ if ((uc_info->out_snd_device != SND_DEVICE_NONE) ||
+ (uc_info->in_snd_device != SND_DEVICE_NONE)) {
+ if (fp_audio_extn_ext_hw_plugin_usecase_stop(adev->ext_hw_plugin, uc_info))
+ ALOGE("%s: failed to stop ext hw plugin", __func__);
+ }
+
+ /* 2. Disable echo reference while stopping icc */
+ fp_platform_set_echo_reference(adev, false, &uc_info->device_list);
+
+ /* 3. Get and set stream specific mixer controls */
+ fp_disable_audio_route(adev, uc_info);
+
+ /* 4. Disable the rx and tx devices */
+ fp_disable_snd_device(adev, uc_info->out_snd_device);
+ fp_disable_snd_device(adev, uc_info->in_snd_device);
+
+ list_remove(&uc_info->list);
+ free(uc_info);
+
+ ALOGD("%s: exit: status(%d)", __func__, ret);
+ return ret;
+}
+
+void icc_init(icc_init_config_t init_config)
+{
+ fp_platform_get_pcm_device_id = init_config.fp_platform_get_pcm_device_id;
+ fp_platform_set_echo_reference = init_config.fp_platform_set_echo_reference;
+ fp_select_devices = init_config.fp_select_devices;
+ fp_audio_extn_ext_hw_plugin_usecase_start =
+ init_config.fp_audio_extn_ext_hw_plugin_usecase_start;
+ fp_audio_extn_ext_hw_plugin_usecase_stop =
+ init_config.fp_audio_extn_ext_hw_plugin_usecase_stop;
+ fp_get_usecase_from_list = init_config.fp_get_usecase_from_list;
+ fp_disable_audio_route = init_config.fp_disable_audio_route;
+ fp_disable_snd_device = init_config.fp_disable_snd_device;
+}
+
+bool icc_is_active(struct audio_device *adev)
+{
+ struct audio_usecase *icc_usecase = NULL;
+ icc_usecase = fp_get_usecase_from_list(adev, iccmod.ucid);
+
+ if (icc_usecase != NULL)
+ return true;
+ else
+ return false;
+}
+
+audio_usecase_t icc_get_usecase()
+{
+ return iccmod.ucid;
+}
+
+void icc_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+ int ret;
+ int rate;
+ int val;
+ float vol;
+ char value[32]={0};
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_ICC_ENABLE, value,
+ sizeof(value));
+ if (ret >= 0) {
+ if (!strncmp(value,"true",sizeof(value)) && !iccmod.is_icc_running)
+ ret = start_icc(adev,parms);
+ else if (!strncmp(value, "false", sizeof(value)) && iccmod.is_icc_running)
+ stop_icc(adev);
+ else
+ ALOGE("%s=%s is unsupported", AUDIO_PARAMETER_ICC_ENABLE, value);
+ }
+ memset(value, 0, sizeof(value));
+ ret = str_parms_get_str(parms,AUDIO_PARAMETER_ICC_SET_SAMPLING_RATE, value,
+ sizeof(value));
+ if (ret >= 0) {
+ rate = atoi(value);
+ if (rate == 16000){
+ iccmod.ucid = USECASE_ICC_CALL;
+ pcm_config_icc.rate = rate;
+ } else
+ ALOGE("Unsupported rate..");
+ }
+
+ if (iccmod.is_icc_running) {
+ memset(value, 0, sizeof(value));
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ if (ret >= 0) {
+ val = atoi(value);
+ if (val > 0)
+ fp_select_devices(adev, iccmod.ucid);
+ }
+ }
+
+ memset(value, 0, sizeof(value));
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ICC_VOLUME,
+ value, sizeof(value));
+ if (ret >= 0) {
+ if (sscanf(value, "%f", &vol) != 1){
+ ALOGE("%s: error in retrieving icc volume", __func__);
+ ret = -EIO;
+ goto exit;
+ }
+ ALOGD("%s: icc_set_volume usecase, Vol: [%f]", __func__, vol);
+ icc_set_volume(adev, vol);
+ }
+exit:
+ ALOGV("%s Exit",__func__);
+}