hal: add support for speaker protection 3.0
Check applicability of speaker protection v3 with ADSP and
WSA version. Set boost value and limiter threshold following
lookup table.
Change-Id: Icf1d5ef1eae7639448a285eb56d08ac092729058
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
index a33d432..ea4d856 100644
--- a/hal/audio_extn/spkr_protection.c
+++ b/hal/audio_extn/spkr_protection.c
@@ -46,6 +46,7 @@
#include <cutils/properties.h>
#include "audio_extn.h"
#include <linux/msm_audio_calibration.h>
+#include <linux/msm_audio.h>
#ifdef DYNAMIC_LOG_ENABLED
#include <log_xml_parser.h>
@@ -59,7 +60,29 @@
#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6))
#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6))
#define VI_FEED_CHANNEL "VI_FEED_TX Channels"
+#define WSA8815_NAME_LEFT "wsatz.13"
+#define WSA8815_NAME_RIGHT "wsatz.14"
+#define WCD_LEFT_BOOST_MAX_STATE "SPKR Left Boost Max State"
+#define WCD_RIGHT_BOOST_MAX_STATE "SPKR Right Boost Max State"
+#define WSA_LEFT_BOOST_LEVEL "SpkrLeft Boost Level"
+#define WSA_RIGHT_BOOST_LEVEL "SpkrRight Boost Level"
+/* Min and max resistance value in lookup table. */
+#define MIN_RESISTANCE_LOOKUP (3.2)
+#define MAX_RESISTANCE_LOOKUP (8)
+#define SPV3_LOOKUP_TABLE_ROWS (49)
+/* default limiter threshold is 0dB */
+#define DEFAULT_LIMITER_TH (0x0)
+#define AFE_API_VERSION_SUPPORT_SPV3 (0x2)
+enum spv3_boost_max_state {
+ BOOST_NO_MAX_STATE,
+ BOOST_MAX_STATE_1,
+ BOOST_MAX_STATE_2,
+};
+enum sp_version {
+ SP_V2 = 0x1,
+ SP_V3 = AFE_API_VERSION_SUPPORT_SPV3,
+};
/*Set safe temp value to 40C*/
#define SAFE_SPKR_TEMP 40
#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
@@ -162,6 +185,8 @@
pthread_cond_t cal_wait_condition;
bool init_check;
volatile bool thread_exit;
+ unsigned int sp_version;
+ int limiter_th[SP_V2_NUM_MAX_SPKRS];
};
static struct pcm_config pcm_config_skr_prot = {
@@ -180,6 +205,86 @@
char *spkr_2_name;
};
+struct spv3_boost {
+ /* bit7-4: first stage; bit 3-0: second stage */
+ int boost_value;
+ int max_state;
+};
+
+#define SPV3_BOOST_VALUE_STATE(value, state) \
+{ .boost_value = (value), .max_state = (state) }
+
+static struct spv3_boost spv3_boost_lookup_table[SPV3_LOOKUP_TABLE_ROWS] = {
+ SPV3_BOOST_VALUE_STATE(0xc7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0xd7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0xd7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0xe7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0xe7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0xf7, BOOST_MAX_STATE_1),
+ SPV3_BOOST_VALUE_STATE(0x70, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x70, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x71, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x71, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x72, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x72, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x73, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x73, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x74, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x75, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x75, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x76, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x76, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x77, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x77, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x78, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x78, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x79, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x79, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7a, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7a, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7a, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7b, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7b, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7c, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7c, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7d, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7d, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7e, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7e, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+ SPV3_BOOST_VALUE_STATE(0x7f, BOOST_MAX_STATE_2),
+};
+
+/* 3.2 ohm in q24 format: (3.2 * (1 << 24)) */
+#define MIN_LOOKUP_RESISTANCE_SPKR_Q24 (53687091)
+/* 8 ohm in q24 format: (8 * (1 << 24)) */
+#define MAX_LOOKUP_RESISTANCE_SPKR_Q24 (134217728)
+/* 0.1 ohm in q24 format: (0.1 * (1 <<24)) */
+#define LOOKUP_RESISTANCE_GAP_SPKR_Q24 (1677722)
+
+/* 3.2ohm : 0.1ohm : 8ohm lookup table */
+static int spv3_limiter_th_q27_table[SPV3_LOOKUP_TABLE_ROWS] = {
+ -526133494, -508685189, -491236884, -473788580, -457682452, -441576325,
+ -426812375, -410706248, -395942298, -382520525, -367756575, -354334802,
+ -340913029, -327491256, -315411661, -301989888, -289910292, -277830697,
+ -265751101, -255013683, -242934088, -232196669, -221459251, -210721833,
+ -199984415, -190589174, -179851756, -170456515, -159719096, -150323855,
+ -140928614, -131533373, -122138132, -114085069, -104689828, -95294587,
+ -87241523, -79188460, -69793219, -61740155, -53687091, -45634028,
+ -37580964, -29527900, -22817014, -14763950, -6710886, 0,
+ 0
+};
static struct speaker_prot_session handle;
static int vi_feed_no_channels;
static struct spkr_tz_names tz_names;
@@ -380,6 +485,11 @@
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;
+#ifdef MSM_SPKR_PROT_SPV3
+ cal_data.cal_type.cal_info.sp_version = protCfg->sp_version;
+ cal_data.cal_type.cal_info.limiter_th[SP_V2_SPKR_1] = protCfg->limiter_th[SP_V2_SPKR_1];
+ cal_data.cal_type.cal_info.limiter_th[SP_V2_SPKR_2] = protCfg->limiter_th[SP_V2_SPKR_2];
+#endif
property_get("persist.vendor.audio.spkr.cal.duration", value, "0");
if (atoi(value) > 0) {
ALOGD("%s: quick calibration enabled", __func__);
@@ -444,6 +554,179 @@
}
}
+static bool is_wsa_present(void)
+{
+ ALOGD("%s: tz1: %s, tz2: %s", __func__,
+ tz_names.spkr_1_name, tz_names.spkr_2_name);
+ handle.spkr_1_tzn = get_tzn(tz_names.spkr_1_name);
+ handle.spkr_2_tzn = get_tzn(tz_names.spkr_2_name);
+ if ((handle.spkr_1_tzn >= 0) || (handle.spkr_2_tzn >= 0))
+ handle.wsa_found = true;
+
+ return handle.wsa_found;
+}
+
+static void audio_extn_check_wsa_support_sp_v3(struct audio_device *adev,
+ unsigned int num_of_spkrs, bool *wsa_support_spv3)
+{
+ unsigned int i = 0;
+ if (!is_wsa_present() ||
+ platform_spkr_prot_is_wsa_analog_mode(adev)){
+ for (i = 0; i < num_of_spkrs; i++)
+ wsa_support_spv3[i] = false;
+
+ return;
+ }
+
+ if (!strncmp(WSA8815_NAME_LEFT, tz_names.spkr_1_name,
+ sizeof(WSA8815_NAME_LEFT)) ||
+ !strncmp(WSA8815_NAME_RIGHT, tz_names.spkr_1_name,
+ sizeof(WSA8815_NAME_RIGHT))) {
+ wsa_support_spv3[SP_V2_SPKR_1] = true;
+ } else {
+ wsa_support_spv3[SP_V2_SPKR_1] = false;
+ ALOGI("%s: Speaker1(%s) is not wsa8815.", __func__, tz_names.spkr_1_name);
+ }
+
+ if (num_of_spkrs == SP_V2_NUM_MAX_SPKRS &&
+ (!strncmp(WSA8815_NAME_RIGHT, tz_names.spkr_2_name,
+ sizeof(WSA8815_NAME_RIGHT)) ||
+ !strncmp(WSA8815_NAME_LEFT, tz_names.spkr_2_name,
+ sizeof(WSA8815_NAME_LEFT)))) {
+ wsa_support_spv3[SP_V2_SPKR_2] = true;
+ } else {
+ wsa_support_spv3[SP_V2_SPKR_2] = false;
+ ALOGI("%s: Speaker2(%s) is not wsa8815.", __func__, tz_names.spkr_2_name);
+ }
+
+}
+
+int audio_extn_set_wcd_boost_max_state(struct audio_device *adev,
+ int boost_max_state, int wsa_num)
+{
+ struct mixer_ctl *ctl = NULL;
+ const char *mixer_ctl_name[] = {
+ WCD_LEFT_BOOST_MAX_STATE,
+ WCD_RIGHT_BOOST_MAX_STATE
+ };
+ int status = 0;
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name[wsa_num]);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name[wsa_num]);
+ return -EINVAL;
+ }
+
+ status = mixer_ctl_set_value(ctl, 0, boost_max_state);
+ if (status < 0) {
+ ALOGE("%s: failed to set WCD boost state.\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int audio_extn_set_wsa_boost_level(struct audio_device *adev,
+ int wsa_num, int boost_table_index)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name_boost_level[] = {
+ WSA_LEFT_BOOST_LEVEL,
+ WSA_RIGHT_BOOST_LEVEL
+ };
+ int status = 0;
+
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ mixer_ctl_name_boost_level[wsa_num]);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name_boost_level[wsa_num]);
+ return -EINVAL;
+ }
+
+ status = mixer_ctl_set_value(ctl, 0,
+ spv3_boost_lookup_table[boost_table_index].boost_value);
+
+ if (status < 0) {
+ ALOGE("%s: Could not set ctl for mixer %s\n", __func__,
+ mixer_ctl_name_boost_level[wsa_num]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audio_extn_config_spv3(struct audio_device *adev, unsigned int wsa_num)
+{
+ float dcr = 0;
+ unsigned int r0_index = 0;
+ int boost_max_state = 0;
+ int ret = 0;
+
+ /* get R0 value */
+ dcr = ((float)handle.sp_r0t0_cal.r0[wsa_num] / MIN_RESISTANCE_SPKR_Q24 * 2);
+ if (dcr < MIN_RESISTANCE_LOOKUP) {
+ ALOGV("%s: resistance %f changes to min value of 3.2.",
+ __func__, dcr);
+ dcr = MIN_RESISTANCE_LOOKUP;
+ }
+
+ if (dcr > MAX_RESISTANCE_LOOKUP) {
+ ALOGV("%s: resistance %f changes to max value of 8.",
+ __func__, dcr);
+ dcr = MAX_RESISTANCE_LOOKUP;
+ }
+
+ r0_index = (int)((dcr - MIN_RESISTANCE_LOOKUP) * 10);
+ if (r0_index >= SPV3_LOOKUP_TABLE_ROWS) {
+ ALOGE("%s: r0_index=%d overflows.", __func__, r0_index);
+ return -EINVAL;
+ }
+
+ boost_max_state = spv3_boost_lookup_table[r0_index].max_state;
+ ret = audio_extn_set_wcd_boost_max_state(adev, boost_max_state, wsa_num);
+ if (ret < 0) {
+ ALOGE("%s: failed to set wcd max boost state.",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = audio_extn_set_wsa_boost_level(adev, wsa_num, r0_index);
+ if (ret < 0) {
+ ALOGE("%s: failed to set wsa boost level.",
+ __func__);
+ return -EINVAL;
+ }
+
+ handle.limiter_th[wsa_num] = spv3_limiter_th_q27_table[r0_index];
+
+ return 0;
+}
+
+static void audio_extn_check_config_sp_v3(struct audio_device *adev,
+ bool spv3_enable, unsigned int afe_api_version)
+{
+ int chn = 0;
+ bool wsa_support_spv3[SP_V2_NUM_MAX_SPKRS] = {false, false};
+
+ if (spv3_enable && afe_api_version >= AFE_API_VERSION_SUPPORT_SPV3) {
+ handle.sp_version = SP_V2;
+ audio_extn_check_wsa_support_sp_v3(adev, vi_feed_no_channels, wsa_support_spv3);
+ /*
+ * In case of WSA8815+8810, invalid limiter threshold is sent to DSP
+ * for WSA8810 speaker. DSP ignores the invalid value and use default one.
+ * The approach let spv3 apply on 8815 and spv2 on 8810 respectively.
+ */
+ for (chn = 0; chn < vi_feed_no_channels; chn++) {
+ if (wsa_support_spv3[chn] && !audio_extn_config_spv3(adev, chn))
+ handle.sp_version = SP_V3;
+ else
+ handle.limiter_th[chn] = DEFAULT_LIMITER_TH;
+ }
+ }
+}
+
static int spkr_calibrate(int t0_spk_1, int t0_spk_2)
{
struct audio_device *adev = handle.adev_handle;
@@ -706,6 +989,8 @@
int spk_1_tzn, spk_2_tzn;
char buf[32] = {0};
int ret;
+ bool spv3_enable = false;
+ unsigned int afe_api_version = 0;
memset(&protCfg, 0, sizeof(protCfg));
/* If the value of this persist.vendor.audio.spkr.cal.duration is 0
@@ -738,6 +1023,9 @@
return NULL;
}
+ spv3_enable = property_get_bool("persist.vendor.audio.spv3.enable", false);
+ afe_api_version = property_get_int32("persist.vendor.audio.avs.afe_api_version", 0);
+
fp = fopen(CALIB_FILE,"rb");
if (fp) {
int i;
@@ -770,6 +1058,9 @@
} else
handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
close(acdb_fd);
+
+ audio_extn_check_config_sp_v3(adev, spv3_enable, vi_feed_no_channels);
+
pthread_exit(0);
return NULL;
}
@@ -921,6 +1212,9 @@
if (handle.thermal_handle)
dlclose(handle.thermal_handle);
handle.thermal_handle = NULL;
+
+ audio_extn_check_config_sp_v3(adev, spv3_enable, vi_feed_no_channels);
+
pthread_exit(0);
return NULL;
}
@@ -936,17 +1230,6 @@
return 0;
}
-static bool is_wsa_present(void)
-{
- ALOGD("%s: tz1: %s, tz2: %s", __func__,
- tz_names.spkr_1_name, tz_names.spkr_2_name);
- handle.spkr_1_tzn = get_tzn(tz_names.spkr_1_name);
- handle.spkr_2_tzn = get_tzn(tz_names.spkr_2_name);
- if ((handle.spkr_1_tzn >= 0) || (handle.spkr_2_tzn >= 0))
- handle.wsa_found = true;
- return handle.wsa_found;
-}
-
void audio_extn_spkr_prot_set_parameters(struct str_parms *parms,
char *value, int len)
{
@@ -1449,6 +1732,11 @@
break;
}
protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+#ifdef MSM_SPKR_PROT_SPV3
+ protCfg.sp_version = handle.sp_version;
+ protCfg.limiter_th[SP_V2_SPKR_1] = handle.limiter_th[SP_V2_SPKR_1];
+ protCfg.limiter_th[SP_V2_SPKR_2] = handle.limiter_th[SP_V2_SPKR_2];
+#endif
ret = set_spkr_prot_cal(acdb_fd, &protCfg);
if (ret)
ALOGE("%s: speaker protection cal data swap failed", __func__);