summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com>2023-12-17 18:40:41 -0800
committerSecurityBot <android-nexus-securitybot@system.gserviceaccount.com>2023-12-17 18:40:41 -0800
commit6403e8085107993db824cf987ad05b4db2be6eb0 (patch)
tree43e0a24d5763da1e46bb0605f28b1c321bef57c8
parent47fdb78ba9606c02a4fe0f9fd371e4975fa0662c (diff)
parentd8fe94e828fae8ec4eac371c13aa9ca8b496562b (diff)
downloadamplifiers-6403e8085107993db824cf987ad05b4db2be6eb0.tar.gz
Merge android13-gs-pixel-5.10-24Q2 into android13-gs-pixel-5.10
SBMerger: 571992243 Change-Id: I7f504f9e1f9aabcaa204628a58f3ac93ddb203cb Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-rw-r--r--Documentation/ABI/testing/sysfs-devices-platform-audiometrics20
-rw-r--r--Documentation/ABI/testing/sysfs-driver-input-cs40l2611
-rw-r--r--audiometrics/audiometrics.c132
-rw-r--r--audiometrics/uapi/audiometrics_api.h11
-rw-r--r--cs40l26/cs40l26-sysfs.c42
-rw-r--r--cs40l26/cs40l26.c267
-rw-r--r--cs40l26/cs40l26.h29
7 files changed, 452 insertions, 60 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-audiometrics b/Documentation/ABI/testing/sysfs-devices-platform-audiometrics
new file mode 100644
index 0000000..ce3cb75
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-audiometrics
@@ -0,0 +1,20 @@
+What: /sys/devices/platform/audiometrics/cca_show
+Date: Aug, 2023
+KernelVersion: 5.15
+Contact: poomarin <poomarin@google.com>
+Description:
+ Reading from this file provides CCA count but not resets count value.
+
+What: /sys/devices/platform/audiometrics/cca_count_read_once_show
+Date: Aug, 2023
+KernelVersion: 5.15
+Contact: poomarin <poomarin@google.com>
+Description:
+ Reading from this file provides CCA count and resets count value.
+
+What: /sys/devices/platform/audiometrics/call_count
+Date: Jul, 2023
+KernelVersion: 5.15
+Contact: poomarin <poomarin@google.com>
+Description:
+ Reading from this file provides total count of voice-call and VoIP-call
diff --git a/Documentation/ABI/testing/sysfs-driver-input-cs40l26 b/Documentation/ABI/testing/sysfs-driver-input-cs40l26
new file mode 100644
index 0000000..e641417
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-input-cs40l26
@@ -0,0 +1,11 @@
+What: /sys/class/input/input(x)/device/default/reset
+Date: December 2023
+Contact: Tai Kuo <taikuo@google.com>
+Description:
+ Hardware reset trigger.
+
+ Access: Read, Write
+
+ Valid values: Represented as integer
+ 0: Make a reset decision and trigger reset if needed.
+ 1: Manual reset
diff --git a/audiometrics/audiometrics.c b/audiometrics/audiometrics.c
index 9b702ad..b310496 100644
--- a/audiometrics/audiometrics.c
+++ b/audiometrics/audiometrics.c
@@ -27,6 +27,8 @@
#define AUDIOMETRIC_CH_LENGTH 16
#define AMCS_MAX_MINOR (1U)
#define AMCS_CDEV_NAME "amcs"
+#define CCA_SOURCE_MAX 2
+#define CCA_SOURCE_VOICE 1
static struct platform_device *amcs_pdev;
@@ -70,9 +72,11 @@ struct audio_sz_type {
uint32_t mic_broken_degrade;
uint32_t ams_count;
uint32_t cs_count;
- uint32_t cca_active;
- uint32_t cca_enable;
- uint32_t cca_cs;
+ uint32_t cca_active[CCA_SOURCE_MAX];
+ uint32_t cca_enable[CCA_SOURCE_MAX];
+ uint32_t cca_cs[CCA_SOURCE_MAX];
+ int32_t voice_call_count;
+ int32_t voip_call_count;
};
struct audiometrics_priv_type {
@@ -409,62 +413,55 @@ static ssize_t ams_rate_read_once_show(struct device *dev,
static ssize_t cca_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct audiometrics_priv_type *priv;
- int counts;
-
- if (IS_ERR_OR_NULL(dev))
- return -ENODEV;
-
- priv = dev_get_drvdata(dev);
-
- if (IS_ERR_OR_NULL(priv))
- return -ENODEV;
+ struct audiometrics_priv_type *priv = dev_get_drvdata(dev);
+ int length;
mutex_lock(&priv->lock);
- counts = scnprintf(buf, PAGE_SIZE, "%u,%u,%u", priv->sz.cca_active,
- priv->sz.cca_enable, priv->sz.cca_cs);
+ length = sysfs_emit(buf, "%u %u %u", priv->sz.cca_active[CCA_SOURCE_VOICE],
+ priv->sz.cca_enable[CCA_SOURCE_VOICE], priv->sz.cca_cs[CCA_SOURCE_VOICE]);
mutex_unlock(&priv->lock);
- return counts;
+ return length;
}
-static ssize_t cca_rate_read_once_show(struct device *dev,
+static ssize_t cca_count_read_once_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct audiometrics_priv_type *priv;
- int counts;
- uint rate_active = 0, rate_enable = 0;
- const int scale = 100;
-
-
- if (IS_ERR_OR_NULL(dev))
- return -ENODEV;
-
- priv = dev_get_drvdata(dev);
-
- if (IS_ERR_OR_NULL(priv))
- return -ENODEV;
+ struct audiometrics_priv_type *priv = dev_get_drvdata(dev);
+ int i, length;
mutex_lock(&priv->lock);
-
- if (priv->sz.cca_cs) {
- rate_active = (priv->sz.cca_active * scale / priv->sz.cca_cs);
- rate_enable = (priv->sz.cca_enable * scale / priv->sz.cca_cs);
- }
-
- if (rate_active > scale) {
- rate_active = scale;
- rate_enable = scale;
+ length = 0;
+ for (i = 0; i < CCA_SOURCE_MAX; i++) {
+ length += sysfs_emit_at(buf, length, "%u %u ", priv->sz.cca_active[i],
+ priv->sz.cca_enable[i]);
+ priv->sz.cca_active[i] = 0;
+ priv->sz.cca_enable[i] = 0;
}
+ buf[--length] = 0;
+ mutex_unlock(&priv->lock);
+ return length;
+}
- counts = scnprintf(buf, PAGE_SIZE, "%u,%u", rate_active, rate_enable);
-
- priv->sz.cca_active = 0;
- priv->sz.cca_enable = 0;
- priv->sz.cca_cs = 0;
+/*
+ * Report call counts including voice-call and VoIP-call.
+ * Ex: result 10 20
+ *
+ * means there are 10 voice-call and 20 VoIP-call.
+ */
+static ssize_t call_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct audiometrics_priv_type *priv = dev_get_drvdata(dev);
+ int length = 0;
+ mutex_lock(&priv->lock);
+ length = sysfs_emit(buf, "%d %d", priv->sz.voice_call_count,
+ priv->sz.voip_call_count);
mutex_unlock(&priv->lock);
- return counts;
+ priv->sz.voice_call_count = 0;
+ priv->sz.voip_call_count = 0;
+ return length;
}
static int amcs_cdev_open(struct inode *inode, struct file *file)
@@ -487,6 +484,7 @@ static long amcs_cdev_unlocked_ioctl(struct file *file, unsigned int cmd, unsign
long ret = -EINVAL;
int i = 0;
struct amcs_params params;
+ uint32_t cca_source;
dev_dbg(priv->device, "%s cmd = 0x%x", __func__, cmd);
@@ -620,13 +618,13 @@ static long amcs_cdev_unlocked_ioctl(struct file *file, unsigned int cmd, unsign
case AMCS_OP_CCA:
mutex_lock(&priv->lock);
if (params.val[0] == AMCS_OP2_GET) {
- params.val[1] = priv->sz.cca_active;
- params.val[2] = priv->sz.cca_enable;
- params.val[3] = priv->sz.cca_cs;
+ params.val[1] = priv->sz.cca_active[CCA_SOURCE_VOICE];
+ params.val[2] = priv->sz.cca_enable[CCA_SOURCE_VOICE];
+ params.val[3] = priv->sz.cca_cs[CCA_SOURCE_VOICE];
} else if (params.val[0] == AMCS_OP2_SET) {
- priv->sz.cca_active = params.val[1];
- priv->sz.cca_enable = params.val[2];
- priv->sz.cca_cs = params.val[3];
+ priv->sz.cca_active[CCA_SOURCE_VOICE] = params.val[1];
+ priv->sz.cca_enable[CCA_SOURCE_VOICE] = params.val[2];
+ priv->sz.cca_cs[CCA_SOURCE_VOICE] = params.val[3];
}
mutex_unlock(&priv->lock);
@@ -637,15 +635,29 @@ static long amcs_cdev_unlocked_ioctl(struct file *file, unsigned int cmd, unsign
break;
case AMCS_OP_CCA_INCREASE:
- mutex_lock(&priv->lock);
+ ret = 0;
if (params.val[0] == AMCS_OP2_SET) {
- priv->sz.cca_active += params.val[1];
- priv->sz.cca_enable += params.val[2];
- priv->sz.cca_cs += params.val[3];
+ cca_source = params.val[4];
+ if (cca_source >= CCA_SOURCE_MAX) {
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_lock(&priv->lock);
+ priv->sz.cca_active[cca_source] += params.val[1];
+ priv->sz.cca_enable[cca_source] += params.val[2];
+ priv->sz.cca_cs[cca_source] += params.val[3];
+ mutex_unlock(&priv->lock);
}
- mutex_unlock(&priv->lock);
+ break;
+
+ case AMCS_OP_CALL_COUNT_INCREASE:
ret = 0;
- break;
+ if (params.val[0])
+ priv->sz.voice_call_count++;
+ else
+ priv->sz.voip_call_count++;
+ break;
default:
dev_warn(priv->device, "%s, unsupported op = %d\n", __func__, params.op);
@@ -709,7 +721,8 @@ static DEVICE_ATTR_RO(codec_crashed_counter);
static DEVICE_ATTR_RO(ams_cs);
static DEVICE_ATTR_RO(ams_rate_read_once);
static DEVICE_ATTR_RO(cca);
-static DEVICE_ATTR_RO(cca_rate_read_once);
+static DEVICE_ATTR_RO(cca_count_read_once);
+static DEVICE_ATTR_RO(call_count);
static struct attribute *audiometrics_fs_attrs[] = {
@@ -726,7 +739,8 @@ static struct attribute *audiometrics_fs_attrs[] = {
&dev_attr_ams_cs.attr,
&dev_attr_ams_rate_read_once.attr,
&dev_attr_cca.attr,
- &dev_attr_cca_rate_read_once.attr,
+ &dev_attr_cca_count_read_once.attr,
+ &dev_attr_call_count.attr,
NULL,
};
diff --git a/audiometrics/uapi/audiometrics_api.h b/audiometrics/uapi/audiometrics_api.h
index 7877fd7..1a313f1 100644
--- a/audiometrics/uapi/audiometrics_api.h
+++ b/audiometrics/uapi/audiometrics_api.h
@@ -45,6 +45,17 @@ enum amcs_params_op {
AMCS_OP_AMS_INCREASE,
AMCS_OP_CCA,
AMCS_OP_CCA_INCREASE,
+ AMCS_OP_VOICE_INFO_NOISE_LEVEL,
+ AMCS_OP_ADD_PCM_LATENCY,
+ AMCS_OP_PCM_ACTIVE_COUNT_INCREASE,
+ AMCS_OP_OFFLOAD_EFFECT_DURATION,
+ AMCS_OP_SOFTWARE_RESTART_INCREASE,
+ AMCS_OP_DSP_RECORD_USAGE_DURATION_INCREASE,
+ AMCS_OP_DSP_RECORD_USAGE_COUNT_INCREASE,
+ AMCS_OP_BT_ACTIVE_DURATION_INCREASE,
+ AMCS_OP_WAVES_VOLUME_INCREASE,
+ AMCS_OP_ADAPTED_INFO_FEATURE,
+ AMCS_OP_CALL_COUNT_INCREASE,
AMCS_OP_PARAMS_MAX,
};
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 34a2a35..bb6f3d8 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -817,6 +817,45 @@ err_mutex:
}
static DEVICE_ATTR_RW(vpbr_thld);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+static ssize_t reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+
+ dev_info(cs40l26->dev, "Reset: Event: %d; Count: %d; Time: (%lld,%lld).\n",
+ cs40l26->reset_event, cs40l26->reset_count, cs40l26->reset_time_s,
+ cs40l26->reset_time_e);
+ return sysfs_emit(buf, "%d\n", cs40l26->reset_event);
+}
+
+static ssize_t reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+ int choice;
+
+ ret = kstrtou32(buf, 10, &choice);
+ if (ret)
+ return ret;
+
+ if (choice == 0) {
+ cs40l26_make_reset_decision(cs40l26, __func__);
+ } else if (choice == 1) {
+ cs40l26->reset_event = CS40L26_RESET_EVENT_NONEED;
+ cs40l26->reset_count = 0;
+ queue_work(cs40l26->vibe_workqueue, &cs40l26->reset_work);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RW(reset);
+#endif
+
static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_num_waves.attr,
&dev_attr_die_temp.attr,
@@ -834,6 +873,9 @@ static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_redc_comp_enable.attr,
&dev_attr_swap_firmware.attr,
&dev_attr_vpbr_thld.attr,
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ &dev_attr_reset.attr,
+#endif
NULL,
};
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 02ffdc3..4dfdc85 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -466,6 +466,10 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
ATRACE_BEGIN("CS40L26_PM_STATE_WAKEUP");
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd, CS40L26_DSP_MBOX_RESET);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret)
+ dev_err(dev, "CS40L26_PM_STATE_WAKEUP failed");
+#endif
if (ret)
return ret;
@@ -479,11 +483,19 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd, CS40L26_DSP_MBOX_RESET);
if (ret)
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ break;
+#else
return ret;
+#endif
ret = cs40l26_dsp_state_get(cs40l26, &curr_state);
if (ret)
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ break;
+#else
return ret;
+#endif
if (curr_state == CS40L26_DSP_STATE_ACTIVE)
break;
@@ -491,7 +503,11 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
if (curr_state == CS40L26_DSP_STATE_STANDBY) {
ret = cs40l26_check_pm_lock(cs40l26, &dsp_lock);
if (ret)
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ break;
+#else
return ret;
+#endif
if (dsp_lock)
break;
@@ -499,6 +515,13 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
usleep_range(5000, 5100);
}
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret) {
+ dev_err(dev, "CS40L26_PM_STATE_PREVENT_HIBERNATE failed");
+ return ret;
+ }
+#endif
+
if (i == CS40L26_DSP_STATE_ATTEMPTS) {
dev_err(cs40l26->dev, "DSP not starting\n");
return -ETIMEDOUT;
@@ -511,6 +534,10 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
cs40l26->wksrc_sts = 0x00;
ret = cs40l26_dsp_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret)
+ dev_err(dev, "CS40L26_PM_STATE_ALLOW_HIBERNATE failed");
+#endif
if (ret)
return ret;
@@ -519,6 +546,10 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
cs40l26->wksrc_sts = 0x00;
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd, CS40L26_DSP_MBOX_RESET);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret)
+ dev_err(dev, "CS40L26_PM_STATE_SHUTDOWN failed");
+#endif
break;
default:
@@ -887,6 +918,14 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26,
case CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK:
case CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER:
cs40l26_remove_asp_scaling(cs40l26);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (cs40l26->effects_in_flight > 0) {
+ cs40l26->reset_event = CS40L26_RESET_EVENT_TRIGGER;
+ dev_err(cs40l26->dev,
+ "Invalid effects_in_flight (%d)! Reset at the next chip resume.",
+ cs40l26->effects_in_flight);
+ }
+#endif
cs40l26->effects_in_flight = cs40l26->effects_in_flight <= 0 ? 1 :
cs40l26->effects_in_flight + 1;
break;
@@ -1918,6 +1957,10 @@ static void cs40l26_set_gain_worker(struct work_struct *work)
err_mutex:
mutex_unlock(&cs40l26->lock);
cs40l26_pm_exit(cs40l26->dev);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret < 0)
+ cs40l26_make_reset_decision(cs40l26, __func__);
+#endif
}
static void cs40l26_vibe_start_worker(struct work_struct *work)
@@ -2011,6 +2054,10 @@ err_mutex:
mutex_unlock(&cs40l26->lock);
cs40l26_pm_exit(dev);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret < 0)
+ cs40l26_make_reset_decision(cs40l26, __func__);
+#endif
}
static void cs40l26_vibe_stop_worker(struct work_struct *work)
@@ -2061,6 +2108,10 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work)
mutex_exit:
mutex_unlock(&cs40l26->lock);
cs40l26_pm_exit(cs40l26->dev);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret < 0)
+ cs40l26_make_reset_decision(cs40l26, __func__);
+#endif
}
static void cs40l26_set_gain(struct input_dev *dev, u16 gain)
@@ -2961,8 +3012,11 @@ out_free:
memset(&cs40l26->upload_effect, 0, sizeof(struct ff_effect));
kfree(cs40l26->raw_custom_data);
cs40l26->raw_custom_data = NULL;
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (ret < 0)
+ cs40l26_make_reset_decision(cs40l26, __func__);
ATRACE_END();
-
+#endif
return ret;
}
@@ -3120,7 +3174,11 @@ static int cs40l26_erase_effect(struct input_dev *dev, int effect_id)
/* Wait for erase to finish */
flush_work(&cs40l26->erase_work);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ if (cs40l26->erase_ret < 0)
+ cs40l26_make_reset_decision(cs40l26, __func__);
ATRACE_END();
+#endif
return cs40l26->erase_ret;
}
@@ -4836,6 +4894,195 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
return cs40l26_no_wait_ram_indices_get(cs40l26, np);
}
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+static void cs40l26_reset_worker(struct work_struct *work)
+{
+ struct cs40l26_private *cs40l26 = container_of(work,
+ struct cs40l26_private, reset_work);
+ struct device *dev = cs40l26->dev;
+ int error;
+ u32 id;
+
+ if (IS_ERR_OR_NULL(cs40l26->reset_gpio)) {
+ dev_dbg(dev, "Invalid reset GPIO\n");
+ return;
+ }
+
+ dev_dbg(dev, "Reset start: Event: %d; Count: %d.",
+ cs40l26->reset_event, cs40l26->reset_count);
+
+ /* cs40l26_remove(cs40l26) */
+ if (cs40l26->fw_loaded)
+ disable_irq(cs40l26->irq);
+
+ if (cs40l26->vibe_workqueue) {
+ cancel_work_sync(&cs40l26->vibe_start_work);
+ cancel_work_sync(&cs40l26->vibe_stop_work);
+ cancel_work_sync(&cs40l26->set_gain_work);
+ cancel_work_sync(&cs40l26->upload_work);
+ cancel_work_sync(&cs40l26->erase_work);
+ }
+
+ /* Skip power off since REFCLK is shared and cannot be disabled. */
+
+ gpiod_set_value_cansleep(cs40l26->reset_gpio, 0);
+
+ /* cs40l26_probe(cs40l26, pdata) */
+ if (cs40l26->dev->of_node) {
+ error = cs40l26_handle_platform_data(cs40l26);
+ if (error)
+ goto err;
+ } else
+ dev_err(dev, "No DTSI to reset platform data\n");
+
+ /* Skip power on since REFCLK is shared and cannot be disabled. */
+
+ usleep_range(CS40L26_MIN_RESET_PULSE_WIDTH,
+ CS40L26_MIN_RESET_PULSE_WIDTH + 100);
+
+ gpiod_set_value_cansleep(cs40l26->reset_gpio, 1);
+
+ usleep_range(CS40L26_CONTROL_PORT_READY_DELAY,
+ CS40L26_CONTROL_PORT_READY_DELAY + 100);
+
+ /*
+ * The DSP may lock up if a haptic effect is triggered via
+ * GPI event or control port and the PLL is set to closed-loop.
+ *
+ * Set PLL to open-loop and remove any default GPI mappings
+ * to prevent this while the driver is loading and configuring RAM
+ * firmware.
+ */
+
+ error = cs40l26_set_pll_loop(cs40l26, CS40L26_PLL_REFCLK_SET_OPEN_LOOP);
+ if (error)
+ goto err;
+
+ error = cs40l26_erase_gpi_mapping(cs40l26, CS40L26_GPIO_MAP_A_PRESS);
+ if (error)
+ goto err;
+
+ error = cs40l26_erase_gpi_mapping(cs40l26, CS40L26_GPIO_MAP_A_RELEASE);
+ if (error)
+ goto err;
+
+ error = cs40l26_part_num_resolve(cs40l26);
+ if (error)
+ goto err;
+
+ /* Set LRA to high-z to avoid fault conditions */
+ error = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG,
+ CS40L26_SPK_DEFAULT_HIZ_MASK, 1 <<
+ CS40L26_SPK_DEFAULT_HIZ_SHIFT);
+ if (error) {
+ dev_err(dev, "Failed to set LRA to HI-Z\n");
+ goto err;
+ }
+
+ /* Load firmware at cs40l26_fw_swap() */
+ cs40l26->fw_defer = false;
+ if (cs40l26->calib_fw)
+ id = CS40L26_FW_CALIB_ID;
+ else
+ id = CS40L26_FW_ID;
+
+ if (cs40l26->fw_loaded)
+ enable_irq(cs40l26->irq);
+
+ error = cs40l26_fw_swap(cs40l26, id);
+ if (error)
+ goto err;
+
+ /* Reset vibe_state and counter/flag */
+ cs40l26->effects_in_flight = 0;
+ cs40l26->asp_enable = false;
+ cs40l26->vibe_state = CS40L26_VIBE_STATE_STOPPED;
+ sysfs_notify(&cs40l26->dev->kobj, "default", "vibe_state");
+
+ cs40l26->reset_event = CS40L26_RESET_EVENT_NONEED;
+ cs40l26->reset_count++;
+
+ dev_info(dev, "Reset end: Event: %d; Count: %d.",
+ cs40l26->reset_event, cs40l26->reset_count);
+ return;
+
+err:
+ cs40l26->reset_event = CS40L26_RESET_EVENT_FAILED;
+ cs40l26->reset_time_s = ktime_get_real_seconds();
+ dev_err(dev, "Reset end: Fatal error at count: %d.", cs40l26->reset_count);
+}
+
+static bool cs40l26_handle_reset_boundary_condition(struct cs40l26_private *cs40l26)
+{
+ time64_t delta_sec = 0;
+
+ cs40l26->reset_time_e = ktime_get_real_seconds();
+ delta_sec = cs40l26->reset_time_e - cs40l26->reset_time_s;
+
+ if (delta_sec > CS40L26_RESET_COOLDOWN_TIMEOUT_SEC || delta_sec < 0 ||
+ cs40l26->reset_count == 0) {
+ dev_info(cs40l26->dev, "Reset event: %d. Back to default.", cs40l26->reset_event);
+ cs40l26->reset_event = CS40L26_RESET_EVENT_ONGOING;
+ cs40l26->reset_time_s = cs40l26->reset_time_e;
+ cs40l26->reset_count = 0;
+ return true;
+ }
+
+ return false;
+}
+
+void cs40l26_make_reset_decision(struct cs40l26_private *cs40l26, const char *func)
+{
+ struct device *dev = cs40l26->dev;
+ bool trigger = false;
+
+ switch (cs40l26->reset_event) {
+ case CS40L26_RESET_EVENT_NONEED:
+ if (cs40l26_handle_reset_boundary_condition(cs40l26)) {
+ trigger = true;
+ break;
+ }
+
+ /*
+ * Implies the following conditions are true:
+ * 0 < cs40l26->reset_count && elapsed time <= CS40L26_RESET_COOLDOWN_TIMEOUT_SEC
+ */
+ if (cs40l26->reset_count < CS40L26_RESET_MAX_COUNT) {
+ cs40l26->reset_event = CS40L26_RESET_EVENT_ONGOING;
+ trigger = true;
+ } else {
+ /* Enters the cooldown mode if reset too many times in a period. */
+ cs40l26->reset_event = CS40L26_RESET_EVENT_COOLDOWN;
+ cs40l26->reset_time_s = cs40l26->reset_time_e;
+ }
+ break;
+ case CS40L26_RESET_EVENT_TRIGGER:
+ cs40l26->reset_count = 0;
+ cs40l26_handle_reset_boundary_condition(cs40l26);
+ trigger = true;
+ break;
+ case CS40L26_RESET_EVENT_ONGOING:
+ break;
+ case CS40L26_RESET_EVENT_FAILED:
+ fallthrough;
+ case CS40L26_RESET_EVENT_COOLDOWN:
+ if (cs40l26_handle_reset_boundary_condition(cs40l26))
+ trigger = true;
+
+ break;
+ default:
+ dev_err(dev, "Invalid reset event!");
+ }
+
+ if (trigger) {
+ dev_info(dev, "Queue reset work after %s", func);
+ queue_work(cs40l26->vibe_workqueue, &cs40l26->reset_work);
+ } else
+ dev_info(dev, "Reset event: %d. Skip this trigger from %s.", cs40l26->reset_event,
+ func);
+}
+#endif
+
int cs40l26_probe(struct cs40l26_private *cs40l26,
struct cs40l26_platform_data *pdata)
{
@@ -4856,6 +5103,12 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
INIT_WORK(&cs40l26->set_gain_work, cs40l26_set_gain_worker);
INIT_WORK(&cs40l26->upload_work, cs40l26_upload_worker);
INIT_WORK(&cs40l26->erase_work, cs40l26_erase_worker);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ INIT_WORK(&cs40l26->reset_work, cs40l26_reset_worker);
+ cs40l26->reset_event = CS40L26_RESET_EVENT_NONEED;
+ cs40l26->reset_time_e = ktime_get_real_seconds();
+ cs40l26->reset_time_s = cs40l26->reset_time_e;
+#endif
ret = devm_regulator_bulk_get(dev, CS40L26_NUM_SUPPLIES,
cs40l26_supplies);
@@ -5108,6 +5361,9 @@ EXPORT_SYMBOL(cs40l26_resume_error_handle);
int cs40l26_resume(struct device *dev)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ int error;
+#endif
if (!cs40l26->pm_ready) {
dev_dbg(dev, "Resume call ignored\n");
@@ -5116,8 +5372,17 @@ int cs40l26_resume(struct device *dev)
dev_dbg(cs40l26->dev, "%s: Disabling hibernation\n", __func__);
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ error = cs40l26_pm_state_transition(cs40l26,
+ CS40L26_PM_STATE_PREVENT_HIBERNATE);
+ if (error < 0 || cs40l26->reset_event == CS40L26_RESET_EVENT_TRIGGER)
+ cs40l26_make_reset_decision(cs40l26, __func__);
+
+ return error;
+#else
return cs40l26_pm_state_transition(cs40l26,
CS40L26_PM_STATE_PREVENT_HIBERNATE);
+#endif
}
EXPORT_SYMBOL(cs40l26_resume);
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 05f0458..13f24db 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -42,6 +42,9 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+#include <linux/timekeeping.h>
+#endif
#include "cl_dsp.h"
#include "../../../gs-google/drivers/soc/google/vh/kernel/systrace.h"
@@ -668,6 +671,12 @@
#define CS40L26_TEST_KEY_UNLOCK_CODE1 0x00000055
#define CS40L26_TEST_KEY_UNLOCK_CODE2 0x000000AA
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+/* Reset Recovery */
+#define CS40L26_RESET_MAX_COUNT 10
+#define CS40L26_RESET_COOLDOWN_TIMEOUT_SEC 300
+#endif
+
/* DSP State */
#define CS40L26_DSP_STATE_HIBERNATE 0
#define CS40L26_DSP_STATE_SHUTDOWN 1
@@ -1432,6 +1441,16 @@ enum cs40l26_pm_state {
CS40L26_PM_STATE_SHUTDOWN,
};
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+enum cs40l26_reset_event {
+ CS40L26_RESET_EVENT_NONEED,
+ CS40L26_RESET_EVENT_TRIGGER,
+ CS40L26_RESET_EVENT_ONGOING,
+ CS40L26_RESET_EVENT_COOLDOWN,
+ CS40L26_RESET_EVENT_FAILED,
+};
+#endif
+
/* structs */
struct cs40l26_owt_section {
@@ -1568,6 +1587,13 @@ struct cs40l26_private {
bool dbg_fw_ym;
struct cl_dsp_debugfs *cl_dsp_db;
#endif
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+ struct work_struct reset_work;
+ enum cs40l26_reset_event reset_event;
+ u8 reset_count;
+ time64_t reset_time_s;
+ time64_t reset_time_e;
+#endif
};
struct cs40l26_codec {
@@ -1659,5 +1685,8 @@ void cs40l26_debugfs_init(struct cs40l26_private *cs40l26);
void cs40l26_debugfs_cleanup(struct cs40l26_private *cs40l26);
#endif
+#if IS_ENABLED(CONFIG_GOOG_CUST)
+void cs40l26_make_reset_decision(struct cs40l26_private *cs40l26, const char *func);
+#endif
#endif /* __CS40L26_H__ */