Fix potential NULL dereference in Visualizer effect am: 0c39abc483 am: 18b2de20a7 am: bcd048aea2 am: 67c5eb2059 am: 996c0f33a8
am: ae2849ff1d
Change-Id: Ic71b709d17623bef1c8580c9d9c194a029f1d368
diff --git a/Android.mk b/Android.mk
index 6dbcccd..1e26411 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74 msm8x84 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74 msm8x84 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
MY_LOCAL_PATH := $(call my-dir)
diff --git a/hal/Android.mk b/hal/Android.mk
index 18b8bf4..0d931c1 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -7,7 +7,7 @@
LOCAL_ARM_MODE := arm
AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
-ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
# B-family platform uses msm8974 code base
AUDIO_PLATFORM = msm8974
ifneq ($(filter msm8226,$(TARGET_BOARD_PLATFORM)),)
@@ -16,6 +16,12 @@
ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),)
LOCAL_CFLAGS := -DPLATFORM_MSM8084
endif
+ifneq ($(filter msm8992,$(TARGET_BOARD_PLATFORM)),)
+ LOCAL_CFLAGS := -DPLATFORM_MSM8994
+endif
+ifneq ($(filter msm8994,$(TARGET_BOARD_PLATFORM)),)
+ LOCAL_CFLAGS := -DPLATFORM_MSM8994
+endif
endif
LOCAL_SRC_FILES := \
@@ -54,9 +60,37 @@
LOCAL_SRC_FILES += audio_extn/hfp.c
endif
+ifeq ($(strip $(AUDIO_FEATURE_NO_AUDIO_OUT)),true)
+ LOCAL_CFLAGS += -DNO_AUDIO_OUT
+endif
+
+ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true)
+ LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED
+ LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
+ LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger
+ LOCAL_SRC_FILES += audio_extn/soundtrigger.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SPKR_PROTECTION)),true)
+ LOCAL_CFLAGS += -DSPKR_PROT_ENABLED
+ LOCAL_SRC_FILES += audio_extn/spkr_protection.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DSM_FEEDBACK)),true)
+ LOCAL_CFLAGS += -DDSM_FEEDBACK_ENABLED
+ LOCAL_SRC_FILES += audio_extn/dsm_feedback.c
+endif
+
+ifneq ($(filter msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
+ # push codec/mad calibration to HW dep node
+ # applicable to msm8992/8994 or newer platforms
+ LOCAL_CFLAGS += -DHWDEP_CAL_ENABLED
+ LOCAL_SRC_FILES += audio_extn/hwdep_cal.c
+endif
+
LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 26c2fb4..b99378e 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -25,6 +25,24 @@
void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
void audio_extn_extspk_set_voice_vol(void* extn, float vol);
+#ifndef SPKR_PROT_ENABLED
+#define audio_extn_spkr_prot_init(adev) (0)
+#define audio_extn_spkr_prot_start_processing(snd_device) (-EINVAL)
+#define audio_extn_spkr_prot_calib_cancel(adev) (0)
+#define audio_extn_spkr_prot_stop_processing(snd_device) (0)
+#define audio_extn_spkr_prot_is_enabled() (false)
+#define audio_extn_spkr_prot_get_acdb_id(snd_device) (-EINVAL)
+#define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device)
+#else
+void audio_extn_spkr_prot_init(void *adev);
+int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
+void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device);
+bool audio_extn_spkr_prot_is_enabled();
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
+void audio_extn_spkr_prot_calib_cancel(void *adev);
+#endif
+
#ifndef HFP_ENABLED
#define audio_extn_hfp_is_active(adev) (0)
#define audio_extn_hfp_get_usecase() (-1)
@@ -38,4 +56,49 @@
struct str_parms *parms);
#endif
+#ifndef SOUND_TRIGGER_ENABLED
+#define audio_extn_sound_trigger_init(adev) (0)
+#define audio_extn_sound_trigger_deinit(adev) (0)
+#define audio_extn_sound_trigger_update_device_status(snd_dev, event) (0)
+#define audio_extn_sound_trigger_set_parameters(adev, parms) (0)
+#define audio_extn_sound_trigger_check_and_get_session(in) (0)
+#define audio_extn_sound_trigger_stop_lab(in) (0)
+#define audio_extn_sound_trigger_read(in, buffer, bytes) (0)
+
+#else
+
+enum st_event_type {
+ ST_EVENT_SND_DEVICE_FREE,
+ ST_EVENT_SND_DEVICE_BUSY,
+ ST_EVENT_STREAM_FREE,
+ ST_EVENT_STREAM_BUSY
+};
+typedef enum st_event_type st_event_type_t;
+
+int audio_extn_sound_trigger_init(struct audio_device *adev);
+void audio_extn_sound_trigger_deinit(struct audio_device *adev);
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+ st_event_type_t event);
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
+ struct str_parms *parms);
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
+int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
+ size_t bytes);
+#endif
+
+#ifndef DSM_FEEDBACK_ENABLED
+#define audio_extn_dsm_feedback_enable(adev, snd_device, benable) (0)
+#else
+void audio_extn_dsm_feedback_enable(struct audio_device *adev,
+ snd_device_t snd_device,
+ bool benable);
+#endif
+
+#ifndef HWDEP_CAL_ENABLED
+#define audio_extn_hwdep_cal_send(snd_card, acdb_handle) (0)
+#else
+void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle);
+#endif
+
#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/dsm_feedback.c b/hal/audio_extn/dsm_feedback.c
new file mode 100755
index 0000000..e340dae
--- /dev/null
+++ b/hal/audio_extn/dsm_feedback.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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_dsm_feedback"
+/*#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>
+
+
+static struct pcm_config pcm_config_dsm = {
+ .channels = 2,
+ .rate = 48000,
+ .period_size = 256,
+ .period_count = 4,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .stop_threshold = INT_MAX,
+ .avail_min = 0,
+};
+
+int start_dsm_feedback_processing(struct audio_device *adev, int enable)
+{
+ int ret = 0;
+ int32_t pcm_dev_tx_id = -1;
+ static struct pcm *dsm_pcm_handle = NULL;
+
+ if (enable) {
+ /*do nothing if already enabled*/
+ if (dsm_pcm_handle)
+ return ret;
+
+ pcm_dev_tx_id = platform_get_pcm_device_id(USECASE_AUDIO_DSM_FEEDBACK, PCM_CAPTURE);
+ if (pcm_dev_tx_id < 0) {
+ ALOGE("%s: Invalid pcm device for usecase (%d)",
+ __func__, USECASE_AUDIO_DSM_FEEDBACK);
+ ret = -ENODEV;
+ goto close;
+ }
+
+ dsm_pcm_handle = pcm_open(adev->snd_card,
+ pcm_dev_tx_id,
+ PCM_IN, &pcm_config_dsm);
+ if (dsm_pcm_handle && !pcm_is_ready(dsm_pcm_handle)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(dsm_pcm_handle));
+ ret = -EIO;
+ goto close;
+ }
+
+ if (pcm_start(dsm_pcm_handle) < 0) {
+ ALOGE("%s: pcm start for RX failed", __func__);
+ ret = -EINVAL;
+ goto close;
+ }
+
+ return ret;
+ }
+
+close:
+ /*close pcm if disable or error happend in opening*/
+ if (dsm_pcm_handle) {
+ pcm_close(dsm_pcm_handle);
+ dsm_pcm_handle = NULL;
+ }
+
+ return ret;
+}
+
+void audio_extn_dsm_feedback_enable(struct audio_device *adev,
+ snd_device_t snd_device,
+ int benable)
+{
+ if ( NULL == adev )
+ return;
+
+ if( snd_device == SND_DEVICE_OUT_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
+ snd_device == SND_DEVICE_OUT_VOICE_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE )
+ start_dsm_feedback_processing(adev, benable);
+}
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
index a551fb3..55cbb4c 100644
--- a/hal/audio_extn/ext_speaker.c
+++ b/hal/audio_extn/ext_speaker.c
@@ -18,6 +18,7 @@
/*#define LOG_NDEBUG 0*/
#include <cutils/log.h>
+#include <stdlib.h>
#include <audio_hw.h>
#include <dlfcn.h>
diff --git a/hal/audio_extn/hwdep_cal.c b/hal/audio_extn/hwdep_cal.c
new file mode 100644
index 0000000..811db3e
--- /dev/null
+++ b/hal/audio_extn/hwdep_cal.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 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 "hardware_cal"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#ifdef HWDEP_CAL_ENABLED
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <cutils/log.h>
+#include <audio_hw.h>
+#include "audio_extn.h"
+#include "sound/msmcal-hwdep.h"
+
+#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100)
+#define MAX_CAL_NAME 20
+
+typedef struct acdb_audio_cal_cfg {
+ uint32_t persist;
+ uint32_t snd_dev_id;
+ audio_devices_t dev_id;
+ int32_t acdb_dev_id;
+ uint32_t app_type;
+ uint32_t topo_id;
+ uint32_t sampling_rate;
+ uint32_t cal_type;
+ uint32_t module_id;
+ uint32_t param_id;
+} acdb_audio_cal_cfg_t;
+
+struct param_data {
+ int use_case;
+ int acdb_id;
+ int get_size;
+ int buff_size;
+ int data_size;
+ void *buff;
+};
+
+char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = {
+ [WCD9XXX_ANC_CAL] = "anc_cal",
+ [WCD9XXX_MBHC_CAL] = "mbhc_cal",
+ [WCD9XXX_MAD_CAL] = "mad_cal",
+};
+
+typedef int (*acdb_get_calibration_t)(char *attr, int size, void *data);
+acdb_get_calibration_t acdb_get_calibration;
+
+static int hw_util_open(int card_no)
+{
+ int fd = -1;
+ char dev_name[256];
+
+ snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u",
+ card_no, WCD9XXX_CODEC_HWDEP_NODE);
+ ALOGD("%s: Opening device %s\n", __func__, dev_name);
+ fd = open(dev_name, O_WRONLY);
+ if (fd < 0) {
+ ALOGE("%s: cannot open device '%s'\n", __func__, dev_name);
+ return fd;
+ }
+ ALOGD("%s: success", __func__);
+ return fd;
+}
+
+static int send_codec_cal(acdb_get_calibration_t acdb_loader_get_calibration, int fd)
+{
+ int ret = 0, type;
+
+ for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) {
+ struct wcdcal_ioctl_buffer codec_buffer;
+ struct param_data calib;
+
+ if (!strcmp(cal_name_info[type], "mad_cal"))
+ calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID;
+ calib.get_size = 1;
+ ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data),
+ &calib);
+ if (ret < 0) {
+ ALOGE("%s get_calibration failed\n", __func__);
+ return ret;
+ }
+ calib.get_size = 0;
+ calib.buff = malloc(calib.buff_size);
+ ret = acdb_loader_get_calibration(cal_name_info[type],
+ sizeof(struct param_data), &calib);
+ if (ret < 0) {
+ ALOGE("%s get_calibration failed\n", __func__);
+ free(calib.buff);
+ return ret;
+ }
+ codec_buffer.buffer = calib.buff;
+ codec_buffer.size = calib.data_size;
+ codec_buffer.cal_type = type;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0)
+ ALOGE("Failed to call ioctl for %s err=%d",
+ cal_name_info[type], errno);
+ ALOGD("%s cal sent for %s", __func__, cal_name_info[type]);
+ free(calib.buff);
+ }
+ return ret;
+}
+
+
+void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle)
+{
+ int fd;
+
+ fd = hw_util_open(snd_card);
+ if (fd == -1) {
+ ALOGE("%s error open\n", __func__);
+ return;
+ }
+
+ acdb_get_calibration = (acdb_get_calibration_t)
+ dlsym(acdb_handle, "acdb_loader_get_calibration");
+
+ if (acdb_get_calibration == NULL) {
+ ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__,
+ dlerror());
+ return;
+ }
+ if (send_codec_cal(acdb_get_calibration, fd) < 0)
+ ALOGE("%s: Could not send anc cal", __FUNCTION__);
+
+ close(fd);
+}
+
+#endif
diff --git a/hal/audio_extn/soundtrigger.c b/hal/audio_extn/soundtrigger.c
new file mode 100644
index 0000000..4d09387
--- /dev/null
+++ b/hal/audio_extn/soundtrigger.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2015 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 "soundtrigger"
+/* #define LOG_NDEBUG 0 */
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/log.h>
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "sound_trigger_prop_intf.h"
+
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+struct sound_trigger_info {
+ struct sound_trigger_session_info st_ses;
+ bool lab_stopped;
+ struct listnode list;
+};
+
+struct sound_trigger_audio_device {
+ void *lib_handle;
+ struct audio_device *adev;
+ sound_trigger_hw_call_back_t st_callback;
+ struct listnode st_ses_list;
+ pthread_mutex_t lock;
+};
+
+static struct sound_trigger_audio_device *st_dev;
+
+static struct sound_trigger_info *
+get_sound_trigger_info(int capture_handle)
+{
+ struct sound_trigger_info *st_ses_info = NULL;
+ struct listnode *node;
+ ALOGV("%s: list %d capture_handle %d", __func__,
+ list_empty(&st_dev->st_ses_list), capture_handle);
+ list_for_each(node, &st_dev->st_ses_list) {
+ st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+ if (st_ses_info->st_ses.capture_handle == capture_handle)
+ return st_ses_info;
+ }
+ return NULL;
+}
+
+int audio_hw_call_back(sound_trigger_event_type_t event,
+ sound_trigger_event_info_t* config)
+{
+ int status = 0;
+ struct sound_trigger_info *st_ses_info;
+
+ if (!st_dev)
+ return -EINVAL;
+
+ pthread_mutex_lock(&st_dev->lock);
+ switch (event) {
+ case ST_EVENT_SESSION_REGISTER:
+ if (!config) {
+ ALOGE("%s: NULL config", __func__);
+ status = -EINVAL;
+ break;
+ }
+ st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
+ if (!st_ses_info) {
+ ALOGE("%s: st_ses_info alloc failed", __func__);
+ status = -ENOMEM;
+ break;
+ }
+ memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
+ ALOGV("%s: add capture_handle %d pcm %p", __func__,
+ st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+ list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
+ break;
+
+ case ST_EVENT_SESSION_DEREGISTER:
+ if (!config) {
+ ALOGE("%s: NULL config", __func__);
+ status = -EINVAL;
+ break;
+ }
+ st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
+ if (!st_ses_info) {
+ ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
+ status = -EINVAL;
+ break;
+ }
+ ALOGV("%s: remove capture_handle %d pcm %p", __func__,
+ st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+ list_remove(&st_ses_info->list);
+ free(st_ses_info);
+ break;
+ default:
+ ALOGW("%s: Unknown event %d", __func__, event);
+ break;
+ }
+ pthread_mutex_unlock(&st_dev->lock);
+ return status;
+}
+
+int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
+ size_t bytes)
+{
+ int ret = -1;
+ struct sound_trigger_info *st_info = NULL;
+ audio_event_info_t event;
+
+ if (!st_dev)
+ return ret;
+
+ if (!in->is_st_session_active) {
+ ALOGE(" %s: Sound trigger is not active", __func__);
+ goto exit;
+ }
+ if (in->standby)
+ in->standby = false;
+
+ pthread_mutex_lock(&st_dev->lock);
+ st_info = get_sound_trigger_info(in->capture_handle);
+ pthread_mutex_unlock(&st_dev->lock);
+ if (st_info) {
+ event.u.aud_info.ses_info = &st_info->st_ses;
+ event.u.aud_info.buf = buffer;
+ event.u.aud_info.num_bytes = bytes;
+ ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
+ }
+
+exit:
+ if (ret) {
+ if (-ENETRESET == ret)
+ in->is_st_session_active = false;
+ memset(buffer, 0, bytes);
+ ALOGV("%s: read failed status %d - sleep", __func__, ret);
+ usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
+ in->config.rate));
+ }
+ return ret;
+}
+
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
+{
+ int status = 0;
+ struct sound_trigger_info *st_ses_info = NULL;
+ audio_event_info_t event;
+
+ if (!st_dev || !in)
+ return;
+
+ pthread_mutex_lock(&st_dev->lock);
+ st_ses_info = get_sound_trigger_info(in->capture_handle);
+ pthread_mutex_unlock(&st_dev->lock);
+ if (st_ses_info) {
+ event.u.ses_info = st_ses_info->st_ses;
+ ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
+ st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
+ }
+}
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
+{
+ struct sound_trigger_info *st_ses_info = NULL;
+ struct listnode *node;
+
+ if (!st_dev || !in)
+ return;
+
+ pthread_mutex_lock(&st_dev->lock);
+ in->is_st_session = false;
+ ALOGV("%s: list %d capture_handle %d", __func__,
+ list_empty(&st_dev->st_ses_list), in->capture_handle);
+ list_for_each(node, &st_dev->st_ses_list) {
+ st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+ if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
+ in->pcm = st_ses_info->st_ses.pcm;
+ in->config = st_ses_info->st_ses.config;
+ in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
+ in->is_st_session = true;
+ in->is_st_session_active = true;
+ ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&st_dev->lock);
+}
+
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+ st_event_type_t event)
+{
+ int device_type = -1;
+
+ if (!st_dev)
+ return;
+
+ if (snd_device >= SND_DEVICE_OUT_BEGIN &&
+ snd_device < SND_DEVICE_OUT_END)
+ device_type = PCM_PLAYBACK;
+ else if (snd_device >= SND_DEVICE_IN_BEGIN &&
+ snd_device < SND_DEVICE_IN_END)
+ device_type = PCM_CAPTURE;
+ else {
+ ALOGE("%s: invalid device 0x%x, for event %d",
+ __func__, snd_device, event);
+ return;
+ }
+
+ ALOGI("%s: device 0x%x of type %d for Event %d",
+ __func__, snd_device, device_type, event);
+ if (device_type == PCM_CAPTURE) {
+ switch(event) {
+ case ST_EVENT_SND_DEVICE_FREE:
+ st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
+ break;
+ case ST_EVENT_SND_DEVICE_BUSY:
+ st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
+ break;
+ default:
+ ALOGW("%s:invalid event %d for device 0x%x",
+ __func__, event, snd_device);
+ }
+ }/*Events for output device, if required can be placed here in else*/
+}
+
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
+ struct str_parms *params)
+{
+ audio_event_info_t event;
+ char value[32];
+ int ret, val;
+
+ if(!st_dev || !params) {
+ ALOGE("%s: str_params NULL", __func__);
+ return;
+ }
+
+ ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
+ sizeof(value));
+ if (ret > 0) {
+ if (strstr(value, "OFFLINE")) {
+ event.u.status = SND_CARD_STATUS_OFFLINE;
+ st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+ }
+ else if (strstr(value, "ONLINE")) {
+ event.u.status = SND_CARD_STATUS_ONLINE;
+ st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+ }
+ else
+ ALOGE("%s: unknown snd_card_status", __func__);
+ }
+
+ ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
+ if (ret > 0) {
+ if (strstr(value, "OFFLINE")) {
+ event.u.status = CPE_STATUS_OFFLINE;
+ st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+ }
+ else if (strstr(value, "ONLINE")) {
+ event.u.status = CPE_STATUS_ONLINE;
+ st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+ }
+ else
+ ALOGE("%s: unknown CPE status", __func__);
+ }
+
+ ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
+ if (ret >= 0) {
+ event.u.value = val;
+ st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
+ }
+}
+
+int audio_extn_sound_trigger_init(struct audio_device *adev)
+{
+ int status = 0;
+ char sound_trigger_lib[100];
+ void *lib_handle;
+
+ ALOGI("%s: Enter", __func__);
+
+ st_dev = (struct sound_trigger_audio_device*)
+ calloc(1, sizeof(struct sound_trigger_audio_device));
+ if (!st_dev) {
+ ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
+ return -ENOMEM;
+ }
+
+ snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
+ "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
+ XSTR(SOUND_TRIGGER_PLATFORM_NAME));
+
+ st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
+
+ if (st_dev->lib_handle == NULL) {
+ ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
+ dlerror());
+ status = -EINVAL;
+ goto cleanup;
+ }
+ ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
+
+ st_dev->st_callback = (sound_trigger_hw_call_back_t)
+ dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
+
+ if (st_dev->st_callback == NULL) {
+ ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
+ dlerror());
+ goto cleanup;
+ }
+
+ st_dev->adev = adev;
+ list_init(&st_dev->st_ses_list);
+
+ return 0;
+
+cleanup:
+ if (st_dev->lib_handle)
+ dlclose(st_dev->lib_handle);
+ free(st_dev);
+ st_dev = NULL;
+ return status;
+
+}
+
+void audio_extn_sound_trigger_deinit(struct audio_device *adev)
+{
+ ALOGI("%s: Enter", __func__);
+ if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
+ dlclose(st_dev->lib_handle);
+ free(st_dev);
+ st_dev = NULL;
+ }
+}
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
new file mode 100644
index 0000000..4d8b233
--- /dev/null
+++ b/hal/audio_extn/spkr_protection.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2015 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_spkr_prot"
+/*#define LOG_NDEBUG 0*/
+//#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+#include <fcntl.h>
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <cutils/properties.h>
+#include "audio_extn.h"
+#include <linux/msm_audio_calibration.h>
+
+#ifdef SPKR_PROT_ENABLED
+
+/*Range of spkr temparatures -30C to 80C*/
+#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6))
+#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6))
+#define VI_FEED_CHANNEL "VI_FEED_TX Channels"
+
+/*Set safe temp value to 40C*/
+#define SAFE_SPKR_TEMP 40
+#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
+
+/*Range of resistance values 2ohms to 40 ohms*/
+#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
+#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
+
+/*Path where the calibration file will be stored*/
+#define CALIB_FILE "/data/misc/audio/audio.cal"
+
+/*Time between retries for calibartion or intial wait time
+ after boot up*/
+#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
+
+#define MIN_SPKR_IDLE_SEC (60 * 30)
+
+/*Once calibration is started sleep for 1 sec to allow
+ the calibration to kick off*/
+#define SLEEP_AFTER_CALIB_START (3000)
+
+/*If calibration is in progress wait for 200 msec before querying
+ for status again*/
+#define WAIT_FOR_GET_CALIB_STATUS (200)
+#define GET_SPKR_PROT_CAL_TIMEOUT_MSEC (5000)
+
+/*Speaker states*/
+#define SPKR_NOT_CALIBRATED -1
+#define SPKR_CALIBRATED 1
+
+/*Speaker processing state*/
+#define SPKR_PROCESSING_IN_PROGRESS 1
+#define SPKR_PROCESSING_IN_IDLE 0
+
+/*Modes of Speaker Protection*/
+enum speaker_protection_mode {
+ SPKR_PROTECTION_DISABLED = -1,
+ SPKR_PROTECTION_MODE_PROCESSING = 0,
+ SPKR_PROTECTION_MODE_CALIBRATE = 1,
+};
+
+struct speaker_prot_session {
+ int spkr_prot_mode;
+ int spkr_processing_state;
+ int thermal_client_handle;
+ pthread_mutex_t mutex_spkr_prot;
+ pthread_t spkr_calibration_thread;
+ pthread_mutex_t spkr_prot_thermalsync_mutex;
+ pthread_cond_t spkr_prot_thermalsync;
+ int cancel_spkr_calib;
+ pthread_cond_t spkr_calib_cancel;
+ pthread_mutex_t spkr_calib_cancelack_mutex;
+ pthread_cond_t spkr_calibcancel_ack;
+ pthread_t speaker_prot_threadid;
+ void *thermal_handle;
+ void *adev_handle;
+ int spkr_prot_t0;
+ struct pcm *pcm_rx;
+ struct pcm *pcm_tx;
+ int (*thermal_client_register_callback)
+ (char *client_name, int (*callback)(int), void *data);
+ void (*thermal_client_unregister_callback)(int handle);
+ int (*thermal_client_request)(char *client_name, int req_data);
+ bool spkr_prot_enable;
+ bool spkr_in_use;
+ struct timespec spkr_last_time_used;
+};
+
+static struct pcm_config pcm_config_skr_prot = {
+ .channels = 4,
+ .rate = 48000,
+ .period_size = 256,
+ .period_count = 4,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .stop_threshold = INT_MAX,
+ .avail_min = 0,
+};
+
+static struct speaker_prot_session handle;
+static int vi_feed_no_channels;
+
+static void spkr_prot_set_spkrstatus(bool enable)
+{
+ struct timespec ts;
+ if (enable)
+ handle.spkr_in_use = true;
+ else {
+ handle.spkr_in_use = false;
+ clock_gettime(CLOCK_BOOTTIME, &handle.spkr_last_time_used);
+ }
+}
+
+void audio_extn_spkr_prot_calib_cancel(void *adev)
+{
+ pthread_t threadid;
+ struct audio_usecase *uc_info;
+ int count = 0;
+ threadid = pthread_self();
+ ALOGV("%s: Entry", __func__);
+ if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
+ ALOGV("%s: Calibration not in progress.. nothihg to cancel", __func__);
+ return;
+ }
+ uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
+ if (uc_info) {
+ pthread_mutex_lock(&handle.mutex_spkr_prot);
+ pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+ handle.cancel_spkr_calib = 1;
+ pthread_cond_signal(&handle.spkr_calib_cancel);
+ pthread_mutex_unlock(&handle.mutex_spkr_prot);
+ pthread_cond_wait(&handle.spkr_calibcancel_ack,
+ &handle.spkr_calib_cancelack_mutex);
+ pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+ }
+ ALOGV("%s: Exit", __func__);
+}
+
+static bool is_speaker_in_use(unsigned long *sec)
+{
+ struct timespec temp;
+ if (!sec) {
+ ALOGE("%s: Invalid params", __func__);
+ return true;
+ }
+ if (handle.spkr_in_use) {
+ *sec = 0;
+ return true;
+ } else {
+ clock_gettime(CLOCK_BOOTTIME, &temp);
+ *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
+ return false;
+ }
+}
+
+
+static int get_spkr_prot_cal(int cal_fd,
+ struct audio_cal_info_msm_spk_prot_status *status)
+{
+ int ret = 0;
+ struct audio_cal_fb_spk_prot_status cal_data;
+
+ if (cal_fd < 0) {
+ ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (status == NULL) {
+ ALOGE("%s: Error: status NULL", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cal_data.hdr.data_size = sizeof(cal_data);
+ cal_data.hdr.version = VERSION_0_0;
+ cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
+ cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+ cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+ cal_data.cal_type.cal_hdr.buffer_number = 0;
+ cal_data.cal_type.cal_data.mem_handle = -1;
+
+ if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
+ ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!",
+ __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1];
+ status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2];
+ status->status = cal_data.cal_type.cal_info.status;
+done:
+ return ret;
+}
+
+static int set_spkr_prot_cal(int cal_fd,
+ struct audio_cal_info_spk_prot_cfg *protCfg)
+{
+ int ret = 0;
+ struct audio_cal_fb_spk_prot_cfg cal_data;
+ char value[PROPERTY_VALUE_MAX];
+
+ if (cal_fd < 0) {
+ ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (protCfg == NULL) {
+ ALOGE("%s: Error: status NULL", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&cal_data, 0, sizeof(cal_data));
+ cal_data.hdr.data_size = sizeof(cal_data);
+ cal_data.hdr.version = VERSION_0_0;
+ cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
+ cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+ cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+ cal_data.cal_type.cal_hdr.buffer_number = 0;
+ cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1];
+ cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2];
+ cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1];
+ cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2];
+ cal_data.cal_type.cal_info.mode = protCfg->mode;
+ property_get("persist.spkr.cal.duration", value, "0");
+ if (atoi(value) > 0) {
+ ALOGD("%s: quick calibration enabled", __func__);
+ cal_data.cal_type.cal_info.quick_calib_flag = 1;
+ } else {
+ ALOGD("%s: quick calibration disabled", __func__);
+ cal_data.cal_type.cal_info.quick_calib_flag = 0;
+ }
+
+ cal_data.cal_type.cal_data.mem_handle = -1;
+
+ if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) {
+ ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!",
+ __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int vi_feed_get_channels(struct audio_device *adev)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = VI_FEED_CHANNEL;
+ int value;
+
+ ALOGV("%s: entry", __func__);
+ 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);
+ goto error;
+ }
+ value = mixer_ctl_get_value(ctl, 0);
+ if (value < 0)
+ goto error;
+ else
+ return value+1;
+error:
+ return -EINVAL;
+}
+
+// must be called with adev->lock acquired
+static int spkr_calibrate(int t0)
+{
+ struct audio_device *adev = handle.adev_handle;
+ struct audio_cal_info_spk_prot_cfg protCfg;
+ struct audio_cal_info_msm_spk_prot_status status;
+ bool cleanup = false, disable_rx = false, disable_tx = false;
+ int acdb_fd = -1;
+ struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
+ int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
+ struct timespec ts;
+ int retry_duration;
+
+ if (!adev) {
+ ALOGE("%s: Invalid params", __func__);
+ return -EINVAL;
+ }
+ if (!list_empty(&adev->usecase_list)) {
+ ALOGD("%s: Usecase present retry speaker protection", __func__);
+ return -EAGAIN;
+ }
+ acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
+ if (acdb_fd < 0) {
+ ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
+ return -ENODEV;
+ } else {
+ protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
+ /* HAL for speaker protection gets only one Temperature */
+ protCfg.t0[SP_V2_SPKR_1] = t0;
+ protCfg.t0[SP_V2_SPKR_2] = t0;
+ if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+ ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
+ __func__);
+ status.status = -ENODEV;
+ goto exit;
+ }
+ }
+ uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ if (!uc_info_rx) {
+ return -ENOMEM;
+ }
+ uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
+ uc_info_rx->type = PCM_PLAYBACK;
+ uc_info_rx->in_snd_device = SND_DEVICE_NONE;
+ uc_info_rx->stream.out = adev->primary_output;
+ uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
+ disable_rx = true;
+ list_add_tail(&adev->usecase_list, &uc_info_rx->list);
+ enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
+ enable_audio_route(adev, uc_info_rx);
+
+ pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
+ ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
+ if (pcm_dev_rx_id < 0) {
+ ALOGE("%s: Invalid pcm device for usecase (%d)",
+ __func__, uc_info_rx->id);
+ status.status = -ENODEV;
+ goto exit;
+ }
+ handle.pcm_rx = handle.pcm_tx = NULL;
+ handle.pcm_rx = pcm_open(adev->snd_card,
+ pcm_dev_rx_id,
+ PCM_OUT, &pcm_config_skr_prot);
+ if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
+ status.status = -EIO;
+ goto exit;
+ }
+ uc_info_tx = (struct audio_usecase *)
+ calloc(1, sizeof(struct audio_usecase));
+ if (!uc_info_tx) {
+ status.status = -ENOMEM;
+ goto exit;
+ }
+ uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
+ uc_info_tx->type = PCM_CAPTURE;
+ uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
+ uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+
+ disable_tx = true;
+ list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+ enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+ enable_audio_route(adev, uc_info_tx);
+
+ pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+ if (pcm_dev_tx_id < 0) {
+ ALOGE("%s: Invalid pcm device for usecase (%d)",
+ __func__, uc_info_tx->id);
+ status.status = -ENODEV;
+ goto exit;
+ }
+ handle.pcm_tx = pcm_open(adev->snd_card,
+ pcm_dev_tx_id,
+ PCM_IN, &pcm_config_skr_prot);
+ if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
+ status.status = -EIO;
+ goto exit;
+ }
+ if (pcm_start(handle.pcm_rx) < 0) {
+ ALOGE("%s: pcm start for RX failed", __func__);
+ status.status = -EINVAL;
+ goto exit;
+ }
+ if (pcm_start(handle.pcm_tx) < 0) {
+ ALOGE("%s: pcm start for TX failed", __func__);
+ status.status = -EINVAL;
+ goto exit;
+ }
+ cleanup = true;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
+ ts.tv_nsec = 0;
+ pthread_mutex_lock(&handle.mutex_spkr_prot);
+ pthread_mutex_unlock(&adev->lock);
+
+ (void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
+ &handle.mutex_spkr_prot, &ts);
+ ALOGD("%s: Speaker calibration done", __func__);
+ pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+ if (handle.cancel_spkr_calib) {
+ status.status = -EAGAIN;
+ goto exit;
+ }
+
+ if (acdb_fd >= 0) {
+ status.status = -EINVAL;
+ retry_duration = 0;
+ while (!get_spkr_prot_cal(acdb_fd, &status) &&
+ retry_duration < GET_SPKR_PROT_CAL_TIMEOUT_MSEC) {
+ if (!status.status) {
+ ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
+ __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
+ FILE *fp;
+
+ vi_feed_no_channels = vi_feed_get_channels(adev);
+ ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
+ if (vi_feed_no_channels < 0) {
+ ALOGE("%s: no of channels negative !!", __func__);
+ /* limit the number of channels to 2*/
+ vi_feed_no_channels = 2;
+ }
+
+ fp = fopen(CALIB_FILE,"wb");
+ if (!fp) {
+ ALOGE("%s: spkr_prot_thread File open failed %s",
+ __func__, strerror(errno));
+ status.status = -ENODEV;
+ } else {
+ int i;
+ /* HAL for speaker protection is always calibrating for stereo usecase*/
+ for (i = 0; i < vi_feed_no_channels; i++) {
+ fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp);
+ fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
+ }
+ fclose(fp);
+ }
+ break;
+ } else if (status.status == -EAGAIN) {
+ ALOGD("%s: spkr_prot_thread try again", __func__);
+ usleep(WAIT_FOR_GET_CALIB_STATUS * 1000);
+ retry_duration += WAIT_FOR_GET_CALIB_STATUS;
+ } else {
+ ALOGE("%s: spkr_prot_thread get failed status %d",
+ __func__, status.status);
+ break;
+ }
+ }
+ }
+
+exit:
+ if (handle.pcm_rx)
+ pcm_close(handle.pcm_rx);
+ handle.pcm_rx = NULL;
+
+ if (handle.pcm_tx)
+ pcm_close(handle.pcm_tx);
+ handle.pcm_tx = NULL;
+
+ /* Clear TX calibration to handset mic */
+ platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+ if (!status.status) {
+ protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+ protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
+ protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
+ if (set_spkr_prot_cal(acdb_fd, &protCfg))
+ ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
+ else
+ handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+ } else {
+ protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+ handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+ if (set_spkr_prot_cal(acdb_fd, &protCfg))
+ ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
+ }
+ if (acdb_fd >= 0)
+ close(acdb_fd);
+
+ if (!handle.cancel_spkr_calib && cleanup) {
+ pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+ pthread_cond_wait(&handle.spkr_calib_cancel, &handle.mutex_spkr_prot);
+ pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+ }
+ if (disable_rx) {
+ list_remove(&uc_info_rx->list);
+ disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
+ disable_audio_route(adev, uc_info_rx);
+ }
+ if (disable_tx) {
+ list_remove(&uc_info_tx->list);
+ disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+ disable_audio_route(adev, uc_info_tx);
+ }
+ if (uc_info_rx) free(uc_info_rx);
+ if (uc_info_tx) free(uc_info_tx);
+ if (cleanup) {
+ if (handle.cancel_spkr_calib)
+ pthread_cond_signal(&handle.spkr_calibcancel_ack);
+ handle.cancel_spkr_calib = 0;
+ pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+ pthread_mutex_unlock(&handle.mutex_spkr_prot);
+ pthread_mutex_lock(&adev->lock);
+ }
+
+ return status.status;
+}
+
+static void* spkr_calibration_thread()
+{
+ unsigned long sec = 0;
+ int t0;
+ bool goahead = false;
+ struct audio_cal_info_spk_prot_cfg protCfg;
+ FILE *fp;
+ int acdb_fd;
+ struct audio_device *adev = handle.adev_handle;
+ unsigned long min_idle_time = MIN_SPKR_IDLE_SEC;
+ char value[PROPERTY_VALUE_MAX];
+
+ /* If the value of this persist.spkr.cal.duration is 0
+ * then it means it will take 30min to calibrate
+ * and if the value is greater than zero then it would take
+ * that much amount of time to calibrate.
+ */
+ property_get("persist.spkr.cal.duration", value, "0");
+ if (atoi(value) > 0)
+ min_idle_time = atoi(value);
+ handle.speaker_prot_threadid = pthread_self();
+ ALOGD("spkr_prot_thread enable prot Entry");
+ acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
+ if (acdb_fd >= 0) {
+ /*Set processing mode with t0/r0*/
+ protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+ if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+ ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
+ handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+ close(acdb_fd);
+ } else
+ handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+ } else {
+ handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+ ALOGE("%s: Failed to open acdb node", __func__);
+ }
+ if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
+ ALOGD("%s: Speaker protection disabled", __func__);
+ pthread_exit(0);
+ return NULL;
+ }
+
+ fp = fopen(CALIB_FILE,"rb");
+ if (fp) {
+ int i;
+ bool spkr_calibrated = true;
+ /* HAL for speaker protection is always calibrating for stereo usecase*/
+ vi_feed_no_channels = vi_feed_get_channels(adev);
+ ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
+ if (vi_feed_no_channels < 0) {
+ ALOGE("%s: no of channels negative !!", __func__);
+ /* limit the number of channels to 2*/
+ vi_feed_no_channels = 2;
+ }
+ for (i = 0; i < vi_feed_no_channels; i++) {
+ fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
+ fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
+ }
+ ALOGD("%s: spkr_prot_thread r0 value %d %d",
+ __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
+ ALOGD("%s: spkr_prot_thread t0 value %d %d",
+ __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
+ fclose(fp);
+ /*Valid tempature range: -30C to 80C(in q6 format)
+ Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
+ for (i = 0; i < vi_feed_no_channels; i++) {
+ if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
+ && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
+ && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
+ spkr_calibrated = false;
+ break;
+ }
+ }
+ if (spkr_calibrated) {
+ ALOGD("%s: Spkr calibrated", __func__);
+ protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+ if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+ ALOGE("%s: enable prot failed", __func__);
+ handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+ } else
+ handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+ close(acdb_fd);
+ pthread_exit(0);
+ return NULL;
+ }
+ close(acdb_fd);
+ }
+
+ while (1) {
+ ALOGV("%s: start calibration", __func__);
+ if (!handle.thermal_client_request("spkr",1)) {
+ ALOGD("%s: wait for callback from thermal daemon", __func__);
+ pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
+ pthread_cond_wait(&handle.spkr_prot_thermalsync,
+ &handle.spkr_prot_thermalsync_mutex);
+ /*Convert temp into q6 format*/
+ t0 = (handle.spkr_prot_t0 * (1 << 6));
+ pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
+ if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
+ ALOGE("%s: Calibration temparature error %d", __func__,
+ handle.spkr_prot_t0);
+ continue;
+ }
+ ALOGD("%s: Request t0 success value %d", __func__,
+ handle.spkr_prot_t0);
+ } else {
+ ALOGE("%s: Request t0 failed", __func__);
+ /*Assume safe value for temparature*/
+ t0 = SAFE_SPKR_TEMP_Q6;
+ }
+ goahead = false;
+ pthread_mutex_lock(&adev->lock);
+ if (is_speaker_in_use(&sec)) {
+ ALOGD("%s: Speaker in use retry calibration", __func__);
+ pthread_mutex_unlock(&adev->lock);
+ continue;
+ } else {
+ ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time);
+ if (sec < min_idle_time) {
+ ALOGD("%s: speaker idle is less retry", __func__);
+ pthread_mutex_unlock(&adev->lock);
+ continue;
+ }
+ goahead = true;
+ }
+ if (!list_empty(&adev->usecase_list)) {
+ ALOGD("%s: Usecase active re-try calibration", __func__);
+ goahead = false;
+ pthread_mutex_unlock(&adev->lock);
+ }
+ if (goahead) {
+ int status;
+ status = spkr_calibrate(t0);
+ pthread_mutex_unlock(&adev->lock);
+ if (status == -EAGAIN) {
+ ALOGE("%s: failed to calibrate try again %s",
+ __func__, strerror(status));
+ continue;
+ } else {
+ ALOGE("%s: calibrate status %s", __func__, strerror(status));
+ }
+ ALOGD("%s: spkr_prot_thread end calibration", __func__);
+ break;
+ }
+ }
+ if (handle.thermal_client_handle)
+ handle.thermal_client_unregister_callback(handle.thermal_client_handle);
+ handle.thermal_client_handle = 0;
+ if (handle.thermal_handle)
+ dlclose(handle.thermal_handle);
+ handle.thermal_handle = NULL;
+ pthread_exit(0);
+ return NULL;
+}
+
+static int thermal_client_callback(int temp)
+{
+ pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
+ ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
+ if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
+ handle.spkr_prot_t0 = temp;
+ pthread_cond_signal(&handle.spkr_prot_thermalsync);
+ pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
+ return 0;
+}
+
+void audio_extn_spkr_prot_init(void *adev)
+{
+ char value[PROPERTY_VALUE_MAX];
+ ALOGD("%s: Initialize speaker protection module", __func__);
+ memset(&handle, 0, sizeof(handle));
+ if (!adev) {
+ ALOGE("%s: Invalid params", __func__);
+ return;
+ }
+ property_get("persist.speaker.prot.enable", value, "");
+ handle.spkr_prot_enable = false;
+ if (!strncmp("true", value, 4))
+ handle.spkr_prot_enable = true;
+ if (!handle.spkr_prot_enable) {
+ ALOGD("%s: Speaker protection disabled", __func__);
+ return;
+ }
+ handle.adev_handle = adev;
+ handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+ handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
+ handle.spkr_prot_t0 = -1;
+ pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
+ pthread_cond_init(&handle.spkr_calib_cancel, NULL);
+ pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
+ pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
+ pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
+ pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
+ handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so",
+ RTLD_NOW);
+ if (!handle.thermal_handle) {
+ ALOGE("%s: DLOPEN for thermal client failed", __func__);
+ } else {
+ /*Query callback function symbol*/
+ handle.thermal_client_register_callback =
+ (int (*)(char *, int (*)(int),void *))
+ dlsym(handle.thermal_handle, "thermal_client_register_callback");
+ handle.thermal_client_unregister_callback =
+ (void (*)(int) )
+ dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
+ if (!handle.thermal_client_register_callback ||
+ !handle.thermal_client_unregister_callback) {
+ ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
+ } else {
+ /*Register callback function*/
+ handle.thermal_client_handle =
+ handle.thermal_client_register_callback("spkr", thermal_client_callback, NULL);
+ if (!handle.thermal_client_handle) {
+ ALOGE("%s: thermal_client_register_callback failed", __func__);
+ } else {
+ ALOGD("%s: spkr_prot thermal_client_register_callback success", __func__);
+ handle.thermal_client_request = (int (*)(char *, int))
+ dlsym(handle.thermal_handle, "thermal_client_request");
+ }
+ }
+ }
+ if (handle.thermal_client_request) {
+ ALOGD("%s: Create calibration thread", __func__);
+ (void)pthread_create(&handle.spkr_calibration_thread,
+ (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
+ } else {
+ ALOGE("%s: thermal_client_request failed", __func__);
+ if (handle.thermal_client_handle &&
+ handle.thermal_client_unregister_callback)
+ handle.thermal_client_unregister_callback(handle.thermal_client_handle);
+ if (handle.thermal_handle)
+ dlclose(handle.thermal_handle);
+ handle.thermal_handle = NULL;
+ handle.spkr_prot_enable = false;
+ }
+
+ if (handle.spkr_prot_enable) {
+ char platform[PROPERTY_VALUE_MAX];
+ property_get("ro.board.platform", platform, "");
+ if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
+ platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
+ "speaker-protected",
+ "SLIMBUS_0_RX");
+ }
+ }
+}
+
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
+{
+ int acdb_id;
+
+ switch(snd_device) {
+ case SND_DEVICE_OUT_SPEAKER:
+ acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
+ break;
+ case SND_DEVICE_OUT_VOICE_SPEAKER:
+ acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
+ break;
+ default:
+ acdb_id = -EINVAL;
+ break;
+ }
+ return acdb_id;
+}
+
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
+{
+ if (!handle.spkr_prot_enable)
+ return snd_device;
+
+ switch(snd_device) {
+ case SND_DEVICE_OUT_SPEAKER:
+ return SND_DEVICE_OUT_SPEAKER_PROTECTED;
+ case SND_DEVICE_OUT_VOICE_SPEAKER:
+ return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
+ default:
+ return snd_device;
+ }
+}
+
+int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
+{
+ struct audio_usecase *uc_info_tx;
+ struct audio_device *adev = handle.adev_handle;
+ int32_t pcm_dev_tx_id = -1, ret = 0;
+
+ ALOGV("%s: Entry", __func__);
+ if (!adev) {
+ ALOGE("%s: Invalid params", __func__);
+ return -EINVAL;
+ }
+ snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+ spkr_prot_set_spkrstatus(true);
+ uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ if (!uc_info_tx) {
+ return -ENOMEM;
+ }
+ ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
+ platform_get_snd_device_name(snd_device));
+ audio_route_apply_and_update_path(adev->audio_route,
+ platform_get_snd_device_name(snd_device));
+
+ pthread_mutex_lock(&handle.mutex_spkr_prot);
+ if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
+ uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
+ uc_info_tx->type = PCM_CAPTURE;
+ uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
+ uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+ handle.pcm_tx = NULL;
+ list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+ enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+ enable_audio_route(adev, uc_info_tx);
+
+ pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+ if (pcm_dev_tx_id < 0) {
+ ALOGE("%s: Invalid pcm device for usecase (%d)",
+ __func__, uc_info_tx->id);
+ ret = -ENODEV;
+ goto exit;
+ }
+ handle.pcm_tx = pcm_open(adev->snd_card,
+ pcm_dev_tx_id,
+ PCM_IN, &pcm_config_skr_prot);
+ if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
+ ret = -EIO;
+ goto exit;
+ }
+ if (pcm_start(handle.pcm_tx) < 0) {
+ ALOGE("%s: pcm start for TX failed", __func__);
+ ret = -EINVAL;
+ }
+ }
+
+exit:
+ /* Clear VI feedback cal and replace with handset MIC */
+ platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+ if (ret) {
+ if (handle.pcm_tx)
+ pcm_close(handle.pcm_tx);
+ handle.pcm_tx = NULL;
+ list_remove(&uc_info_tx->list);
+ disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+ disable_audio_route(adev, uc_info_tx);
+ free(uc_info_tx);
+ } else
+ handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
+ pthread_mutex_unlock(&handle.mutex_spkr_prot);
+ ALOGV("%s: Exit", __func__);
+ return ret;
+}
+
+void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
+{
+ struct audio_usecase *uc_info_tx;
+ struct audio_device *adev = handle.adev_handle;
+
+ ALOGV("%s: Entry", __func__);
+ snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+ spkr_prot_set_spkrstatus(false);
+ pthread_mutex_lock(&handle.mutex_spkr_prot);
+ if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
+ uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
+ if (handle.pcm_tx)
+ pcm_close(handle.pcm_tx);
+ handle.pcm_tx = NULL;
+ disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+ if (uc_info_tx) {
+ list_remove(&uc_info_tx->list);
+ disable_audio_route(adev, uc_info_tx);
+ free(uc_info_tx);
+ }
+ }
+ handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
+ pthread_mutex_unlock(&handle.mutex_spkr_prot);
+ if (adev)
+ audio_route_reset_and_update_path(adev->audio_route,
+ platform_get_snd_device_name(snd_device));
+ ALOGV("%s: Exit", __func__);
+}
+
+bool audio_extn_spkr_prot_is_enabled()
+{
+ return handle.spkr_prot_enable;
+}
+#endif /*SPKR_PROT_ENABLED*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 864ab5c..8fc7764 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -52,8 +52,9 @@
#include "sound/compress_params.h"
-#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
-#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4
+#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (256 * 1024)
+// 2 buffers causes problems with high bitrate files
+#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 3
/* ToDo: Check and update a proper value in msec */
#define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96
#define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
@@ -106,6 +107,8 @@
.channels = 2,
.period_count = AUDIO_CAPTURE_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
+ .stop_threshold = INT_MAX,
+ .avail_min = 0,
};
#define AFE_PROXY_CHANNEL_COUNT 2
@@ -144,6 +147,7 @@
[USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
[USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback",
[USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
+ [USECASE_AUDIO_PLAYBACK_TTS] = "audio-tts-playback",
[USECASE_AUDIO_RECORD] = "audio-record",
[USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
@@ -157,6 +161,9 @@
[USECASE_QCHAT_CALL] = "qchat-call",
[USECASE_VOWLAN_CALL] = "vowlan-call",
+ [USECASE_AUDIO_SPKR_CALIB_RX] = "spkr-rx-calib",
+ [USECASE_AUDIO_SPKR_CALIB_TX] = "spkr-vi-record",
+
[USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
[USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
};
@@ -176,6 +183,30 @@
};
static int set_voice_volume_l(struct audio_device *adev, float volume);
+static struct audio_device *adev = NULL;
+static pthread_mutex_t adev_init_lock;
+static unsigned int audio_device_ref_count;
+
+__attribute__ ((visibility ("default")))
+bool audio_hw_send_gain_dep_calibration(int level) {
+ bool ret_val = false;
+ ALOGV("%s: enter ... ", __func__);
+
+ pthread_mutex_lock(&adev_init_lock);
+
+ if (adev != NULL && adev->platform != NULL) {
+ pthread_mutex_lock(&adev->lock);
+ ret_val = platform_send_gain_dep_cal(adev->platform, level);
+ pthread_mutex_unlock(&adev->lock);
+ } else {
+ ALOGE("%s: %s is NULL", __func__, adev == NULL ? "adev" : "adev->platform");
+ }
+
+ pthread_mutex_unlock(&adev_init_lock);
+
+ ALOGV("%s: exit with ret_val %d ", __func__, ret_val);
+ return ret_val;
+}
static bool is_supported_format(audio_format_t format)
{
@@ -260,6 +291,9 @@
int enable_snd_device(struct audio_device *adev,
snd_device_t snd_device)
{
+ int i, num_devices = 0;
+ snd_device_t new_snd_devices[2];
+
if (snd_device < SND_DEVICE_MIN ||
snd_device >= SND_DEVICE_MAX) {
ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -273,14 +307,43 @@
return 0;
}
+ /* due to the possibility of calibration overwrite between listen
+ and audio, notify sound trigger hal before audio calibration is sent */
+ audio_extn_sound_trigger_update_device_status(snd_device,
+ ST_EVENT_SND_DEVICE_BUSY);
+
+ if (audio_extn_spkr_prot_is_enabled())
+ audio_extn_spkr_prot_calib_cancel(adev);
+
if (platform_send_audio_calibration(adev->platform, snd_device) < 0) {
adev->snd_dev_ref_cnt[snd_device]--;
+ audio_extn_sound_trigger_update_device_status(snd_device,
+ ST_EVENT_SND_DEVICE_FREE);
return -EINVAL;
}
- const char * dev_path = platform_get_snd_device_name(snd_device);
- ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
- audio_route_apply_and_update_path(adev->audio_route, dev_path);
+ audio_extn_dsm_feedback_enable(adev, snd_device, true);
+
+ if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
+ audio_extn_spkr_prot_is_enabled()) {
+ if (audio_extn_spkr_prot_get_acdb_id(snd_device) < 0) {
+ adev->snd_dev_ref_cnt[snd_device]--;
+ return -EINVAL;
+ }
+ if (audio_extn_spkr_prot_start_processing(snd_device)) {
+ ALOGE("%s: spkr_start_processing failed", __func__);
+ return -EINVAL;
+ }
+ } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+ for (i = 0; i < num_devices; i++) {
+ enable_snd_device(adev, new_snd_devices[i]);
+ }
+ } else {
+ const char * dev_path = platform_get_snd_device_name(snd_device);
+ ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
+ audio_route_apply_and_update_path(adev->audio_route, dev_path);
+ }
return 0;
}
@@ -288,6 +351,9 @@
int disable_snd_device(struct audio_device *adev,
snd_device_t snd_device)
{
+ int i, num_devices = 0;
+ snd_device_t new_snd_devices[2];
+
if (snd_device < SND_DEVICE_MIN ||
snd_device >= SND_DEVICE_MAX) {
ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -301,14 +367,28 @@
if (adev->snd_dev_ref_cnt[snd_device] == 0) {
const char * dev_path = platform_get_snd_device_name(snd_device);
ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
- audio_route_reset_and_update_path(adev->audio_route, dev_path);
+
+ audio_extn_dsm_feedback_enable(adev, snd_device, false);
+ if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
+ audio_extn_spkr_prot_is_enabled()) {
+ audio_extn_spkr_prot_stop_processing(snd_device);
+ } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+ for (i = 0; i < num_devices; i++) {
+ disable_snd_device(adev, new_snd_devices[i]);
+ }
+ } else {
+ audio_route_reset_and_update_path(adev->audio_route, dev_path);
+ }
+ audio_extn_sound_trigger_update_device_status(snd_device,
+ ST_EVENT_SND_DEVICE_FREE);
}
return 0;
}
-static void check_usecases_codec_backend(struct audio_device *adev,
- struct audio_usecase *uc_info,
- snd_device_t snd_device)
+static void check_and_route_playback_usecases(struct audio_device *adev,
+ struct audio_usecase *uc_info,
+ snd_device_t snd_device)
{
struct listnode *node;
struct audio_usecase *usecase;
@@ -335,7 +415,8 @@
if (usecase->type != PCM_CAPTURE &&
usecase != uc_info &&
usecase->out_snd_device != snd_device &&
- usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+ usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND &&
+ platform_check_backends_match(snd_device, usecase->out_snd_device)) {
ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
__func__, use_case_table[usecase->id],
platform_get_snd_device_name(usecase->out_snd_device));
@@ -399,7 +480,8 @@
usecase = node_to_item(node, struct audio_usecase, list);
if (usecase->type != PCM_PLAYBACK &&
usecase != uc_info &&
- usecase->in_snd_device != snd_device) {
+ usecase->in_snd_device != snd_device &&
+ (usecase->id != USECASE_AUDIO_SPKR_CALIB_TX)) {
ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
__func__, use_case_table[usecase->id],
platform_get_snd_device_name(usecase->in_snd_device));
@@ -524,7 +606,7 @@
* so that it would not result any device switch. All the usecases will
* be switched to new device when select_devices() is called for voice call
* usecase. This is to avoid switching devices for voice call when
- * check_usecases_codec_backend() is called below.
+ * check_and_route_playback_usecases() is called below.
*/
if (voice_is_in_call(adev)) {
vc_usecase = get_usecase_from_list(adev,
@@ -619,7 +701,7 @@
/* 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_and_route_playback_usecases(adev, usecase, out_snd_device);
enable_snd_device(adev, out_snd_device);
}
@@ -1308,7 +1390,7 @@
/*
* select_devices() call below switches all the usecases on the same
- * backend to the new device. Refer to check_usecases_codec_backend() in
+ * backend to the new device. Refer to check_and_route_playback_usecases() in
* the select_devices(). But how do we undo this?
*
* For example, music playback is active on headset (deep-buffer usecase)
@@ -1446,6 +1528,23 @@
return -ENOSYS;
}
+#ifdef NO_AUDIO_OUT
+static ssize_t out_write_for_no_output(struct audio_stream_out *stream,
+ const void *buffer, size_t bytes)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ /* No Output device supported other than BT for playback.
+ * Sleep for the amount of buffer duration
+ */
+ pthread_mutex_lock(&out->lock);
+ usleep(bytes * 1000000 / audio_stream_frame_size(&out->stream.common) /
+ out_get_sample_rate(&out->stream.common));
+ pthread_mutex_unlock(&out->lock);
+ return bytes;
+}
+#endif
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
@@ -1506,7 +1605,7 @@
if (ret != 0) {
if (out->pcm)
- ALOGE("%s: error %d - %s", __func__, ret, pcm_get_error(out->pcm));
+ ALOGE("%s: error %zu - %s", __func__, ret, pcm_get_error(out->pcm));
out_standby(&out->stream.common);
usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
out_get_sample_rate(&out->stream.common));
@@ -1573,7 +1672,7 @@
}
} else {
if (out->pcm) {
- size_t avail;
+ unsigned int avail;
if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
int64_t signed_frames = out->written - kernel_buffer_size + avail;
@@ -1716,6 +1815,13 @@
int status = 0;
ALOGV("%s: enter", __func__);
pthread_mutex_lock(&in->lock);
+
+ if (!in->standby && in->is_st_session) {
+ ALOGD("%s: sound trigger pcm stop lab", __func__);
+ audio_extn_sound_trigger_stop_lab(in);
+ in->standby = true;
+ }
+
if (!in->standby) {
pthread_mutex_lock(&adev->lock);
in->standby = true;
@@ -1802,6 +1908,14 @@
int i, ret = -1;
pthread_mutex_lock(&in->lock);
+ if (in->is_st_session) {
+ ALOGVV(" %s: reading on st session bytes=%d", __func__, bytes);
+ /* Read from sound trigger HAL */
+ audio_extn_sound_trigger_read(in, buffer, bytes);
+ pthread_mutex_unlock(&in->lock);
+ return bytes;
+ }
+
if (in->standby) {
pthread_mutex_lock(&adev->lock);
ret = start_input_stream(in);
@@ -2030,6 +2144,9 @@
if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
out->config = pcm_config_deep_buffer;
+ } else if (out->flags & AUDIO_OUTPUT_FLAG_TTS) {
+ out->usecase = USECASE_AUDIO_PLAYBACK_TTS;
+ out->config = pcm_config_deep_buffer;
} else {
out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
out->config = pcm_config_low_latency;
@@ -2088,7 +2205,11 @@
out->stream.common.remove_audio_effect = out_remove_audio_effect;
out->stream.get_latency = out_get_latency;
out->stream.set_volume = out_set_volume;
+#ifdef NO_AUDIO_OUT
+ out->stream.write = out_write_for_no_output;
+#else
out->stream.write = out_write;
+#endif
out->stream.get_render_position = out_get_render_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
out->stream.get_presentation_position = out_get_presentation_position;
@@ -2161,9 +2282,7 @@
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
if (ret >= 0) {
- /* When set to false, HAL should disable EC and NS
- * But it is currently not supported.
- */
+ /* When set to false, HAL should disable EC and NS */
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
adev->bluetooth_nrec = true;
else
@@ -2197,19 +2316,7 @@
status = -EINVAL;
}
if (status == 0) {
- if (adev->speaker_lr_swap != reverse_speakers) {
- adev->speaker_lr_swap = reverse_speakers;
- // only update the selected device if there is active pcm playback
- struct audio_usecase *usecase;
- struct listnode *node;
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == PCM_PLAYBACK) {
- select_devices(adev, usecase->id);
- break;
- }
- }
- }
+ platform_swap_lr_channels(adev, reverse_speakers);
}
}
@@ -2337,7 +2444,7 @@
}
static int adev_open_input_stream(struct audio_hw_device *dev,
- audio_io_handle_t handle __unused,
+ audio_io_handle_t handle,
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_in **stream_in,
@@ -2381,6 +2488,7 @@
in->dev = adev;
in->standby = 1;
in->channel_mask = config->channel_mask;
+ in->capture_handle = handle;
/* Update config params with the requested sample rate and channels */
if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
@@ -2423,6 +2531,9 @@
in->config.channels = channel_count;
in->config.rate = config->sample_rate;
+ /* This stream could be for sound trigger lab,
+ get sound trigger pcm if present */
+ audio_extn_sound_trigger_check_and_get_session(in);
*stream_in = &in->stream;
ALOGV("%s: exit", __func__);
@@ -2588,14 +2699,25 @@
{
size_t i;
struct audio_device *adev = (struct audio_device *)device;
- audio_route_free(adev->audio_route);
- free(adev->snd_dev_ref_cnt);
- platform_deinit(adev->platform);
- audio_extn_extspk_deinit(adev->extspk);
- for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
- pcm_params_free(adev->use_case_table[i]);
+
+ if (!adev)
+ return 0;
+
+ pthread_mutex_lock(&adev_init_lock);
+
+ if ((--audio_device_ref_count) == 0) {
+ audio_route_free(adev->audio_route);
+ free(adev->snd_dev_ref_cnt);
+ platform_deinit(adev->platform);
+ audio_extn_extspk_deinit(adev->extspk);
+ audio_extn_sound_trigger_deinit(adev);
+ for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
+ pcm_params_free(adev->use_case_table[i]);
+ }
+ free(device);
}
- free(device);
+
+ pthread_mutex_unlock(&adev_init_lock);
return 0;
}
@@ -2606,7 +2728,11 @@
static int period_size_is_plausible_for_low_latency(int period_size)
{
switch (period_size) {
+ case 48:
+ case 96:
+ case 144:
case 160:
+ case 192:
case 240:
case 320:
case 480:
@@ -2619,12 +2745,19 @@
static int adev_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
- struct audio_device *adev;
int i, ret;
ALOGD("%s: enter", __func__);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
-
+ pthread_mutex_lock(&adev_init_lock);
+ if (audio_device_ref_count != 0) {
+ *device = &adev->device.common;
+ audio_device_ref_count++;
+ ALOGV("%s: returning existing instance of adev", __func__);
+ ALOGV("%s: exit", __func__);
+ pthread_mutex_unlock(&adev_init_lock);
+ return 0;
+ }
adev = calloc(1, sizeof(struct audio_device));
pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
@@ -2672,10 +2805,12 @@
free(adev);
ALOGE("%s: Failed to init platform data, aborting.", __func__);
*device = NULL;
+ pthread_mutex_unlock(&adev_init_lock);
return -EINVAL;
}
adev->extspk = audio_extn_extspk_init(adev);
+ audio_extn_sound_trigger_init(adev);
if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
@@ -2713,6 +2848,7 @@
adev->enable_voicerx = false;
*device = &adev->device.common;
+
if (k_enable_extended_precision)
adev_verify_devices(adev);
@@ -2734,6 +2870,9 @@
}
}
+ audio_device_ref_count++;
+ pthread_mutex_unlock(&adev_init_lock);
+
ALOGV("%s: exit", __func__);
return 0;
}
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 1888aa1..b4b2583 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -57,6 +57,7 @@
USECASE_AUDIO_PLAYBACK_LOW_LATENCY,
USECASE_AUDIO_PLAYBACK_MULTI_CH,
USECASE_AUDIO_PLAYBACK_OFFLOAD,
+ USECASE_AUDIO_PLAYBACK_TTS,
/* HFP Use case*/
USECASE_AUDIO_HFP_SCO,
@@ -77,8 +78,12 @@
USECASE_INCALL_REC_DOWNLINK,
USECASE_INCALL_REC_UPLINK_AND_DOWNLINK,
+ USECASE_AUDIO_SPKR_CALIB_RX,
+ USECASE_AUDIO_SPKR_CALIB_TX,
+
USECASE_AUDIO_PLAYBACK_AFE_PROXY,
USECASE_AUDIO_RECORD_AFE_PROXY,
+ USECASE_AUDIO_DSM_FEEDBACK,
AUDIO_USECASE_MAX
};
@@ -167,6 +172,10 @@
bool enable_aec;
bool enable_ns;
+ audio_io_handle_t capture_handle;
+ bool is_st_session;
+ bool is_st_session_active;
+
struct audio_device *dev;
};
@@ -207,7 +216,6 @@
struct listnode usecase_list;
struct audio_route *audio_route;
int acdb_settings;
- bool speaker_lr_swap;
struct voice voice;
unsigned int cur_hdmi_channels;
bool bt_wb_speech_enabled;
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 4eaf488..2f596ba 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -81,6 +81,7 @@
bool fluence_in_voice_call;
bool fluence_in_voice_rec;
int dualmic_config;
+ bool speaker_lr_swap;
void *acdb_handle;
acdb_init_t acdb_init;
@@ -416,6 +417,12 @@
return -ENODEV;
}
+int platform_get_snd_device_acdb_id(snd_device_t snd_device __unused)
+{
+ ALOGE("%s: Not implemented", __func__);
+ return -ENOSYS;
+}
+
int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
{
struct platform_data *my_data = (struct platform_data *)platform;
@@ -655,7 +662,7 @@
devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
snd_device = SND_DEVICE_OUT_HEADPHONES;
} else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
- if (adev->speaker_lr_swap)
+ if (my_data->speaker_lr_swap)
snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
else
snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -951,6 +958,13 @@
return -ENOSYS;
}
+int platform_set_parameters(void *platform __unused,
+ struct str_parms *parms __unused)
+{
+ ALOGE("%s: Not implemented", __func__);
+ return -ENOSYS;
+}
+
/* Delay in Us */
int64_t platform_render_latency(audio_usecase_t usecase)
{
@@ -995,12 +1009,64 @@
}
int platform_set_snd_device_backend(snd_device_t device __unused,
- const char *backend __unused)
+ const char *backend __unused,
+ const char *hw_interface __unused)
{
return -ENOSYS;
}
-void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
+void platform_set_echo_reference(struct audio_device *adev __unused,
+ bool enable __unused,
+ audio_devices_t out_device __unused)
{
return;
}
+
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update the selected device if there is active pcm playback
+ struct audio_usecase *usecase;
+ struct listnode *node;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ int status = 0;
+
+ if (my_data->speaker_lr_swap != swap_channels) {
+ my_data->speaker_lr_swap = swap_channels;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type == PCM_PLAYBACK &&
+ usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ const char *mixer_path;
+ if (swap_channels) {
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+ } else {
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+ }
+ break;
+ }
+ }
+ }
+ return status;
+}
+
+bool platform_send_gain_dep_cal(void *platform __unused,
+ int level __unused)
+{
+ return 0;
+}
+
+bool platform_can_split_snd_device(snd_device_t in_snd_device __unused,
+ int *num_devices __unused,
+ snd_device_t *out_snd_devices __unused)
+{
+ return false;
+}
+
+bool platform_check_backends_match(snd_device_t snd_device1 __unused,
+ snd_device_t snd_device2 __unused)
+{
+ return true;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 7ba0bf0..03aa596 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -26,10 +26,13 @@
#include <audio_hw.h>
#include <platform_api.h>
#include "platform.h"
+#include "audio_extn.h"
+#include <linux/msm_audio.h>
#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
#define LIB_ACDB_LOADER "libacdbloader.so"
#define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID"
+#define CVD_VERSION_MIXER_CTL "CVD Version"
#define DUALMIC_CONFIG_NONE 0 /* Target does not contain 2 mics */
#define DUALMIC_CONFIG_ENDFIRE 1
@@ -45,6 +48,8 @@
#define MAX_SAD_BLOCKS 10
#define SAD_BLOCK_SIZE 3
+#define MAX_CVD_VERSION_STRING_SIZE 100
+
/* EDID format ID for LPCM audio */
#define EDID_FORMAT_LPCM 1
@@ -53,22 +58,31 @@
#define RETRY_US 500000
#define MAX_SND_CARD 8
+#define MAX_SND_CARD_NAME_LEN 31
+
+#define DEFAULT_APP_TYPE_RX_PATH 0x11130
+
struct audio_block_header
{
int reserved;
int length;
};
+enum {
+ CAL_MODE_SEND = 0x1,
+ CAL_MODE_PERSIST = 0x2,
+ CAL_MODE_RTAC = 0x4
+};
+
/* Audio calibration related functions */
typedef void (*acdb_deallocate_t)();
-#ifdef PLATFORM_MSM8084
-typedef int (*acdb_init_t)(char *);
-#else
+typedef int (*acdb_init_v2_cvd_t)(char *, char *);
+typedef int (*acdb_init_v2_t)(char *);
typedef int (*acdb_init_t)();
-#endif
typedef void (*acdb_send_audio_cal_t)(int, int);
typedef void (*acdb_send_voice_cal_t)(int, int);
typedef int (*acdb_reload_vocvoltable_t)(int);
+typedef int (*acdb_send_gain_dep_cal_t)(int, int, int, int, int);
/* Audio calibration related functions */
struct platform_data {
@@ -78,16 +92,18 @@
bool fluence_in_voice_comm;
bool fluence_in_voice_rec;
int dualmic_config;
+ bool speaker_lr_swap;
+
void *acdb_handle;
- acdb_init_t acdb_init;
acdb_deallocate_t acdb_deallocate;
acdb_send_audio_cal_t acdb_send_audio_cal;
acdb_send_voice_cal_t acdb_send_voice_cal;
acdb_reload_vocvoltable_t acdb_reload_vocvoltable;
+ acdb_send_gain_dep_cal_t acdb_send_gain_dep_cal;
struct csd_data *csd;
- bool ext_speaker;
- bool ext_earpiece;
char ec_ref_mixer_path[64];
+
+ char *snd_card_name;
};
static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -99,6 +115,8 @@
MULTIMEDIA2_PCM_DEVICE},
[USECASE_AUDIO_PLAYBACK_OFFLOAD] = {PLAYBACK_OFFLOAD_DEVICE,
PLAYBACK_OFFLOAD_DEVICE},
+ [USECASE_AUDIO_PLAYBACK_TTS] = {MULTIMEDIA3_PCM_DEVICE,
+ MULTIMEDIA3_PCM_DEVICE},
[USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE,
AUDIO_RECORD_PCM_DEVICE},
[USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
@@ -117,10 +135,14 @@
AUDIO_RECORD_PCM_DEVICE},
[USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX},
+ [USECASE_AUDIO_SPKR_CALIB_RX] = {SPKR_PROT_CALIB_RX_PCM_DEVICE, -1},
+ [USECASE_AUDIO_SPKR_CALIB_TX] = {-1, SPKR_PROT_CALIB_TX_PCM_DEVICE},
+
[USECASE_AUDIO_PLAYBACK_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
AFE_PROXY_RECORD_PCM_DEVICE},
[USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
AFE_PROXY_RECORD_PCM_DEVICE},
+ [USECASE_AUDIO_DSM_FEEDBACK] = {QUAT_MI2S_PCM_DEVICE, QUAT_MI2S_PCM_DEVICE},
};
@@ -135,7 +157,9 @@
[SND_DEVICE_OUT_HEADPHONES] = "headphones",
[SND_DEVICE_OUT_LINE] = "line",
[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
+ [SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = "speaker-safe-and-headphones",
[SND_DEVICE_OUT_SPEAKER_AND_LINE] = "speaker-and-line",
+ [SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE] = "speaker-safe-and-line",
[SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset",
[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = "voice-hac-handset",
[SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker",
@@ -150,6 +174,8 @@
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
[SND_DEVICE_OUT_VOICE_TX] = "voice-tx",
+ [SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected",
+ [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
/* Capture sound devices */
[SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -177,8 +203,9 @@
[SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic",
[SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic",
+ [SND_DEVICE_IN_BT_SCO_MIC_NREC] = "bt-sco-mic",
[SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb",
-
+ [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = "bt-sco-mic-wb",
[SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
[SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef",
@@ -196,6 +223,8 @@
[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
[SND_DEVICE_IN_VOICE_RX] = "voice-rx",
+
+ [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = "vi-feedback",
};
/* ACDB IDs (audio DSP path configuration IDs) for each sound device */
@@ -208,7 +237,9 @@
[SND_DEVICE_OUT_HEADPHONES] = 10,
[SND_DEVICE_OUT_LINE] = 77,
[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
+ [SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = 10,
[SND_DEVICE_OUT_SPEAKER_AND_LINE] = 77,
+ [SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE] = 77,
[SND_DEVICE_OUT_VOICE_HANDSET] = ACDB_ID_VOICE_HANDSET,
[SND_DEVICE_OUT_VOICE_SPEAKER] = ACDB_ID_VOICE_SPEAKER,
[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = 53,
@@ -223,6 +254,8 @@
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
[SND_DEVICE_OUT_VOICE_TX] = 45,
+ [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124,
+ [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101,
[SND_DEVICE_IN_HANDSET_MIC] = 4,
[SND_DEVICE_IN_HANDSET_MIC_AEC] = 106,
@@ -249,8 +282,9 @@
[SND_DEVICE_IN_HDMI_MIC] = 4,
[SND_DEVICE_IN_BT_SCO_MIC] = 21,
+ [SND_DEVICE_IN_BT_SCO_MIC_NREC] = 21,
[SND_DEVICE_IN_BT_SCO_MIC_WB] = 38,
-
+ [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = 38,
[SND_DEVICE_IN_CAMCORDER_MIC] = 61,
[SND_DEVICE_IN_VOICE_DMIC] = 41,
@@ -268,6 +302,8 @@
[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 43,
[SND_DEVICE_IN_VOICE_RX] = 44,
+
+ [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102,
};
struct name_to_index {
@@ -287,7 +323,9 @@
{TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_LINE)},
{TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_LINE)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)},
@@ -303,6 +341,8 @@
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
/* in */
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)},
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)},
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)},
@@ -324,11 +364,13 @@
{TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_STEREO)},
{TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC_AEC)},
{TO_NAME_INDEX(SND_DEVICE_IN_HDMI_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_NREC)},
{TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)},
-
+ {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)},
{TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)},
@@ -344,9 +386,12 @@
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC_NS)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)},
+
+ {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)},
};
-static char * backend_table[SND_DEVICE_MAX] = {0};
+static char * backend_tag_table[SND_DEVICE_MAX] = {0};
+static char * hw_interface_table[SND_DEVICE_MAX] = {0};
static const struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = {
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)},
@@ -408,6 +453,60 @@
return is_tmus;
}
+bool platform_send_gain_dep_cal(void *platform, int level)
+{
+ bool ret_val = false;
+ struct platform_data *my_data = (struct platform_data *)platform;
+ struct audio_device *adev = my_data->adev;
+ int acdb_dev_id, app_type;
+ int acdb_dev_type = MSM_SNDDEV_CAP_RX;
+ int mode = CAL_MODE_RTAC;
+ struct listnode *node;
+ struct audio_usecase *usecase;
+
+ if (my_data->acdb_send_gain_dep_cal == NULL) {
+ ALOGE("%s: dlsym error for acdb_send_gain_dep_cal", __func__);
+ return ret_val;
+ }
+
+ if (!voice_is_in_call(adev)) {
+ ALOGV("%s: Not Voice call usecase, apply new cal for level %d",
+ __func__, level);
+ app_type = DEFAULT_APP_TYPE_RX_PATH;
+
+ // find the current active sound device
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+
+ if (usecase != NULL &&
+ usecase->type == PCM_PLAYBACK &&
+ (usecase->stream.out->devices == AUDIO_DEVICE_OUT_SPEAKER)) {
+
+ ALOGV("%s: out device is %d", __func__, usecase->out_snd_device);
+ if (audio_extn_spkr_prot_is_enabled()) {
+ acdb_dev_id = audio_extn_spkr_prot_get_acdb_id(usecase->out_snd_device);
+ } else {
+ acdb_dev_id = acdb_device_table[usecase->out_snd_device];
+ }
+
+ if (!my_data->acdb_send_gain_dep_cal(acdb_dev_id, app_type,
+ acdb_dev_type, mode, level)) {
+ // set ret_val true if at least one calibration is set successfully
+ ret_val = true;
+ } else {
+ ALOGE("%s: my_data->acdb_send_gain_dep_cal failed ", __func__);
+ }
+ } else {
+ ALOGW("%s: Usecase list is empty", __func__);
+ }
+ }
+ } else {
+ ALOGW("%s: Voice call in progress .. ignore setting new cal",
+ __func__);
+ }
+ return ret_val;
+}
+
void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
{
struct platform_data *my_data = (struct platform_data *)adev->platform;
@@ -613,42 +712,79 @@
#endif
}
-static void set_platform_defaults(struct platform_data * my_data)
+static void set_platform_defaults(struct platform_data * my_data __unused)
{
int32_t dev;
for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
- backend_table[dev] = NULL;
+ backend_tag_table[dev] = NULL;
+ hw_interface_table[dev] = NULL;
}
- // TBD - do these go to the platform-info.xml file.
- // will help in avoiding strdups here
- backend_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
- backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
- backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
- backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
- backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
- backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
- backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
- backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+ // To overwrite these go to the audio_platform_info.xml file.
+ backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
+ backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
+ backend_tag_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+ backend_tag_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
+ backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
+ backend_tag_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+ backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+ backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+ backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
+ backend_tag_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
- if (my_data->ext_speaker) {
- backend_table[SND_DEVICE_OUT_SPEAKER] = strdup("speaker");
- backend_table[SND_DEVICE_OUT_SPEAKER_SAFE] = strdup("speaker");
- backend_table[SND_DEVICE_OUT_VOICE_SPEAKER] = strdup("speaker");
- backend_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("speaker");
- backend_table[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] =
- strdup("speaker-and-headphones");
- backend_table[SND_DEVICE_OUT_SPEAKER_AND_LINE] =
- strdup("speaker-and-line");
+ hw_interface_table[SND_DEVICE_OUT_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_LINE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_LINE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_LINE] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_HDMI] = strdup("HDMI_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("SLIMBUS_0_RX-and-HDMI_RX");
+ hw_interface_table[SND_DEVICE_OUT_BT_SCO] = strdup("SEC_AUX_PCM_RX");
+ hw_interface_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("SEC_AUX_PCM_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+}
+
+void get_cvd_version(char *cvd_version, struct audio_device *adev)
+{
+ struct mixer_ctl *ctl;
+ int count;
+ int ret = 0;
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, CVD_VERSION_MIXER_CTL);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, CVD_VERSION_MIXER_CTL);
+ goto done;
+ }
+ mixer_ctl_update(ctl);
+
+ count = mixer_ctl_get_num_values(ctl);
+ if (count > MAX_CVD_VERSION_STRING_SIZE)
+ count = MAX_CVD_VERSION_STRING_SIZE - 1;
+
+ ret = mixer_ctl_get_array(ctl, cvd_version, count);
+ if (ret != 0) {
+ ALOGE("%s: ERROR! mixer_ctl_get_array() failed to get CVD Version", __func__);
+ goto done;
}
- if (my_data->ext_earpiece) {
- backend_table[SND_DEVICE_OUT_VOICE_HANDSET] = strdup("handset");
- backend_table[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = strdup("handset");
- backend_table[SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = strdup("handset");
- backend_table[SND_DEVICE_OUT_HANDSET] = strdup("handset");
- backend_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("handset");
- }
+done:
+ return;
}
void *platform_init(struct audio_device *adev)
@@ -657,6 +793,16 @@
struct platform_data *my_data;
int retry_num = 0, snd_card_num = 0;
const char *snd_card_name;
+ char *cvd_version = NULL;
+
+ my_data = calloc(1, sizeof(struct platform_data));
+
+ my_data->adev = adev;
+
+ set_platform_defaults(my_data);
+
+ /* Initialize platform specific ids and/or backends*/
+ platform_info_init(my_data);
while (snd_card_num < MAX_SND_CARD) {
adev->mixer = mixer_open(snd_card_num);
@@ -676,12 +822,23 @@
}
snd_card_name = mixer_get_name(adev->mixer);
+
+ /* validate the sound card name */
+ if (my_data->snd_card_name != NULL &&
+ strncmp(snd_card_name, my_data->snd_card_name, MAX_SND_CARD_NAME_LEN) != 0) {
+ ALOGI("%s: found valid sound card %s, but not primary sound card %s",
+ __func__, snd_card_name, my_data->snd_card_name);
+ retry_num = 0;
+ snd_card_num++;
+ continue;
+ }
+
ALOGD("%s: snd_card_name: %s", __func__, snd_card_name);
adev->audio_route = audio_route_init(snd_card_num, MIXER_XML_PATH);
if (!adev->audio_route) {
ALOGE("%s: Failed to init audio route controls, aborting.", __func__);
- return NULL;
+ goto init_failed;
}
adev->snd_card = snd_card_num;
ALOGD("%s: Opened sound card:%d", __func__, snd_card_num);
@@ -690,32 +847,15 @@
if (snd_card_num >= MAX_SND_CARD) {
ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
- return NULL;
+ goto init_failed;
}
- my_data = calloc(1, sizeof(struct platform_data));
-
- my_data->adev = adev;
my_data->dualmic_config = DUALMIC_CONFIG_NONE;
my_data->fluence_in_spkr_mode = false;
my_data->fluence_in_voice_call = false;
my_data->fluence_in_voice_comm = false;
my_data->fluence_in_voice_rec = false;
- /*
- * The default assumption is that earpiece (handset), speaker and headphones
- * devices are connected to internal HW codec and communicated through
- * slimbus backend. If any platform communicates with speaker or earpiece
- * or headphones through non-slimbus backend such as MI2S or AUXPCM etc.,
- * the ext_xxxx flags must be set accordingly.
- */
- if (strstr(snd_card_name, "tfa9890_stereo")) {
- my_data->ext_speaker = true;
- my_data->ext_earpiece = true;
- } else if (strstr(snd_card_name, "tfa9890")) {
- my_data->ext_speaker = true;
- }
-
property_get("persist.audio.dualmic.config",value,"");
if (!strcmp("broadside", value)) {
ALOGE("%s: Unsupported dualmic configuration", __func__);
@@ -773,39 +913,83 @@
if (!my_data->acdb_reload_vocvoltable)
ALOGE("%s: Could not find the symbol acdb_loader_reload_vocvoltable from %s",
__func__, LIB_ACDB_LOADER);
-#ifdef PLATFORM_MSM8084
- my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
- "acdb_loader_init_v2");
- if (my_data->acdb_init == NULL)
+
+ my_data->acdb_send_gain_dep_cal = (acdb_send_gain_dep_cal_t)dlsym(my_data->acdb_handle,
+ "acdb_loader_send_gain_dep_cal");
+ if (!my_data->acdb_send_gain_dep_cal)
+ ALOGV("%s: Could not find the symbol acdb_loader_send_gain_dep_cal from %s",
+ __func__, LIB_ACDB_LOADER);
+
+#if defined (PLATFORM_MSM8994)
+ acdb_init_v2_cvd_t acdb_init;
+ acdb_init = (acdb_init_v2_cvd_t)dlsym(my_data->acdb_handle,
+ "acdb_loader_init_v2");
+ if (acdb_init == NULL) {
ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
+ goto acdb_init_fail;
+ }
+
+ cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
+ get_cvd_version(cvd_version, adev);
+ if (!cvd_version)
+ ALOGE("failed to allocate cvd_version");
else
- my_data->acdb_init((char *)snd_card_name);
+ acdb_init((char *)snd_card_name, cvd_version);
+ free(cvd_version);
+#elif defined (PLATFORM_MSM8084)
+ acdb_init_v2_t acdb_init;
+ acdb_init = (acdb_init_v2_t)dlsym(my_data->acdb_handle,
+ "acdb_loader_init_v2");
+ if (acdb_init == NULL) {
+ ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
+ goto acdb_init_fail;
+ }
+ acdb_init((char *)snd_card_name);
#else
- my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
+ acdb_init_t acdb_init;
+ acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
"acdb_loader_init_ACDB");
- if (my_data->acdb_init == NULL)
+ if (acdb_init == NULL)
ALOGE("%s: dlsym error %s for acdb_loader_init_ACDB", __func__, dlerror());
else
- my_data->acdb_init();
+ acdb_init();
#endif
-
}
- set_platform_defaults(my_data);
+acdb_init_fail:
- /* Initialize platform specific ids and/or backends*/
- platform_info_init();
+ audio_extn_spkr_prot_init(adev);
+
+ audio_extn_hwdep_cal_send(adev->snd_card, my_data->acdb_handle);
/* load csd client */
platform_csd_init(my_data);
return my_data;
+
+init_failed:
+ if (my_data)
+ free(my_data);
+ return NULL;
}
void platform_deinit(void *platform)
{
+ int32_t dev;
+
struct platform_data *my_data = (struct platform_data *)platform;
close_csd_client(my_data->csd);
+
+ for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+ if (backend_tag_table[dev])
+ free(backend_tag_table[dev]);
+ if (hw_interface_table[dev])
+ free(hw_interface_table[dev]);
+ }
+
+ if (my_data->snd_card_name)
+ free(my_data->snd_card_name);
+
free(platform);
}
@@ -827,7 +1011,7 @@
return;
}
- const char * suffix = backend_table[snd_device];
+ const char * suffix = backend_tag_table[snd_device];
if (suffix != NULL) {
strcat(mixer_path, " ");
@@ -835,6 +1019,36 @@
}
}
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2)
+{
+ bool result = true;
+
+ ALOGV("%s: snd_device1 = %s, snd_device2 = %s", __func__,
+ platform_get_snd_device_name(snd_device1),
+ platform_get_snd_device_name(snd_device2));
+
+ if ((snd_device1 < SND_DEVICE_MIN) || (snd_device1 >= SND_DEVICE_MAX)) {
+ ALOGE("%s: Invalid snd_device = %s", __func__,
+ platform_get_snd_device_name(snd_device1));
+ return false;
+ }
+ if ((snd_device2 < SND_DEVICE_MIN) || (snd_device2 >= SND_DEVICE_MAX)) {
+ ALOGE("%s: Invalid snd_device = %s", __func__,
+ platform_get_snd_device_name(snd_device2));
+ return false;
+ }
+ const char * be_itf1 = hw_interface_table[snd_device1];
+ const char * be_itf2 = hw_interface_table[snd_device2];
+
+ if (NULL != be_itf1 && NULL != be_itf2) {
+ if (0 != strcmp(be_itf1, be_itf2))
+ result = false;
+ }
+
+ ALOGV("%s: be_itf1 = %s, be_itf2 = %s, match %d", __func__, be_itf1, be_itf2, result);
+ return result;
+}
+
int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
{
int device_id;
@@ -897,17 +1111,28 @@
goto done;
}
+ ALOGV("%s: acdb_device_table[%s]: old = %d new = %d", __func__,
+ platform_get_snd_device_name(snd_device), acdb_device_table[snd_device], acdb_id);
acdb_device_table[snd_device] = acdb_id;
done:
return ret;
}
+int platform_get_snd_device_acdb_id(snd_device_t snd_device)
+{
+ if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+ ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+ return -EINVAL;
+ }
+ return acdb_device_table[snd_device];
+}
+
int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
{
struct platform_data *my_data = (struct platform_data *)platform;
int acdb_dev_id, acdb_dev_type;
- acdb_dev_id = acdb_device_table[snd_device];
+ acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)];
if (acdb_dev_id < 0) {
ALOGE("%s: Could not find acdb id for device(%d)",
__func__, snd_device);
@@ -954,7 +1179,11 @@
if (my_data->csd == NULL)
return ret;
- acdb_rx_id = acdb_device_table[out_snd_device];
+ if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+ audio_extn_spkr_prot_is_enabled())
+ acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_SPEAKER_PROTECTED];
+ else
+ acdb_rx_id = acdb_device_table[out_snd_device];
acdb_tx_id = acdb_device_table[in_snd_device];
@@ -982,6 +1211,10 @@
if (my_data->acdb_send_voice_cal == NULL) {
ALOGE("%s: dlsym error for acdb_send_voice_call", __func__);
} else {
+ if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+ audio_extn_spkr_prot_is_enabled())
+ out_snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
+
acdb_rx_id = acdb_device_table[out_snd_device];
acdb_tx_id = acdb_device_table[in_snd_device];
@@ -1006,7 +1239,11 @@
if (my_data->csd == NULL)
return ret;
- acdb_rx_id = acdb_device_table[out_snd_device];
+ if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+ audio_extn_spkr_prot_is_enabled())
+ acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED];
+ else
+ acdb_rx_id = acdb_device_table[out_snd_device];
acdb_tx_id = acdb_device_table[in_snd_device];
@@ -1174,6 +1411,49 @@
return ret;
}
+bool platform_can_split_snd_device(snd_device_t snd_device,
+ int *num_devices,
+ snd_device_t *new_snd_devices)
+{
+ bool status = false;
+
+ if (NULL == num_devices || NULL == new_snd_devices) {
+ ALOGE("%s: NULL pointer ..", __func__);
+ return false;
+ }
+
+ /*
+ * If wired headset/headphones/line devices share the same backend
+ * with speaker/earpiece this routine returns false.
+ */
+ if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+ status = true;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_LINE;
+ status = true;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_HEADPHONES)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+ new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+ status = true;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_LINE)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+ new_snd_devices[1] = SND_DEVICE_OUT_LINE;
+ status = true;
+ }
+ return status;
+}
+
snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
{
struct platform_data *my_data = (struct platform_data *)platform;
@@ -1232,14 +1512,21 @@
if (popcount(devices) == 2) {
if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
- AUDIO_DEVICE_OUT_SPEAKER)) {
- snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
- } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
- AUDIO_DEVICE_OUT_SPEAKER)) {
+ AUDIO_DEVICE_OUT_SPEAKER) ||
+ devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
} else if (devices == (AUDIO_DEVICE_OUT_LINE |
AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_LINE;
+ } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+ devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES;
+ } else if (devices == (AUDIO_DEVICE_OUT_LINE |
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE;
} else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
@@ -1265,7 +1552,7 @@
} else if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
snd_device = SND_DEVICE_OUT_SPEAKER_SAFE;
} else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
- if (adev->speaker_lr_swap)
+ if (my_data->speaker_lr_swap)
snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
else
snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -1343,9 +1630,15 @@
snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
} else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
if (adev->bt_wb_speech_enabled) {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
} else {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC;
}
} else if (out_device & AUDIO_DEVICE_OUT_SPEAKER ||
out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
@@ -1467,9 +1760,15 @@
snd_device = SND_DEVICE_IN_HEADSET_MIC;
} else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
if (adev->bt_wb_speech_enabled) {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
} else {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC;
}
} else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
@@ -1493,9 +1792,15 @@
snd_device = SND_DEVICE_IN_SPEAKER_MIC;
} else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
if (adev->bt_wb_speech_enabled) {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
} else {
- snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+ if (adev->bluetooth_nrec)
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+ else
+ snd_device = SND_DEVICE_IN_BT_SCO_MIC;
}
} else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
@@ -1685,6 +1990,37 @@
return ret;
}
+int platform_set_parameters(void *platform, struct str_parms *parms)
+{
+ struct platform_data *my_data = (struct platform_data *)platform;
+ char value[64];
+ char *kv_pairs = str_parms_to_str(parms);
+ int ret = 0, err;
+
+ if (kv_pairs == NULL) {
+ ret = -EINVAL;
+ ALOGE("%s: key-value pair is NULL",__func__);
+ goto done;
+ }
+
+ ALOGV("%s: enter: %s", __func__, kv_pairs);
+
+ err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME,
+ value, sizeof(value));
+ if (err >= 0) {
+ str_parms_del(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME);
+ my_data->snd_card_name = strdup(value);
+ ALOGV("%s: sound card name %s", __func__, my_data->snd_card_name);
+ }
+
+done:
+ ALOGV("%s: exit with code(%d)", __func__, ret);
+ if (kv_pairs != NULL)
+ free(kv_pairs);
+
+ return ret;
+}
+
/* Delay in Us */
int64_t platform_render_latency(audio_usecase_t usecase)
{
@@ -1698,7 +2034,8 @@
}
}
-int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
+ const char * hw_interface)
{
int ret = 0;
@@ -1709,10 +2046,20 @@
goto done;
}
- if (backend_table[device]) {
- free(backend_table[device]);
+ ALOGV("%s: backend_tag_table[%s]: old = %s new = %s", __func__,
+ platform_get_snd_device_name(device),
+ backend_tag_table[device] != NULL ? backend_tag_table[device]: "null", backend_tag);
+ if (backend_tag_table[device]) {
+ free(backend_tag_table[device]);
}
- backend_table[device] = strdup(backend);
+ backend_tag_table[device] = strdup(backend_tag);
+
+ if (hw_interface != NULL) {
+ if (hw_interface_table[device])
+ free(hw_interface_table[device]);
+ ALOGV("%s: hw_interface_table[%d] = %s", __func__, device, hw_interface);
+ hw_interface_table[device] = strdup(hw_interface);
+ }
done:
return ret;
}
@@ -1730,7 +2077,37 @@
ALOGE("%s: invalid usecase type", __func__);
ret = -EINVAL;
}
+ ALOGV("%s: pcm_device_table[%d][%d] = %d", __func__, usecase, type, pcm_id);
pcm_device_table[usecase][type] = pcm_id;
done:
return ret;
}
+
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update if there is active pcm playback on speaker
+ struct audio_usecase *usecase;
+ struct listnode *node;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ if (my_data->speaker_lr_swap != swap_channels) {
+ my_data->speaker_lr_swap = swap_channels;
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type == PCM_PLAYBACK &&
+ usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ const char *mixer_path;
+ if (swap_channels) {
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+ } else {
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index bb91061..f1c5239 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -45,7 +45,9 @@
SND_DEVICE_OUT_HEADPHONES,
SND_DEVICE_OUT_LINE,
SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+ SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES,
SND_DEVICE_OUT_SPEAKER_AND_LINE,
+ SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE,
SND_DEVICE_OUT_VOICE_HANDSET,
SND_DEVICE_OUT_VOICE_SPEAKER,
SND_DEVICE_OUT_VOICE_HEADPHONES,
@@ -60,6 +62,8 @@
SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
SND_DEVICE_OUT_VOICE_HAC_HANDSET,
SND_DEVICE_OUT_VOICE_TX,
+ SND_DEVICE_OUT_SPEAKER_PROTECTED,
+ SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
SND_DEVICE_OUT_END,
/*
@@ -93,8 +97,9 @@
SND_DEVICE_IN_HDMI_MIC,
SND_DEVICE_IN_BT_SCO_MIC,
+ SND_DEVICE_IN_BT_SCO_MIC_NREC,
SND_DEVICE_IN_BT_SCO_MIC_WB,
-
+ SND_DEVICE_IN_BT_SCO_MIC_WB_NREC,
SND_DEVICE_IN_CAMCORDER_MIC,
SND_DEVICE_IN_VOICE_DMIC,
@@ -113,6 +118,8 @@
SND_DEVICE_IN_VOICE_RX,
+ SND_DEVICE_IN_CAPTURE_VI_FEEDBACK,
+
SND_DEVICE_IN_END,
SND_DEVICE_MAX = SND_DEVICE_IN_END,
@@ -125,19 +132,11 @@
#define DEFAULT_MUTE_RAMP_DURATION_MS 20
#define DEFAULT_VOLUME_RAMP_DURATION_MS 20
-#ifdef PLATFORM_MSM8084
-#define ACDB_ID_VOICE_SPEAKER 66
-#define ACDB_ID_VOICE_HANDSET 67
-#define ACDB_ID_VOICE_HANDSET_TMUS 67
-#define ACDB_ID_VOICE_DMIC_EF_TMUS 89
-#define ACDB_ID_HEADSET_MIC_AEC 47
-#else
#define ACDB_ID_VOICE_SPEAKER 15
#define ACDB_ID_VOICE_HANDSET 7
#define ACDB_ID_VOICE_HANDSET_TMUS 88
#define ACDB_ID_VOICE_DMIC_EF_TMUS 89
#define ACDB_ID_HEADSET_MIC_AEC 8
-#endif
#define MAX_VOL_INDEX 5
#define MIN_VOL_INDEX 0
@@ -152,8 +151,11 @@
* We should take care of returning proper size when AudioFlinger queries for
* the buffer size of an input/output stream
*/
-#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960
-#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 8
+
+/* 1920 frames(40ms) at 2 buffers gives a good tradeoff between power and latency */
+#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 1920
+#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 2
+
#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240
#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
@@ -172,22 +174,22 @@
#define DEEP_BUFFER_PCM_DEVICE 0
#define AUDIO_RECORD_PCM_DEVICE 0
#define MULTIMEDIA2_PCM_DEVICE 1
+
+#define SPKR_PROT_CALIB_RX_PCM_DEVICE 5
+#define SPKR_PROT_CALIB_TX_PCM_DEVICE 25
+
+#define MULTIMEDIA3_PCM_DEVICE 4
+
+#define QUAT_MI2S_PCM_DEVICE 44
#define PLAYBACK_OFFLOAD_DEVICE 9
#define LOWLATENCY_PCM_DEVICE 15
#define VOICE_VSID 0x10C01000
-#ifdef PLATFORM_MSM8084
-#define VOICE_CALL_PCM_DEVICE 20
-#define VOICE2_CALL_PCM_DEVICE 25
-#define VOLTE_CALL_PCM_DEVICE 21
-#define QCHAT_CALL_PCM_DEVICE 33
-#define VOWLAN_CALL_PCM_DEVICE -1
-#else
+
#define VOICE_CALL_PCM_DEVICE 2
#define VOICE2_CALL_PCM_DEVICE 22
#define VOLTE_CALL_PCM_DEVICE 14
#define QCHAT_CALL_PCM_DEVICE 20
#define VOWLAN_CALL_PCM_DEVICE 36
-#endif
#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
#define AFE_PROXY_RECORD_PCM_DEVICE 8
@@ -204,6 +206,8 @@
#define LIB_CSD_CLIENT "libcsd-client.so"
#define LIB_MDM_DETECT "libmdmdetect.so"
+#define PLATFORM_CONFIG_KEY_SOUNDCARD_NAME "snd_card_name"
+
/* CSD-CLIENT related functions */
typedef int (*init_t)(bool);
typedef int (*deinit_t)();
diff --git a/hal/platform_api.h b/hal/platform_api.h
index e50e06d..61bb92f 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -22,9 +22,11 @@
const char *platform_get_snd_device_name(snd_device_t snd_device);
void platform_add_backend_name(void *platform, char *mixer_path,
snd_device_t snd_device);
+bool platform_send_gain_dep_cal(void *platform, int level);
int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
int platform_get_snd_device_index(char *snd_device_index_name);
int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id);
+int platform_get_snd_device_acdb_id(snd_device_t snd_device);
int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
int platform_switch_voice_call_device_pre(void *platform);
int platform_switch_voice_call_enable_device_config(void *platform,
@@ -56,13 +58,23 @@
int platform_start_incall_music_usecase(void *platform);
int platform_stop_incall_music_usecase(void *platform);
-int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend);
+int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend,
+ const char * hw_interface);
-/* From platform_info_parser.c */
-int platform_info_init(void);
+/* From platform_info.c */
+int platform_info_init(void *);
int platform_get_usecase_index(const char * usecase);
int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id);
void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device);
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels);
+
+bool platform_can_split_snd_device(snd_device_t in_snd_device,
+ int *num_devices,
+ snd_device_t *out_snd_devices);
+
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2);
+
+int platform_set_parameters(void *platform, struct str_parms *parms);
#endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 832c0f0..c0527b4 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -32,6 +32,7 @@
ACDB,
PCM_ID,
BACKEND_NAME,
+ CONFIG_PARAMS,
} section_t;
typedef void (* section_process_fn)(const XML_Char **attr);
@@ -39,6 +40,7 @@
static void process_acdb_id(const XML_Char **attr);
static void process_pcm_id(const XML_Char **attr);
static void process_backend_name(const XML_Char **attr);
+static void process_config_params(const XML_Char **attr);
static void process_root(const XML_Char **attr);
static section_process_fn section_table[] = {
@@ -46,10 +48,18 @@
[ACDB] = process_acdb_id,
[PCM_ID] = process_pcm_id,
[BACKEND_NAME] = process_backend_name,
+ [CONFIG_PARAMS] = process_config_params,
};
static section_t section;
+struct platform_info {
+ void *platform;
+ struct str_parms *kvpairs;
+};
+
+static struct platform_info my_data;
+
/*
* <audio_platform_info>
* <acdb_ids>
@@ -67,6 +77,12 @@
* ...
* ...
* </pcm_ids>
+ * <config_params>
+ * <param key="snd_card_name" value="msm8994-tomtom-mtp-snd-card"/>
+ * ...
+ * ...
+ * </config_params>
+ *
* </audio_platform_info>
*/
@@ -80,7 +96,7 @@
int index;
if (strcmp(attr[0], "name") != 0) {
- ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+ ALOGE("%s: 'name' not found, no pcm_id set!", __func__);
goto done;
}
@@ -128,6 +144,7 @@
static void process_backend_name(const XML_Char **attr)
{
int index;
+ char *hw_interface = NULL;
if (strcmp(attr[0], "name") != 0) {
ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
@@ -147,7 +164,15 @@
goto done;
}
- if (platform_set_snd_device_backend(index, attr[3]) < 0) {
+ if (attr[4] != NULL) {
+ if (strcmp(attr[4], "interface") != 0) {
+ hw_interface = NULL;
+ } else {
+ hw_interface = (char *)attr[5];
+ }
+ }
+
+ if (platform_set_snd_device_backend(index, attr[3], hw_interface) < 0) {
ALOGE("%s: Device %s in %s, backend %s was not set!",
__func__, attr[1], PLATFORM_INFO_XML_PATH, attr[3]);
goto done;
@@ -189,6 +214,24 @@
return;
}
+/* platform specific configuration key-value pairs */
+static void process_config_params(const XML_Char **attr)
+{
+ if (strcmp(attr[0], "key") != 0) {
+ ALOGE("%s: 'key' not found", __func__);
+ goto done;
+ }
+
+ if (strcmp(attr[2], "value") != 0) {
+ ALOGE("%s: 'value' not found", __func__);
+ goto done;
+ }
+
+ str_parms_add_str(my_data.kvpairs, (char*)attr[1], (char*)attr[3]);
+done:
+ return;
+}
+
static void start_tag(void *userdata __unused, const XML_Char *tag_name,
const XML_Char **attr)
{
@@ -202,6 +245,8 @@
section = PCM_ID;
} else if (strcmp(tag_name, "backend_names") == 0) {
section = BACKEND_NAME;
+ } else if (strcmp(tag_name, "config_params") == 0) {
+ section = CONFIG_PARAMS;
} else if (strcmp(tag_name, "device") == 0) {
if ((section != ACDB) && (section != BACKEND_NAME)) {
ALOGE("device tag only supported for acdb/backend names");
@@ -219,6 +264,14 @@
section_process_fn fn = section_table[PCM_ID];
fn(attr);
+ } else if (strcmp(tag_name, "param") == 0) {
+ if (section != CONFIG_PARAMS) {
+ ALOGE("param tag only supported with CONFIG_PARAMS section");
+ return;
+ }
+
+ section_process_fn fn = section_table[section];
+ fn(attr);
}
return;
@@ -232,10 +285,13 @@
section = ROOT;
} else if (strcmp(tag_name, "backend_names") == 0) {
section = ROOT;
+ } else if (strcmp(tag_name, "config_params") == 0) {
+ section = ROOT;
+ platform_set_parameters(my_data.platform, my_data.kvpairs);
}
}
-int platform_info_init(void)
+int platform_info_init(void *platform)
{
XML_Parser parser;
FILE *file;
@@ -261,6 +317,9 @@
goto err_close_file;
}
+ my_data.platform = platform;
+ my_data.kvpairs = str_parms_create();
+
XML_SetElementHandler(parser, start_tag, end_tag);
while (1) {
diff --git a/hal/voice.c b/hal/voice.c
index 044dc28..1f36b36 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -18,6 +18,7 @@
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
+#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <cutils/log.h>
@@ -328,6 +329,9 @@
int ret = 0;
adev->voice.in_call = true;
+
+ voice_set_mic_mute(adev, adev->voice.mic_mute);
+
ret = voice_extn_start_call(adev);
if (ret == -ENOSYS) {
ret = voice_start_usecase(adev, USECASE_VOICE_CALL);
diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c
index 89b659c..6e92da8 100644
--- a/hal/voice_extn/voice_extn.c
+++ b/hal/voice_extn/voice_extn.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <math.h>
+#include <stdlib.h>
#include <cutils/log.h>
#include <cutils/str_parms.h>
#include <sys/ioctl.h>
diff --git a/legacy/alsa_sound/Android.mk b/legacy/alsa_sound/Android.mk
index 534dd6a..101c22d 100644
--- a/legacy/alsa_sound/Android.mk
+++ b/legacy/alsa_sound/Android.mk
@@ -45,11 +45,7 @@
libpower \
libalsa-intf
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
+LOCAL_SHARED_LIBRARIES += libdl
LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa
LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/libalsa-intf
@@ -61,7 +57,7 @@
LOCAL_MODULE := audio.primary.msm8960
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
@@ -78,7 +74,7 @@
AudioPolicyManagerALSA.cpp
LOCAL_MODULE := audio_policy.msm8960
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_LIBRARIES := \
@@ -97,9 +93,8 @@
include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_CFLAGS := -D_POSIX_SOURCE -Wno-multichar
LOCAL_CFLAGS += -DQCOM_ACDB_ENABLED
@@ -128,11 +123,7 @@
liblog \
libalsa-intf
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
+LOCAL_SHARED_LIBRARIES += libdl
LOCAL_MODULE:= alsa.msm8960
LOCAL_MODULE_TAGS := optional
diff --git a/legacy/libalsa-intf/Android.mk b/legacy/libalsa-intf/Android.mk
index 5d509b3..c259d9f 100644
--- a/legacy/libalsa-intf/Android.mk
+++ b/legacy/libalsa-intf/Android.mk
@@ -44,11 +44,6 @@
LOCAL_SHARED_LIBRARIES:= libc libcutils #libutils #libmedia libhardware_legacy
LOCAL_CFLAGS := -DQC_PROP -DCONFIG_DIR=\"/system/etc/snd_soc_msm/\"
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-LOCAL_PRELINK_MODULE := false
+LOCAL_SHARED_LIBRARIES += libdl
include $(BUILD_SHARED_LIBRARY)
endif
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 91ed2bc..b8aa9fc 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
LOCAL_PATH:= $(call my-dir)
@@ -21,7 +21,7 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libqcompostprocbundle
LOCAL_C_INCLUDES := \
@@ -29,5 +29,32 @@
$(call include-path-for, audio-effects)
include $(BUILD_SHARED_LIBRARY)
+endif
-endif
\ No newline at end of file
+################################################################################
+
+ifneq ($(filter msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -DLIB_AUDIO_HAL="/system/lib/hw/audio.primary."$(TARGET_BOARD_PLATFORM)".so"
+
+LOCAL_SRC_FILES:= \
+ volume_listener.c
+
+LOCAL_CFLAGS+= -O2 -fvisibility=hidden
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog \
+ libdl
+
+LOCAL_MODULE_RELATIVE_PATH := soundfx
+LOCAL_MODULE:= libvolumelistener
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-effects)
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/post_proc/bundle.c b/post_proc/bundle.c
index 8518e54..df327ab 100644
--- a/post_proc/bundle.c
+++ b/post_proc/bundle.c
@@ -17,6 +17,7 @@
#define LOG_TAG "offload_effect_bundle"
//#define LOG_NDEBUG 0
+#include <stdlib.h>
#include <cutils/list.h>
#include <cutils/log.h>
#include <system/thread_defs.h>
@@ -620,8 +621,9 @@
if (pCmdData == NULL ||
cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
pReplyData == NULL ||
- *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
- sizeof(uint16_t))) {
+ *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint16_t)) ||
+ // constrain memcpy below
+ ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
status = -EINVAL;
ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
cmdSize, *replySize);
diff --git a/post_proc/effect_api.c b/post_proc/effect_api.c
index cf3968b..9c15e8f 100644
--- a/post_proc/effect_api.c
+++ b/post_proc/effect_api.c
@@ -17,6 +17,7 @@
#define LOG_TAG "offload_effect_api"
//#define LOG_NDEBUG 0
+#include <errno.h>
#include <stdbool.h>
#include <cutils/log.h>
#include <tinyalsa/asoundlib.h>
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
new file mode 100644
index 0000000..280bfe3
--- /dev/null
+++ b/post_proc/volume_listener.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2015 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 "volume_listener"
+//#define LOG_NDEBUG 0
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <hardware/audio_effect.h>
+#include <cutils/properties.h>
+
+#define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+#define VOL_FLAG ( EFFECT_FLAG_TYPE_INSERT | \
+ EFFECT_FLAG_VOLUME_IND | \
+ EFFECT_FLAG_DEVICE_IND | \
+ EFFECT_FLAG_OFFLOAD_SUPPORTED)
+
+#define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\
+ i == MUSIC?"MUSIC": \
+ i == RING?"RING": \
+ i == ALARM?"ALARM": \
+ i == VOICE_CALL?"Voice_call": \
+ i == NOTIFICATION?"Notification":\
+ "--INVALID--"); \
+
+#define MAX_GAIN_LEVELS 5
+
+#define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration"
+
+enum {
+ VOL_LISTENER_STATE_UNINITIALIZED,
+ VOL_LISTENER_STATE_INITIALIZED,
+ VOL_LISTENER_STATE_ACTIVE,
+};
+
+typedef struct vol_listener_context_s vol_listener_context_t;
+static const struct effect_interface_s effect_interface;
+
+/* flag to avoid multiple initialization */
+static bool initialized = false;
+
+/* current gain dep cal level that was pushed succesfully */
+static int current_gain_dep_cal_level = -1;
+
+enum STREAM_TYPE {
+ MUSIC,
+ RING,
+ ALARM,
+ VOICE_CALL,
+ NOTIFICATION,
+ MAX_STREAM_TYPES,
+};
+
+struct vol_listener_context_s {
+ const struct effect_interface_s *itfe;
+ struct listnode effect_list_node;
+ effect_config_t config;
+ const effect_descriptor_t *desc;
+ uint32_t stream_type;
+ uint32_t session_id;
+ uint32_t state;
+ uint32_t dev_id;
+ float left_vol;
+ float right_vol;
+};
+
+/* volume listener, music UUID: 08b8b058-0590-11e5-ac71-0025b32654a0 */
+const effect_descriptor_t vol_listener_music_descriptor = {
+ { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+ { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ VOL_FLAG,
+ 0, /* TODO */
+ 1,
+ "Volume listener for Music",
+ "Qualcomm Technologies Inc.",
+};
+
+/* volume listener, ring UUID: 0956df94-0590-11e5-bdbe-0025b32654a0 */
+const effect_descriptor_t vol_listener_ring_descriptor = {
+ { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+ { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ VOL_FLAG,
+ 0, /* TODO */
+ 1,
+ "Volume listener for ring",
+ "Qualcomm Technologies Inc",
+};
+
+/* volume listener, alarm UUID: 09f303e2-0590-11e5-8fdb-0025b32654a0 */
+const effect_descriptor_t vol_listener_alarm_descriptor = {
+ { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+ { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ VOL_FLAG,
+ 0, /* TODO */
+ 1,
+ "Volume listener for alarm",
+ "Qualcomm Technologies Inc",
+};
+
+/* volume listener, voice call UUID: 0ace5c08-0590-11e5-ae9e-0025b32654a0 */
+const effect_descriptor_t vol_listener_voice_call_descriptor = {
+ { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+ { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ VOL_FLAG,
+ 0, /* TODO */
+ 1,
+ "Volume listener for voice call",
+ "Qualcomm Technologies Inc",
+};
+
+/* volume listener, notification UUID: 0b776dde-0590-11e5-81ba-0025b32654a0 */
+const effect_descriptor_t vol_listener_notification_descriptor = {
+ { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+ { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ VOL_FLAG,
+ 0, /* TODO */
+ 1,
+ "Volume listener for notification",
+ "Qualcomm Technologies Inc",
+};
+
+struct amp_db_and_gain_table {
+ float amp;
+ float db;
+ uint32_t level;
+} amp_to_dBLevel_table;
+
+// using gain level for non-drc volume curve
+static const struct amp_db_and_gain_table volume_curve_gain_mapping_table[MAX_GAIN_LEVELS] =
+{
+ /* Level 0 in the calibration database contains default calibration */
+ { 0.001774, -55, 5 },
+ { 0.501187, -6, 4 },
+ { 0.630957, -4, 3 },
+ { 0.794328, -2, 2 },
+ { 1.0, 0, 1 },
+};
+
+static const effect_descriptor_t *descriptors[] = {
+ &vol_listener_music_descriptor,
+ &vol_listener_ring_descriptor,
+ &vol_listener_alarm_descriptor,
+ &vol_listener_voice_call_descriptor,
+ &vol_listener_notification_descriptor,
+ NULL,
+};
+
+pthread_once_t once = PTHREAD_ONCE_INIT;
+/* flag to indicate if init was success */
+static int init_status;
+
+/* current volume level for which gain dep cal level was selected */
+static float current_vol = 0.0;
+
+/* HAL interface to send calibration */
+static bool (*send_gain_dep_cal)(int);
+
+/* if dumping allowed */
+static bool dumping_enabled = false;
+
+/* list of created effects. */
+struct listnode vol_effect_list;
+
+/* lock must be held when modifying or accessing created_effects_list */
+pthread_mutex_t vol_listner_init_lock;
+
+/*
+ * Local functions
+ */
+static void dump_list_l()
+{
+ struct listnode *node;
+ vol_listener_context_t *context;
+
+ ALOGW("DUMP_START :: ===========");
+
+ list_for_each(node, &vol_effect_list) {
+ context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+ // dump stream_type / Device / session_id / left / righ volume
+ ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ",
+ __func__,
+ context->stream_type == MUSIC ? "MUSIC" :
+ context->stream_type == RING ? "RING" :
+ context->stream_type == ALARM ? "ALARM" :
+ context->stream_type == VOICE_CALL ? "VOICE_CALL" :
+ context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--",
+ context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol);
+ }
+
+ ALOGW("DUMP_END :: ===========");
+}
+
+static void check_and_set_gain_dep_cal()
+{
+ // iterate through list and make decision to set new gain dep cal level for speaker device
+ // 1. find all usecase active on speaker
+ // 2. find average of left and right for each usecase
+ // 3. find the highest of all the active usecase
+ // 4. if new value is different than the current value then load new calibration
+
+ struct listnode *node = NULL;
+ float new_vol = 0.0;
+ int max_level = 0;
+ vol_listener_context_t *context = NULL;
+ if (dumping_enabled) {
+ dump_list_l();
+ }
+
+ ALOGV("%s ==> Start ...", __func__);
+
+ // select the highest volume on speaker device
+ list_for_each(node, &vol_effect_list) {
+ context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+ if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
+ (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
+ (new_vol < (context->left_vol + context->right_vol) / 2)) {
+ new_vol = (context->left_vol + context->right_vol) / 2;
+ }
+ }
+
+ if (new_vol != current_vol) {
+ ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",
+ __func__, current_vol, new_vol);
+
+ if (send_gain_dep_cal != NULL) {
+ // send Gain dep cal level
+ int gain_dep_cal_level = -1;
+
+ if (new_vol >= 1) { // max amplitude, use highest DRC level
+ gain_dep_cal_level = volume_curve_gain_mapping_table[MAX_GAIN_LEVELS - 1].level;
+ } else if (new_vol <= 0) {
+ gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
+ } else {
+ for (max_level = 0; max_level + 1 < MAX_GAIN_LEVELS; max_level++) {
+ if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
+ new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
+ gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
+ ALOGV("%s: volume(%f), gain dep cal selcetd %d ",
+ __func__, current_vol, gain_dep_cal_level);
+ break;
+ }
+ }
+ }
+
+ // check here if previous gain dep cal level was not same
+ if (gain_dep_cal_level != -1) {
+ if (gain_dep_cal_level != current_gain_dep_cal_level) {
+ // decision made .. send new level now
+ if (!send_gain_dep_cal(gain_dep_cal_level)) {
+ ALOGE("%s: Failed to set gain dep cal level", __func__);
+ } else {
+ // Success in setting the gain dep cal level, store new level and Volume
+ if (dumping_enabled) {
+ ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)",
+ __func__, current_vol, new_vol, current_gain_dep_cal_level,
+ gain_dep_cal_level);
+ } else {
+ ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)",
+ __func__, current_vol, new_vol, current_gain_dep_cal_level,
+ gain_dep_cal_level);
+ }
+ current_gain_dep_cal_level = gain_dep_cal_level;
+ current_vol = new_vol;
+ }
+ } else {
+ if (dumping_enabled) {
+ ALOGW("%s: volume changed but gain dep cal level is still the same",
+ __func__);
+ } else {
+ ALOGV("%s: volume changed but gain dep cal level is still the same",
+ __func__);
+ }
+ }
+ } else {
+ ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol);
+ }
+ } else {
+ ALOGE("%s: not able to send calibration, NULL function pointer",
+ __func__);
+ }
+ } else {
+ ALOGV("%s:: volume not changed, stick to same config ..... ", __func__);
+ }
+
+ ALOGV("check_and_set_gain_dep_cal ==> End ");
+}
+
+/*
+ * Effect Control Interface Implementation
+ */
+
+static int vol_effect_process(effect_handle_t self,
+ audio_buffer_t *in_buffer,
+ audio_buffer_t *out_buffer)
+{
+ int status = 0;
+ ALOGV("%s Called ", __func__);
+
+ vol_listener_context_t *context = (vol_listener_context_t *)self;
+ pthread_mutex_lock(&vol_listner_init_lock);
+
+ if (context->state != VOL_LISTENER_STATE_ACTIVE) {
+ ALOGE("%s: state is not active .. return error", __func__);
+ status = -EINVAL;
+ goto exit;
+ }
+
+ // calculation based on channel count 2
+ if (in_buffer->raw != out_buffer->raw) {
+ memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
+ } else {
+ ALOGW("%s: something wrong, didn't handle in_buffer and out_buffer same address case",
+ __func__);
+ }
+
+exit:
+ pthread_mutex_unlock(&vol_listner_init_lock);
+ return status;
+}
+
+
+static int vol_effect_command(effect_handle_t self,
+ uint32_t cmd_code, uint32_t cmd_size,
+ void *p_cmd_data, uint32_t *reply_size,
+ void *p_reply_data)
+{
+ vol_listener_context_t *context = (vol_listener_context_t *)self;
+ int status = 0;
+
+ ALOGV("%s Called ", __func__);
+ pthread_mutex_lock(&vol_listner_init_lock);
+
+ if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) {
+ ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
+ "context" : "context->state");
+ status = -EINVAL;
+ goto exit;
+ }
+
+ switch (cmd_code) {
+ case EFFECT_CMD_INIT:
+ ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
+ if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+ ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
+ (p_reply_data == NULL) ? "p_reply_data is NULL" :
+ "*reply_size != sizeof(int)");
+ return -EINVAL;
+ }
+ *(int *)p_reply_data = 0;
+ break;
+
+ case EFFECT_CMD_SET_CONFIG:
+ ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
+ break;
+
+ case EFFECT_CMD_GET_CONFIG:
+ ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
+ break;
+
+ case EFFECT_CMD_RESET:
+ ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
+ break;
+
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
+ break;
+
+ case EFFECT_CMD_OFFLOAD:
+ ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
+ if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+ ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
+ (p_reply_data == NULL) ? "p_reply_data is NULL" :
+ "*reply_size != sizeof(int)");
+ return -EINVAL;
+ }
+ *(int *)p_reply_data = 0;
+ break;
+
+ case EFFECT_CMD_ENABLE:
+ ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
+ if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+ ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
+ (p_reply_data == NULL) ? "p_reply_data is NULL" :
+ "*reply_size != sizeof(int)");
+ status = -EINVAL;
+ goto exit;
+ }
+
+ if (context->state != VOL_LISTENER_STATE_INITIALIZED) {
+ ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
+ status = -ENOSYS;
+ goto exit;
+ }
+
+ context->state = VOL_LISTENER_STATE_ACTIVE;
+ *(int *)p_reply_data = 0;
+
+ // After changing the state and if device is speaker
+ // recalculate gain dep cal level
+ if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+ check_and_set_gain_dep_cal();
+ }
+
+ break;
+
+ case EFFECT_CMD_DISABLE:
+ ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
+ if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+ ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
+ (p_reply_data == NULL) ? "p_reply_data is NULL" :
+ "*reply_size != sizeof(int)");
+ status = -EINVAL;
+ goto exit;
+ }
+
+ if (context->state != VOL_LISTENER_STATE_ACTIVE) {
+ ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
+ status = -ENOSYS;
+ goto exit;
+ }
+
+ context->state = VOL_LISTENER_STATE_INITIALIZED;
+ *(int *)p_reply_data = 0;
+
+ // After changing the state and if device is speaker
+ // recalculate gain dep cal level
+ if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+ check_and_set_gain_dep_cal();
+ }
+
+ break;
+
+ case EFFECT_CMD_GET_PARAM:
+ ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
+ break;
+
+ case EFFECT_CMD_SET_PARAM:
+ ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
+ break;
+
+ case EFFECT_CMD_SET_DEVICE:
+ {
+ uint32_t new_device;
+ bool recompute_gain_dep_cal_Level = false;
+ ALOGV("cmd called EFFECT_CMD_SET_DEVICE ");
+
+ if (p_cmd_data == NULL) {
+ ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
+ status = -EINVAL;
+ goto exit;
+ }
+
+ new_device = *(uint32_t *)p_cmd_data;
+ ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
+ __func__, context->dev_id, new_device);
+
+ // check if old or new device is speaker
+ if ((context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) ||
+ (new_device & AUDIO_DEVICE_OUT_SPEAKER)) {
+ recompute_gain_dep_cal_Level = true;
+ }
+
+ context->dev_id = new_device;
+
+ if (recompute_gain_dep_cal_Level) {
+ check_and_set_gain_dep_cal();
+ }
+ }
+ break;
+
+ case EFFECT_CMD_SET_VOLUME:
+ {
+ float left_vol = 0, right_vol = 0;
+ bool recompute_gain_dep_cal_Level = false;
+
+ ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
+ if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
+ ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
+ "p_cmd_data is NULL" : "cmd_size issue");
+ status = -EINVAL;
+ goto exit;
+ }
+
+ if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+ recompute_gain_dep_cal_Level = true;
+ }
+
+ left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
+ right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
+ ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
+ context->right_vol, left_vol, right_vol);
+
+ context->left_vol = left_vol;
+ context->right_vol = right_vol;
+
+ // recompute gan dep cal level only if volume changed on speaker device
+ if (recompute_gain_dep_cal_Level) {
+ check_and_set_gain_dep_cal();
+ }
+ }
+ break;
+
+ default:
+ ALOGW("volume_listener_command invalid command %d", cmd_code);
+ status = -ENOSYS;
+ break;
+ }
+
+exit:
+ pthread_mutex_unlock(&vol_listner_init_lock);
+ return status;
+}
+
+/* Effect Control Interface Implementation: get_descriptor */
+static int vol_effect_get_descriptor(effect_handle_t self,
+ effect_descriptor_t *descriptor)
+{
+ vol_listener_context_t *context = (vol_listener_context_t *)self;
+ ALOGV("%s Called ", __func__);
+
+ if (descriptor == NULL) {
+ ALOGE("%s: descriptor is NULL", __func__);
+ return -EINVAL;
+ }
+
+ *descriptor = *context->desc;
+ return 0;
+}
+
+static void init_once()
+{
+ int i = 0;
+ if (initialized) {
+ ALOGV("%s : already init .. do nothing", __func__);
+ return;
+ }
+
+ ALOGD("%s Called ", __func__);
+ pthread_mutex_init(&vol_listner_init_lock, NULL);
+
+ // get hal function pointer
+ if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
+ void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
+ if (hal_lib_pointer == NULL) {
+ ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
+ send_gain_dep_cal = NULL;
+ } else {
+ ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
+ send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
+ if (send_gain_dep_cal == NULL) {
+ ALOGE("Couldnt able to get the function symbol");
+ }
+ }
+ } else {
+ ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
+ send_gain_dep_cal = NULL;
+ }
+
+ // check system property to see if dumping is required
+ char check_dump_val[PROPERTY_VALUE_MAX];
+ property_get("audio.volume.listener.dump", check_dump_val, "0");
+ if (atoi(check_dump_val)) {
+ dumping_enabled = true;
+ }
+
+ init_status = 0;
+ list_init(&vol_effect_list);
+ initialized = true;
+}
+
+static int lib_init()
+{
+ pthread_once(&once, init_once);
+ ALOGV("%s Called ", __func__);
+ return init_status;
+}
+
+static int vol_prc_lib_create(const effect_uuid_t *uuid,
+ int32_t session_id,
+ int32_t io_id,
+ effect_handle_t *p_handle)
+{
+ int itt = 0;
+ vol_listener_context_t *context = NULL;
+
+ ALOGV("volume_prc_lib_create .. called ..");
+
+ if (lib_init() != 0) {
+ return init_status;
+ }
+
+ if (p_handle == NULL || uuid == NULL) {
+ ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
+ return -EINVAL;
+ }
+
+ context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));
+
+ if (context == NULL) {
+ ALOGE("%s: failed to allocate for context .. oops !!", __func__);
+ return -EINVAL;
+ }
+
+ // check if UUID is supported
+ for (itt = 0; descriptors[itt] != NULL; itt++) {
+ if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
+ // check if this correct .. very imp
+ context->desc = descriptors[itt];
+ context->stream_type = itt;
+ PRINT_STREAM_TYPE(itt)
+ break;
+ }
+ }
+
+ if (descriptors[itt] == NULL) {
+ ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
+ free(context);
+ return -EINVAL;
+ }
+
+ ALOGV("%s CREATED_CONTEXT %p", __func__, context);
+
+ context->itfe = &effect_interface;
+ context->state = VOL_LISTENER_STATE_INITIALIZED;
+ context->dev_id = AUDIO_DEVICE_NONE;
+ context->session_id = session_id;
+
+ // Add this to master list
+ pthread_mutex_lock(&vol_listner_init_lock);
+ list_add_tail(&vol_effect_list, &context->effect_list_node);
+
+ if (dumping_enabled) {
+ dump_list_l();
+ }
+
+ pthread_mutex_unlock(&vol_listner_init_lock);
+
+ *p_handle = (effect_handle_t)context;
+ return 0;
+}
+
+static int vol_prc_lib_release(effect_handle_t handle)
+{
+ struct listnode *node, *temp_node_next;
+ vol_listener_context_t *context = NULL;
+ vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle;
+ int status = -EINVAL;
+ bool recompute_flag = false;
+ int active_stream_count = 0;
+ uint32_t session_id;
+ uint32_t stream_type;
+ effect_uuid_t uuid;
+
+ ALOGV("%s context %p", __func__, handle);
+
+ if (recv_contex == NULL) {
+ return status;
+ }
+ pthread_mutex_lock(&vol_listner_init_lock);
+ session_id = recv_contex->session_id;
+ stream_type = recv_contex->stream_type;
+ uuid = recv_contex->desc->uuid;
+
+ // check if the handle/context provided is valid
+ list_for_each_safe(node, temp_node_next, &vol_effect_list) {
+ context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+ if ((memcmp(&(context->desc->uuid), &uuid, sizeof(effect_uuid_t)) == 0)
+ && (context->session_id == session_id)
+ && (context->stream_type == stream_type)) {
+ ALOGV("--- Found something to remove ---");
+ list_remove(node);
+ PRINT_STREAM_TYPE(context->stream_type);
+ if (context->dev_id && AUDIO_DEVICE_OUT_SPEAKER) {
+ recompute_flag = true;
+ }
+ free(context);
+ status = 0;
+ } else {
+ ++active_stream_count;
+ }
+ }
+
+ if (status != 0) {
+ ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>");
+ pthread_mutex_unlock(&vol_listner_init_lock);
+ return status;
+ }
+
+ // if there are no active streams, reset cal and volume level
+ if (active_stream_count == 0) {
+ current_gain_dep_cal_level = -1;
+ current_vol = 0.0;
+ }
+
+ if (recompute_flag) {
+ check_and_set_gain_dep_cal();
+ }
+
+ if (dumping_enabled) {
+ dump_list_l();
+ }
+ pthread_mutex_unlock(&vol_listner_init_lock);
+ return status;
+}
+
+static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *descriptor)
+{
+ int i = 0;
+ ALOGV("%s Called ", __func__);
+ if (lib_init() != 0) {
+ return init_status;
+ }
+
+ if (descriptor == NULL || uuid == NULL) {
+ ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
+ return -EINVAL;
+ }
+
+ for (i = 0; descriptors[i] != NULL; i++) {
+ if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
+ *descriptor = *descriptors[i];
+ return 0;
+ }
+ }
+
+ ALOGE("%s: couldnt found uuid passed, oops", __func__);
+ return -EINVAL;
+}
+
+
+/* effect_handle_t interface implementation for volume listener effect */
+static const struct effect_interface_s effect_interface = {
+ vol_effect_process,
+ vol_effect_command,
+ vol_effect_get_descriptor,
+ NULL,
+};
+
+__attribute__((visibility("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Volume Listener Effect Library",
+ .implementor = "Qualcomm Technologies Inc.",
+ .create_effect = vol_prc_lib_create,
+ .release_effect = vol_prc_lib_release,
+ .get_descriptor = vol_prc_lib_get_descriptor,
+};
diff --git a/visualizer/Android.mk b/visualizer/Android.mk
index bec54d6..87d4987 100644
--- a/visualizer/Android.mk
+++ b/visualizer/Android.mk
@@ -27,7 +27,7 @@
libdl \
libtinyalsa
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libqcomvisualizer
LOCAL_C_INCLUDES := \
diff --git a/voice_processing/Android.mk b/voice_processing/Android.mk
index b64c0e3..9b86eaf 100644
--- a/voice_processing/Android.mk
+++ b/voice_processing/Android.mk
@@ -5,7 +5,7 @@
LOCAL_MODULE:= libqcomvoiceprocessing
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_SRC_FILES:= \
voice_processing.c
diff --git a/voice_processing/voice_processing.c b/voice_processing/voice_processing.c
index 08acdb4..7d2b592 100644
--- a/voice_processing/voice_processing.c
+++ b/voice_processing/voice_processing.c
@@ -16,6 +16,7 @@
#define LOG_TAG "voice_processing"
/*#define LOG_NDEBUG 0*/
+#include <stdlib.h>
#include <dlfcn.h>
#include <cutils/log.h>
#include <cutils/list.h>
@@ -559,7 +560,9 @@
if (pCmdData == NULL ||
cmdSize < (int)sizeof(effect_param_t) ||
pReplyData == NULL ||
- *replySize < (int)sizeof(effect_param_t)) {
+ *replySize < (int)sizeof(effect_param_t) ||
+ // constrain memcpy below
+ ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
ALOGV("fx_command() EFFECT_CMD_GET_PARAM invalid args");
return -EINVAL;
}