summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2022-02-08 13:00:32 +0800
committerTai Kuo <taikuo@google.com>2022-02-08 13:27:47 +0800
commitbdf53aab1b4368d3178ee07f7c3234001d839d58 (patch)
tree5be0390552954a6201383f4dfdec6a2623cb2d8b
parentf4c7647aefec271be24e987328fa3e38fef4b874 (diff)
downloadamplifiers-bdf53aab1b4368d3178ee07f7c3234001d839d58.tar.gz
cs40l26: merge CirrusLogic cs40l26 v4.0.0
Branch: v5.10-cs40l26 Tag: CS40L26-v4.0.0_5.10 Files: drivers/input/misc/cs40l26-i2c.c (No changes) drivers/input/misc/cs40l26-spi.c (No changes) drivers/input/misc/cs40l26-sysfs.c drivers/input/misc/cs40l26-tables.c drivers/input/misc/cs40l26.c include/linux/mfd/cs40l26.h sound/soc/codecs/cs40l26.c -> cs40l26-codec.c Features: - Devicetree Property "cirrus,gain-adjust" - For LRA-specific gain changes - Controls for f0 and ReDC Compensation - Sysfs control vmon_max for VMON logging maximum reporting - Allow multiple writes to one register in power on sequencer - Devicetree Property "cirrus,bst-expl-mode-disbale" - If present, will disable boost exploratory mode at the cost of the ability to reliably detect BST_SHORT_ERR instances - Imrpoved error handling for PM Runtime Resume errors Bug fixes: - Update error handling to avoid deadlock - Avoid hibernation when intializing power on sequencer - Updated handling of vibe_state to avoid race condition with firmware - Code Improvements Commits: e4b5d23 ASoC: cs40l26: Ensure recovery from PM Runtime Resume error 2d5cb03 input: cs40l26: Ensure recovery from PM Runtime Resume error (SKIP) 098d137 Documentation: cs40l26: Add DT boolean "cirrus,bst-expl-mode-disable" 30c30e7 input: cs40l26: Add DT boolean "cirrus,bst-expl-mode-disable" (SKIP) 0707fb3 Documentation: cs40l26: Add DT boolean "cirrus,vibe-state" 38592da input: cs40l26: Add DT boolean "cirrus,vibe-state" 7e0c02c input: cs40l26: Read IRQ1_STATUS after PM Runtime resume c903a82 input: cs40l26: Load calibration tuning file 26e93ab input: cs40l26: Print measured LE estimation value b8ef3b0 input: cs40l26: Disable Boost Exploratory Mode 2d8ef2c input: cs40l26: Allow multiple writes to an address in PSEQ 9f166cd input: cs40l26: Update vibe_state in mailbox handler 53f24ab input: cs40l26: Add vmon_max logging sysfs control c7708f5 input: cs40l26: Avoid hibernation when initializing PSEQ 337536d input: cs40l26: Add f0 and redc comp controls (SKIP) 6e1642c Documentation: cs40l26: Add DT property "gain-adjust" eb1fed7 input: cs40l26: Add DT property "gain-adjust" bc34f05 input: cs40l26: Move BST_IPK_FLAG unmask to proper function 0570df1 input: cs40l26: Update error handling procedure Bug: 205688153 Test: idlcli commands. Signed-off-by: Tai Kuo <taikuo@google.com> Change-Id: Ic4b7cc5664c32132125178f7a41ca7c1a916123e
-rw-r--r--cs40l26/cs40l26-codec.c36
-rw-r--r--cs40l26/cs40l26-sysfs.c518
-rw-r--r--cs40l26/cs40l26-tables.c8
-rw-r--r--cs40l26/cs40l26.c475
-rw-r--r--cs40l26/cs40l26.h55
5 files changed, 865 insertions, 227 deletions
diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c
index 001e39e..1b02580 100644
--- a/cs40l26/cs40l26-codec.c
+++ b/cs40l26/cs40l26-codec.c
@@ -297,7 +297,11 @@ static int cs40l26_i2s_vmon_get(struct snd_kcontrol *kcontrol,
int ret;
u32 val;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = regmap_read(cs40l26->regmap, CS40L26_SPKMON_VMON_DEC_OUT_DATA,
&val);
@@ -489,7 +493,11 @@ static int cs40l26_a2h_volume_get(struct snd_kcontrol *kcontrol,
if (ret)
return ret;
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = regmap_read(regmap, reg, &val);
if (ret)
@@ -526,7 +534,11 @@ static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol,
else
val = ucontrol->value.integer.value[0];
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = regmap_write(regmap, reg, val);
if (ret)
@@ -579,7 +591,11 @@ static int cs40l26_a2h_delay_get(struct snd_kcontrol *kcontrol,
if (ret)
return ret;
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = regmap_read(regmap, reg, &val);
if (ret) {
@@ -619,7 +635,11 @@ static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol,
else
val = ucontrol->value.integer.value[0];
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = regmap_write(regmap, reg, val);
if (ret)
@@ -769,7 +789,11 @@ static int cs40l26_pcm_hw_params(struct snd_pcm_substream *substream,
u8 asp_rx_wl, asp_rx_width, global_fs;
int ret, lrck;
- pm_runtime_get_sync(codec->dev);
+ ret = pm_runtime_get_sync(codec->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(codec->dev);
+ return ret;
+ }
lrck = params_rate(params);
switch (lrck) {
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 88c857c..93c1818 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -20,7 +20,11 @@ static ssize_t dsp_state_show(struct device *dev,
u8 dsp_state;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_dsp_state_get(cs40l26, &dsp_state);
@@ -47,7 +51,11 @@ static ssize_t halo_heartbeat_show(struct device *dev,
if (ret)
return ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = regmap_read(cs40l26->regmap, reg, &halo_heartbeat);
@@ -95,7 +103,11 @@ static ssize_t pm_stdby_timeout_ms_show(struct device *dev,
u32 timeout_ms;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms);
@@ -119,7 +131,11 @@ static ssize_t pm_stdby_timeout_ms_store(struct device *dev,
if (ret)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, timeout_ms);
@@ -140,7 +156,11 @@ static ssize_t pm_active_timeout_ms_show(struct device *dev,
u32 timeout_ms;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_pm_active_timeout_ms_get(cs40l26, &timeout_ms);
@@ -164,7 +184,11 @@ static ssize_t pm_active_timeout_ms_store(struct device *dev,
if (ret)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_pm_active_timeout_ms_set(cs40l26, timeout_ms);
@@ -184,6 +208,11 @@ static ssize_t vibe_state_show(struct device *dev,
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
unsigned int state;
+ if (!cs40l26->pdata.vibe_state_reporting) {
+ dev_err(cs40l26->dev, "cirrus,vibe-state not in DT\n");
+ return -EPERM;
+ }
+
mutex_lock(&cs40l26->lock);
state = cs40l26->vibe_state;
mutex_unlock(&cs40l26->lock);
@@ -257,7 +286,11 @@ static ssize_t owt_free_space_show(struct device *dev,
u32 reg, words;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_SIZE_XM",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
@@ -289,7 +322,11 @@ static ssize_t die_temp_show(struct device *dev,
int ret;
u32 val;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = regmap_read(regmap, CS40L26_GLOBAL_ENABLES, &val);
if (ret) {
@@ -330,7 +367,11 @@ static ssize_t num_waves_show(struct device *dev,
u32 nwaves;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_get_num_waves(cs40l26, &nwaves);
if (ret)
@@ -354,7 +395,11 @@ static ssize_t boost_disable_delay_show(struct device *dev,
u32 reg, boost_disable_delay;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
@@ -390,7 +435,11 @@ static ssize_t boost_disable_delay_store(struct device *dev,
boost_disable_delay > CS40L26_BOOST_DISABLE_DELAY_MAX)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
@@ -417,7 +466,12 @@ static ssize_t f0_offset_show(struct device *dev,
unsigned int reg, val;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OFFSET",
@@ -455,7 +509,12 @@ static ssize_t f0_offset_store(struct device *dev,
if (val > CS40L26_F0_OFFSET_MAX && val < CS40L26_F0_OFFSET_MIN)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OFFSET",
@@ -516,6 +575,165 @@ static ssize_t delay_before_stop_playback_us_store(struct device *dev,
}
static DEVICE_ATTR_RW(delay_before_stop_playback_us);
+static ssize_t f0_comp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&cs40l26->lock);
+
+ if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ ret = -EPERM;
+ goto err_mutex;
+ }
+
+ if (cs40l26->comp_enable_pend) {
+ ret = -EIO;
+ goto err_mutex;
+ }
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->comp_enable_f0);
+
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+
+ return ret;
+}
+
+static ssize_t f0_comp_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+ unsigned int val;
+ u32 reg, value;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ cs40l26->comp_enable_pend = true;
+ cs40l26->comp_enable_f0 = val > 0;
+
+ value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) |
+ (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT);
+
+ if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ ret = -EPERM;
+ } else {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ goto err_mutex;
+
+ ret = regmap_write(cs40l26->regmap, reg, value);
+ }
+
+ if (ret)
+ goto err_mutex;
+
+ ret = count;
+
+err_mutex:
+ cs40l26->comp_enable_pend = false;
+ mutex_unlock(&cs40l26->lock);
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ return ret;
+}
+static DEVICE_ATTR_RW(f0_comp_enable);
+
+static ssize_t redc_comp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&cs40l26->lock);
+
+ if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ ret = -EPERM;
+ goto err_mutex;
+ }
+
+ if (cs40l26->comp_enable_pend) {
+ ret = -EIO;
+ goto err_mutex;
+ }
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->comp_enable_redc);
+
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+
+ return ret;
+}
+
+static ssize_t redc_comp_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+ unsigned int val;
+ u32 reg, value;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+ mutex_lock(&cs40l26->lock);
+
+ cs40l26->comp_enable_pend = true;
+ cs40l26->comp_enable_redc = val > 0;
+
+ value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) |
+ (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT);
+
+ if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ ret = -EPERM;
+ } else {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ goto err_mutex;
+
+ ret = regmap_write(cs40l26->regmap, reg, value);
+ }
+
+ if (ret)
+ goto err_mutex;
+
+ ret = count;
+
+err_mutex:
+ cs40l26->comp_enable_pend = false;
+ mutex_unlock(&cs40l26->lock);
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ return ret;
+}
+static DEVICE_ATTR_RW(redc_comp_enable);
+
static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_num_waves.attr,
&dev_attr_die_temp.attr,
@@ -530,6 +748,8 @@ static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_boost_disable_delay.attr,
&dev_attr_f0_offset.attr,
&dev_attr_delay_before_stop_playback_us.attr,
+ &dev_attr_f0_comp_enable.attr,
+ &dev_attr_redc_comp_enable.attr,
NULL,
};
@@ -560,7 +780,12 @@ static ssize_t trigger_calibration_store(struct device *dev,
CS40L26_DSP_MBOX_CMD_PAYLOAD_MASK);
/* pm_runtime_put occurs is irq_handler after diagnostic is finished */
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
@@ -587,7 +812,12 @@ static ssize_t f0_measured_show(struct device *dev,
int ret;
u32 reg, f0_measured;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "F0_EST",
@@ -619,7 +849,12 @@ static ssize_t q_measured_show(struct device *dev,
int ret;
u32 reg, q_measured;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "Q_EST",
@@ -651,7 +886,12 @@ static ssize_t redc_measured_show(struct device *dev,
int ret;
u32 reg, redc_measured;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "RE_EST_STATUS",
@@ -683,7 +923,12 @@ static ssize_t redc_est_show(struct device *dev, struct device_attribute *attr,
int ret;
u32 reg, redc_est;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC",
@@ -720,7 +965,12 @@ static ssize_t redc_est_store(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC",
@@ -752,7 +1002,12 @@ static ssize_t f0_stored_show(struct device *dev,
int ret;
u32 reg, f0_stored;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OTP_STORED",
@@ -792,7 +1047,12 @@ static ssize_t f0_stored_store(struct device *dev,
f0_stored > CS40L26_F0_EST_MAX)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OTP_STORED",
@@ -824,7 +1084,12 @@ static ssize_t q_stored_show(struct device *dev, struct device_attribute *attr,
int ret;
u32 reg, q_stored;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "Q_STORED",
@@ -862,7 +1127,12 @@ static ssize_t q_stored_store(struct device *dev, struct device_attribute *attr,
if (ret || q_stored < CS40L26_Q_EST_MIN || q_stored > CS40L26_Q_EST_MAX)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "Q_STORED",
@@ -894,7 +1164,12 @@ static ssize_t redc_stored_show(struct device *dev,
int ret;
u32 reg, redc_stored;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_OTP_STORED",
@@ -931,7 +1206,12 @@ static ssize_t redc_stored_store(struct device *dev,
if (ret)
return ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_OTP_STORED",
@@ -963,7 +1243,12 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev,
int ret;
u32 reg, freq_span, freq_centre, f0_and_q_cal_time_ms;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_SPAN",
@@ -1009,7 +1294,12 @@ static ssize_t redc_cal_time_ms_show(struct device *dev,
int ret;
u32 reg, redc_playtime_ms, redc_total_cal_time_ms;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_PLAYTIME_MS",
@@ -1043,7 +1333,12 @@ static ssize_t logging_en_show(struct device *dev,
u32 reg, enable;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
ret = cl_dsp_get_reg(cs40l26->dsp, "ENABLE", CL_DSP_XM_UNPACKED_TYPE,
@@ -1069,10 +1364,11 @@ static ssize_t logging_en_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- u32 src_val = CS40L26_LOGGER_SRC_SIZE_MASK;
- u32 src_mask = src_val | CS40L26_LOGGER_SRC_ID_MASK;
- u32 enable, reg;
- int ret;
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *cdev = cs40l26->dev;
+ struct cl_dsp *dsp = cs40l26->dsp;
+ u32 enable, reg, src_count, src;
+ int ret, i;
ret = kstrtou32(buf, 10, &enable);
if (ret)
@@ -1081,58 +1377,101 @@ static ssize_t logging_en_store(struct device *dev,
if (enable != 0 && enable != 1)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev);
+ return ret;
+ }
+
mutex_lock(&cs40l26->lock);
- ret = cl_dsp_get_reg(cs40l26->dsp, "ENABLE", CL_DSP_XM_UNPACKED_TYPE,
+ ret = cl_dsp_get_reg(dsp, "COUNT", CL_DSP_XM_UNPACKED_TYPE,
CS40L26_LOGGER_ALGO_ID, &reg);
if (ret)
goto exit_mutex;
- ret = regmap_write(cs40l26->regmap, reg, enable);
+ ret = regmap_read(regmap, reg, &src_count);
if (ret) {
- dev_err(cs40l26->dev, "Failed to %s logging\n",
- enable ? "enable" : "disable");
+ dev_err(cdev, "Failed to get logger source count\n");
goto exit_mutex;
}
- if (!enable)
- goto exit_mutex;
-
- ret = cl_dsp_get_reg(cs40l26->dsp, "COUNT", CL_DSP_XM_UNPACKED_TYPE,
+ ret = cl_dsp_get_reg(dsp, "SOURCE", CL_DSP_XM_UNPACKED_TYPE,
CS40L26_LOGGER_ALGO_ID, &reg);
if (ret)
goto exit_mutex;
- ret = regmap_write(cs40l26->regmap, reg, 2);
- if (ret) {
- dev_err(cs40l26->dev, "Failed to set up logging sources\n");
+ if (cs40l26->fw.id == CS40L26_FW_ID) {
+ if (src_count != CS40L26_LOGGER_SRC_COUNT) {
+ dev_err(cdev, "Unexpected source count %u\n",
+ src_count);
+ ret = -EINVAL;
+ goto exit_mutex;
+ }
+
+ ret = regmap_read(regmap, reg, &src);
+ if (ret) {
+ dev_err(cdev, "Failed to get Logger Source\n");
+ goto exit_mutex;
+ }
+
+ src &= CS40L26_LOGGER_SRC_ID_MASK;
+ src >>= CS40L26_LOGGER_SRC_ID_SHIFT;
+
+ if (src != CS40L26_LOGGER_SRC_ID_VMON) {
+ dev_err(cdev, "Invalid Logger Source %u\n", src);
+ ret = -EINVAL;
+ goto exit_mutex;
+ }
+ } else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ if (src_count != CS40L26_LOGGER_SRC_COUNT_CALIB) {
+ dev_err(cdev, "Unexpected source count %u\n",
+ src_count);
+ ret = -EINVAL;
+ goto exit_mutex;
+ }
+
+ for (i = 0; i < src_count; i++) {
+ ret = regmap_read(regmap, reg +
+ (i * CL_DSP_BYTES_PER_WORD), &src);
+ if (ret) {
+ dev_err(dev, "Failed to get Logger Source\n");
+ goto exit_mutex;
+ }
+
+ src &= CS40L26_LOGGER_SRC_ID_MASK;
+ src >>= CS40L26_LOGGER_SRC_ID_SHIFT;
+
+ if (src != (i + 1)) {
+ dev_err(cdev, "Invalid Logger Source %u\n",
+ src);
+ ret = -EINVAL;
+ goto exit_mutex;
+ }
+ }
+ } else {
+ dev_err(cdev, "Invalid firmware ID 0x%06X\n",
+ cs40l26->fw.id);
goto exit_mutex;
}
- ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE", CL_DSP_XM_UNPACKED_TYPE,
+ ret = cl_dsp_get_reg(dsp, "ENABLE", CL_DSP_XM_UNPACKED_TYPE,
CS40L26_LOGGER_ALGO_ID, &reg);
if (ret)
goto exit_mutex;
- ret = regmap_update_bits(cs40l26->regmap, reg, src_mask, src_val |
- (1 << CS40L26_LOGGER_SRC_ID_SHIFT));
- if (ret) {
- dev_err(cs40l26->dev, "Failed to set BEMF Logger Source ID\n");
- goto exit_mutex;
- }
-
- ret = regmap_update_bits(cs40l26->regmap, reg + 4, src_mask, src_val |
- (2 << CS40L26_LOGGER_SRC_ID_SHIFT));
+ ret = regmap_write(regmap, reg, enable);
if (ret)
- dev_err(cs40l26->dev, "Failed to set VBST Logger Source ID\n");
+ dev_err(cdev, "Failed to %s logging\n",
+ enable ? "enable" : "disable");
exit_mutex:
mutex_unlock(&cs40l26->lock);
- pm_runtime_mark_last_busy(cs40l26->dev);
- pm_runtime_put_autosuspend(cs40l26->dev);
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
- return count;
+
+ return ret ? ret : count;
}
static DEVICE_ATTR_RW(logging_en);
@@ -1151,7 +1490,11 @@ static ssize_t logging_max_reset_store(struct device *dev,
if (rst != 1)
return -EINVAL;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_DSP_MBOX_CMD_LOGGER_MAX_RESET, CS40L26_DSP_MBOX_RESET);
@@ -1171,6 +1514,11 @@ static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr,
u32 reg, max_bemf;
int ret;
+ if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) {
+ dev_err(cs40l26->dev, "Calib. FW required for BEMF logging\n");
+ return -EPERM;
+ }
+
pm_runtime_get_sync(cs40l26->dev);
mutex_lock(&cs40l26->lock);
@@ -1179,7 +1527,8 @@ static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr,
if (ret)
goto err_mutex;
- ret = regmap_read(cs40l26->regmap, reg + 4, &max_bemf);
+ ret = regmap_read(cs40l26->regmap, reg +
+ CS40L26_LOGGER_DATA_1_MAX_OFFSET, &max_bemf);
if (ret)
dev_err(cs40l26->dev, "Failed to get max. back EMF\n");
@@ -1203,6 +1552,11 @@ static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr,
u32 reg, max_vbst;
int ret;
+ if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) {
+ dev_err(cs40l26->dev, "Calib. FW required for VBST logging\n");
+ return -EPERM;
+ }
+
pm_runtime_get_sync(cs40l26->dev);
mutex_lock(&cs40l26->lock);
@@ -1211,9 +1565,10 @@ static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr,
if (ret)
goto err_mutex;
- ret = regmap_read(cs40l26->regmap, reg + 16, &max_vbst);
+ ret = regmap_read(cs40l26->regmap, reg +
+ CS40L26_LOGGER_DATA_2_MAX_OFFSET, &max_vbst);
if (ret)
- dev_err(cs40l26->dev, "Failed to get max. back EMF\n");
+ dev_err(cs40l26->dev, "Failed to get max. VBST\n");
err_mutex:
mutex_unlock(&cs40l26->lock);
@@ -1227,6 +1582,44 @@ err_mutex:
}
static DEVICE_ATTR_RO(max_vbst);
+static ssize_t max_vmon_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ u32 reg, max_vmon;
+ u8 offset;
+ int ret;
+
+ if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+ offset = CS40L26_LOGGER_DATA_3_MAX_OFFSET;
+ else
+ offset = CS40L26_LOGGER_DATA_1_MAX_OFFSET;
+
+ pm_runtime_get_sync(cs40l26->dev);
+ mutex_lock(&cs40l26->lock);
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "DATA", CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_LOGGER_ALGO_ID, &reg);
+ if (ret)
+ goto err_mutex;
+
+ ret = regmap_read(cs40l26->regmap, reg + offset, &max_vmon);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to get max. VMON\n");
+
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "0x%06X\n", max_vmon);
+}
+static DEVICE_ATTR_RO(max_vmon);
+
static ssize_t calib_fw_load_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1273,6 +1666,7 @@ static struct attribute *cs40l26_dev_attrs_cal[] = {
&dev_attr_calib_fw_load.attr,
&dev_attr_max_vbst.attr,
&dev_attr_max_bemf.attr,
+ &dev_attr_max_vmon.attr,
&dev_attr_logging_max_reset.attr,
&dev_attr_logging_en.attr,
&dev_attr_trigger_calibration.attr,
diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c
index f525e65..d9dbe1d 100644
--- a/cs40l26/cs40l26-tables.c
+++ b/cs40l26/cs40l26-tables.c
@@ -34,6 +34,13 @@ const struct regmap_config cs40l26_regmap = {
.cache_type = REGCACHE_NONE,
};
+const struct reg_sequence cs40l26_a1_errata[CS40L26_ERRATA_A1_NUM_WRITES] = {
+ {CS40L26_PLL_REFCLK_DETECT_0, 0x00000000},
+ {CS40L26_TEST_KEY_CTRL, CS40L26_TEST_KEY_UNLOCK_CODE1},
+ {CS40L26_TEST_KEY_CTRL, CS40L26_TEST_KEY_UNLOCK_CODE2},
+ {CS40L26_TEST_LBST, CS40L26_DISABLE_EXPL_MODE},
+};
+
const struct dev_pm_ops cs40l26_pm_ops = {
SET_RUNTIME_PM_OPS(cs40l26_suspend, cs40l26_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(cs40l26_sys_suspend, cs40l26_sys_resume)
@@ -325,6 +332,7 @@ bool cs40l26_readable_reg(struct device *dev, unsigned int reg)
case CS40L26_DCM_FORCE:
case CS40L26_VBST_OVP:
case CS40L26_BST_DCR:
+ case CS40L26_TEST_LBST:
case CS40L26_VPI_LIMIT_MODE:
case CS40L26_VPI_LIMITING:
case CS40L26_VPI_VP_THLDS:
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 19b1b39..5f8f6da 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -421,7 +421,6 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
break;
case CS40L26_PM_STATE_ALLOW_HIBERNATE:
- case CS40L26_PM_STATE_SHUTDOWN:
cs40l26->wksrc_sts = 0x00;
ret = cs40l26_dsp_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd);
@@ -429,6 +428,12 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
return ret;
break;
+ case CS40L26_PM_STATE_SHUTDOWN:
+ cs40l26->wksrc_sts = 0x00;
+ ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
+ cmd, CS40L26_DSP_MBOX_RESET);
+
+ break;
default:
dev_err(dev, "Invalid PM state: %u\n", state);
return -EINVAL;
@@ -476,52 +481,6 @@ static int cs40l26_dsp_start(struct cs40l26_private *cs40l26)
return 0;
}
-static int cs40l26_dsp_wake(struct cs40l26_private *cs40l26)
-{
- u8 dsp_state;
- int ret;
-
- ret = cs40l26_pm_state_transition(cs40l26, CS40L26_PM_STATE_WAKEUP);
- if (ret)
- return ret;
-
- ret = cs40l26_pm_state_transition(cs40l26,
- CS40L26_PM_STATE_PREVENT_HIBERNATE);
- if (ret)
- return ret;
-
- ret = cs40l26_dsp_state_get(cs40l26, &dsp_state);
- if (ret)
- return ret;
-
- if (dsp_state != CS40L26_DSP_STATE_STANDBY &&
- dsp_state != CS40L26_DSP_STATE_ACTIVE) {
- dev_err(cs40l26->dev, "Failed to wake DSP\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cs40l26_dsp_shutdown(struct cs40l26_private *cs40l26)
-{
- u32 timeout_ms;
- int ret;
-
- ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms);
- if (ret)
- return ret;
-
- ret = cs40l26_pm_state_transition(cs40l26, CS40L26_PM_STATE_SHUTDOWN);
- if (ret)
- return ret;
-
- usleep_range(CS40L26_MS_TO_US(timeout_ms),
- CS40L26_MS_TO_US(timeout_ms) + 100);
-
- return 0;
-}
-
static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26)
{
u32 halo_state, timeout_ms;
@@ -570,14 +529,6 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26)
return -EINVAL;
}
- /* errata write fixing indeterminent PLL lock time */
- ret = regmap_update_bits(cs40l26->regmap, CS40L26_PLL_REFCLK_DETECT_0,
- CS40L26_PLL_REFCLK_DET_EN_MASK, 0);
- if (ret) {
- dev_err(cs40l26->dev, "Failed to disable PLL refclk detect\n");
- return ret;
- }
-
ret = regmap_write(cs40l26->regmap, CS40L26_DSP1_CCM_CORE_CONTROL,
CS40L26_DSP_CCM_CORE_KILL);
if (ret)
@@ -678,6 +629,16 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
case CS40L26_DSP_MBOX_COMPLETE_I2S:
dev_dbg(dev, "Mailbox: COMPLETE_I2S\n");
break;
+ case CS40L26_DSP_MBOX_TRIGGER_CP:
+ if (!cs40l26->pdata.vibe_state_reporting) {
+ dev_err(dev, "cirrus,vibe-state not in DT\n");
+ return -EPERM;
+ }
+
+ dev_dbg(dev, "Mailbox: TRIGGER_CP\n");
+ cs40l26_vibe_state_update(cs40l26,
+ CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK);
+ break;
case CS40L26_DSP_MBOX_TRIGGER_GPIO:
dev_dbg(dev, "Mailbox: TRIGGER_GPIO\n");
cs40l26_vibe_state_update(cs40l26,
@@ -837,20 +798,13 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26,
EXPORT_SYMBOL(cs40l26_vibe_state_update);
static int cs40l26_error_release(struct cs40l26_private *cs40l26,
- unsigned int err_rls, bool bst_err)
+ unsigned int err_rls)
{
struct regmap *regmap = cs40l26->regmap;
struct device *dev = cs40l26->dev;
u32 err_sts, err_cfg;
int ret;
- /* Boost related errors must be handled with DSP turned off */
- if (bst_err) {
- ret = cs40l26_dsp_shutdown(cs40l26);
- if (ret)
- return ret;
- }
-
ret = regmap_read(regmap, CS40L26_ERROR_RELEASE, &err_sts);
if (ret) {
dev_err(cs40l26->dev, "Failed to get error status\n");
@@ -876,16 +830,8 @@ static int cs40l26_error_release(struct cs40l26_private *cs40l26,
err_cfg &= ~BIT(err_rls);
ret = regmap_write(cs40l26->regmap, CS40L26_ERROR_RELEASE, err_cfg);
- if (ret) {
+ if (ret)
dev_err(dev, "Actuator Safe Mode release sequence failed\n");
- return ret;
- }
-
- if (bst_err) {
- ret = cs40l26_dsp_wake(cs40l26);
- if (ret)
- return ret;
- }
return ret;
}
@@ -895,9 +841,8 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
{
struct device *dev = cs40l26->dev;
u32 err_rls = 0;
- unsigned int reg, val;
- bool bst_err = false;
int ret = 0;
+ unsigned int reg, val;
switch (irq1) {
case CS40L26_IRQ1_GPIO1_RISE:
@@ -992,23 +937,19 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
"BST voltage returned below warning threshold\n");
break;
case CS40L26_IRQ1_BST_OVP_ERR:
- dev_err(dev, "BST overvolt. error, CS40L26 shutting down\n");
+ dev_err(dev, "BST overvolt. error\n");
err_rls = CS40L26_BST_OVP_ERR_RLS;
- bst_err = true;
break;
case CS40L26_IRQ1_BST_DCM_UVP_ERR:
- dev_err(dev,
- "BST undervolt. error, CS40L26 shutting down\n");
+ dev_err(dev, "BST undervolt. error\n");
err_rls = CS40L26_BST_UVP_ERR_RLS;
- bst_err = true;
break;
case CS40L26_IRQ1_BST_SHORT_ERR:
- dev_err(dev, "LBST short detected, CS40L26 shutting down\n");
+ dev_err(dev, "LBST short detected\n");
err_rls = CS40L26_BST_SHORT_ERR_RLS;
- bst_err = true;
break;
case CS40L26_IRQ1_BST_IPK_FLAG:
- dev_warn(dev, "Current is being limited by LBST inductor\n");
+ dev_dbg(dev, "Current is being limited by LBST inductor\n");
break;
case CS40L26_IRQ1_TEMP_WARN_RISE:
dev_err(dev, "Die overtemperature warning\n");
@@ -1019,11 +960,11 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
break;
case CS40L26_IRQ1_TEMP_ERR:
dev_err(dev,
- "Die overtemperature error, CS40L26 shutting down\n");
+ "Die overtemperature error\n");
err_rls = CS40L26_TEMP_ERR_RLS;
break;
case CS40L26_IRQ1_AMP_ERR:
- dev_err(dev, "AMP short detected, CS40L26 shutting down\n");
+ dev_err(dev, "AMP short detected\n");
err_rls = CS40L26_AMP_SHORT_ERR_RLS;
break;
case CS40L26_IRQ1_DC_WATCHDOG_RISE:
@@ -1046,7 +987,7 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
}
if (err_rls)
- ret = cs40l26_error_release(cs40l26, err_rls, bst_err);
+ ret = cs40l26_error_release(cs40l26, err_rls);
err:
regmap_write(cs40l26->regmap, CS40L26_IRQ1_EINT_1, BIT(irq1));
@@ -1185,30 +1126,30 @@ static irqreturn_t cs40l26_irq(int irq, void *data)
unsigned long num_irq;
int ret;
- if (cs40l26_dsp_read(cs40l26, CS40L26_IRQ1_STATUS, &sts)) {
- dev_err(dev, "Failed to read IRQ1 Status\n");
- return IRQ_NONE;
- }
-
- if (sts != CS40L26_IRQ_STATUS_ASSERT) {
- dev_err(dev, "IRQ1 asserted with no pending interrupts\n");
- return IRQ_NONE;
- }
-
- ret = pm_runtime_get_sync(dev);
-
- mutex_lock(&cs40l26->lock);
-
- if (ret < 0) {
- pm_runtime_set_active(dev);
+ if (pm_runtime_get_sync(dev) < 0) {
+ cs40l26_resume_error_handle(dev);
- dev_err(dev, "PM Runtime Resume failed, interrupts missed\n");
+ dev_err(dev, "Interrupts missed\n");
cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_1,
CS40L26_IRQ_EINT1_ALL_MASK);
cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_2,
CS40L26_IRQ_EINT2_ALL_MASK);
+ return IRQ_NONE;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ if (regmap_read(regmap, CS40L26_IRQ1_STATUS, &sts)) {
+ dev_err(dev, "Failed to read IRQ1 Status\n");
+ ret = IRQ_NONE;
+ goto err;
+ }
+
+ if (sts != CS40L26_IRQ_STATUS_ASSERT) {
+ dev_err(dev, "IRQ1 asserted with no pending interrupts\n");
+ ret = IRQ_NONE;
goto err;
}
@@ -1305,7 +1246,7 @@ static int cs40l26_pseq_find_end(struct cs40l26_private *cs40l26,
}
static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
- u32 data, u8 op_code)
+ u32 data, bool update, u8 op_code)
{
struct device *dev = cs40l26->dev;
bool is_new = true;
@@ -1360,7 +1301,7 @@ static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
if (op->words[0] == op_words[0] && (op->words[1] & op_mask) ==
- (op_words[1] & op_mask)) {
+ (op_words[1] & op_mask) && update) {
if (op->size != num_op_words) {
dev_err(dev, "Failed to replace PSEQ op.\n");
ret = -EINVAL;
@@ -1432,7 +1373,7 @@ static int cs40l26_pseq_multi_write(struct cs40l26_private *cs40l26,
for (i = 0; i < num_regs; i++) {
ret = cs40l26_pseq_write(cs40l26, reg_seq[i].reg,
- reg_seq[i].def, op_code);
+ reg_seq[i].def, update, op_code);
if (ret)
return ret;
}
@@ -1539,12 +1480,13 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26)
int ret;
ret = cs40l26_pseq_write(cs40l26, CS40L26_NGATE1_INPUT,
- CS40L26_DATA_SRC_DSP1TX4, CS40L26_PSEQ_OP_WRITE_L16);
+ CS40L26_DATA_SRC_DSP1TX4, true,
+ CS40L26_PSEQ_OP_WRITE_L16);
if (ret)
return ret;
ret = cs40l26_pseq_write(cs40l26, CS40L26_MIXER_NGATE_CH1_CFG,
- CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW,
+ CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW, true,
CS40L26_PSEQ_OP_WRITE_FULL);
if (ret) {
dev_err(dev, "Failed to sequence Mixer Noise Gate\n");
@@ -1554,7 +1496,7 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26)
/* set SPK_DEFAULT_HIZ to 1 */
ret = cs40l26_pseq_write(cs40l26, CS40L26_TST_DAC_MSM_CONFIG,
CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_H16,
- CS40L26_PSEQ_OP_WRITE_H16);
+ true, CS40L26_PSEQ_OP_WRITE_H16);
if (ret)
dev_err(dev, "Failed to sequence register default updates\n");
@@ -1598,7 +1540,7 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg,
}
return cs40l26_pseq_write(cs40l26, reg,
- new_mask, CS40L26_PSEQ_OP_WRITE_FULL);
+ new_mask, true, CS40L26_PSEQ_OP_WRITE_FULL);
}
static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
@@ -1685,7 +1627,11 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
(ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) |
(owt << CS40L26_BTN_OWT_SHIFT);
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = regmap_write(cs40l26->regmap, reg, write_val);
if (ret)
@@ -1728,7 +1674,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work)
u32 reg;
int ret;
- pm_runtime_get_sync(cs40l26->dev);
+ if (pm_runtime_get_sync(cs40l26->dev) < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev);
+
mutex_lock(&cs40l26->lock);
if (cs40l26->vibe_state == CS40L26_VIBE_STATE_ASP) {
@@ -1774,7 +1722,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
dev_dbg(dev, "%s\n", __func__);
- pm_runtime_get_sync(dev);
+ if (pm_runtime_get_sync(dev) < 0)
+ return cs40l26_resume_error_handle(dev);
+
mutex_lock(&cs40l26->lock);
effect = cs40l26->trigger_effect;
@@ -1834,8 +1784,6 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
index, CS40L26_DSP_MBOX_RESET);
if (ret)
goto err_mutex;
- cs40l26_vibe_state_update(cs40l26,
- CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK);
break;
default:
dev_err(dev, "Invalid waveform type: 0x%X\n",
@@ -1844,6 +1792,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
goto err_mutex;
}
+ if (!cs40l26->pdata.vibe_state_reporting)
+ cs40l26_vibe_state_update(cs40l26,
+ CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK);
err_mutex:
mutex_unlock(&cs40l26->lock);
pm_runtime_mark_last_busy(dev);
@@ -1858,7 +1809,9 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work)
dev_dbg(cs40l26->dev, "%s\n", __func__);
- pm_runtime_get_sync(cs40l26->dev);
+ if (pm_runtime_get_sync(cs40l26->dev) < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev);
+
mutex_lock(&cs40l26->lock);
if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC)
@@ -2143,7 +2096,11 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data,
unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base;
int ret;
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = cl_dsp_get_reg(dsp, "OWT_NEXT_XM", CL_DSP_XM_UNPACKED_TYPE,
CS40L26_VIBEGEN_ALGO_ID, &reg);
@@ -2560,7 +2517,9 @@ static void cs40l26_upload_worker(struct work_struct *work)
u16 index, bank;
bool pwle;
- pm_runtime_get_sync(cdev);
+ if (pm_runtime_get_sync(cdev) < 0)
+ return cs40l26_resume_error_handle(cdev);
+
mutex_lock(&cs40l26->lock);
effect = &cs40l26->upload_effect;
@@ -2841,8 +2800,10 @@ static void cs40l26_erase_worker(struct work_struct *work)
int effect_id;
u32 index;
+ if (pm_runtime_get_sync(cs40l26->dev) < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev);
+
mutex_lock(&cs40l26->lock);
- pm_runtime_get_sync(cs40l26->dev);
effect_id = cs40l26->erase_effect->id;
index = cs40l26->trigger_indices[effect_id];
@@ -3037,19 +2998,24 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
} else {
cs40l26->fw.id = id;
- if (id == CS40L26_FW_ID)
+ if (id == CS40L26_FW_ID) {
cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
- if (id == CS40L26_FW_CALIB_ID)
+ cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_RT;
+ } else if (id == CS40L26_FW_CALIB_ID) {
cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV;
-
- cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_MAX;
+ cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_CAL;
+ } else {
+ dev_err(cs40l26->dev, "Invalid firmware ID 0x%06X\n",
+ id);
+ return -EINVAL;
+ }
if (!cs40l26->fw.coeff_files)
cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
- CS40L26_TUNING_FILES_MAX, sizeof(char *),
+ cs40l26->fw.num_coeff_files, sizeof(char *),
GFP_KERNEL);
- for (i = 0; i < CS40L26_TUNING_FILES_MAX; i++) {
+ for (i = 0; i < cs40l26->fw.num_coeff_files; i++) {
if (!cs40l26->fw.coeff_files[i]) {
cs40l26->fw.coeff_files[i] =
devm_kzalloc(cs40l26->dev,
@@ -3063,15 +3029,23 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
strncpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
CS40L26_WT_FILE_NAME_LEN);
- strncpy(cs40l26->fw.coeff_files[1],
- CS40L26_A2H_TUNING_FILE_NAME,
- CS40L26_A2H_TUNING_FILE_NAME_LEN);
- strncpy(cs40l26->fw.coeff_files[2],
- CS40L26_SVC_TUNING_FILE_NAME,
- CS40L26_SVC_TUNING_FILE_NAME_LEN);
- strncpy(cs40l26->fw.coeff_files[3],
- CS40L26_DVL_FILE_NAME,
- CS40L26_DVL_FILE_NAME_LEN);
+
+ if (id == CS40L26_FW_ID) {
+ strncpy(cs40l26->fw.coeff_files[1],
+ CS40L26_A2H_TUNING_FILE_NAME,
+ CS40L26_A2H_TUNING_FILE_NAME_LEN);
+ strncpy(cs40l26->fw.coeff_files[2],
+ CS40L26_SVC_TUNING_FILE_NAME,
+ CS40L26_SVC_TUNING_FILE_NAME_LEN);
+ strncpy(cs40l26->fw.coeff_files[3],
+ CS40L26_DVL_FILE_NAME,
+ CS40L26_DVL_FILE_NAME_LEN);
+ } else {
+ strncpy(cs40l26->fw.coeff_files[1],
+ CS40L26_CALIB_BIN_FILE_NAME,
+ CS40L26_CALIB_BIN_FILE_NAME_LEN);
+ }
+
ret = cl_dsp_wavetable_create(cs40l26->dsp,
CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM,
CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
@@ -3155,7 +3129,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_BLOCK_ENABLES2,
- val, CS40L26_PSEQ_OP_WRITE_FULL);
+ val, true, CS40L26_PSEQ_OP_WRITE_FULL);
if (ret) {
dev_err(dev, "Failed to sequence brownout prevention\n");
return ret;
@@ -3256,7 +3230,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, val,
- CS40L26_PSEQ_OP_WRITE_FULL);
+ true, CS40L26_PSEQ_OP_WRITE_FULL);
if (ret)
return ret;
}
@@ -3359,7 +3333,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, val,
- CS40L26_PSEQ_OP_WRITE_FULL);
+ true, CS40L26_PSEQ_OP_WRITE_FULL);
if (ret)
return ret;
}
@@ -3456,7 +3430,7 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_BST_DCM_CTL,
- val, CS40L26_PSEQ_OP_WRITE_FULL);
+ val, true, CS40L26_PSEQ_OP_WRITE_FULL);
}
return ret;
@@ -3528,7 +3502,7 @@ static int calib_device_tree_config(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_1, bst_ctl,
- CS40L26_PSEQ_OP_WRITE_L16);
+ true, CS40L26_PSEQ_OP_WRITE_L16);
if (ret)
return ret;
@@ -3549,11 +3523,10 @@ static int calib_device_tree_config(struct cs40l26_private *cs40l26)
}
ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_2,
- bst_ctl_cfg, CS40L26_PSEQ_OP_WRITE_FULL);
+ bst_ctl_cfg, true, CS40L26_PSEQ_OP_WRITE_FULL);
}
- return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
- BIT(CS40L26_IRQ1_BST_IPK_FLAG));
+ return ret;
}
static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
@@ -3576,8 +3549,13 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
return ret;
}
- return cs40l26_pseq_write(cs40l26, CS40L26_BST_IPK_CTL, val,
- CS40L26_PSEQ_OP_WRITE_L16);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_BST_IPK_CTL, val,
+ true, CS40L26_PSEQ_OP_WRITE_L16);
+ if (ret)
+ return ret;
+
+ return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
+ BIT(CS40L26_IRQ1_BST_IPK_FLAG));
}
static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
@@ -3611,12 +3589,103 @@ static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
return ret;
}
+static int cs40l26_lbst_short_test(struct cs40l26_private *cs40l26)
+{
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *dev = cs40l26->dev;
+ unsigned int err;
+ int ret;
+
+ ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2,
+ CS40L26_BST_CTL_SEL_MASK, CS40L26_BST_CTL_SEL_FIXED);
+ if (ret) {
+ dev_err(dev, "Failed to set VBST_CTL_2\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_1,
+ CS40L26_BST_CTL_MASK, CS40L26_BST_CTL_VP);
+ if (ret) {
+ dev_err(dev, "Failed to set VBST_CTL_1\n");
+ return ret;
+ }
+
+ /* Set GLOBAL_EN; safe because DSP is guaranteed to be off here */
+ ret = regmap_update_bits(regmap, CS40L26_GLOBAL_ENABLES,
+ CS40L26_GLOBAL_EN_MASK, 1);
+ if (ret) {
+ dev_err(dev, "Failed to set GLOBAL_EN\n");
+ return ret;
+ }
+
+ /* Wait until boost converter is guaranteed to be powered up */
+ usleep_range(CS40L26_BST_TIME_MIN_US, CS40L26_BST_TIME_MAX_US);
+
+ ret = regmap_read(regmap, CS40L26_ERROR_RELEASE, &err);
+ if (ret) {
+ dev_err(dev, "Failed to get ERROR_RELEASE contents\n");
+ return ret;
+ }
+
+ if (err & BIT(CS40L26_BST_SHORT_ERR_RLS)) {
+ dev_err(dev, "FATAL: Boost shorted at startup\n");
+ return ret;
+ }
+
+ /* Clear GLOBAL_EN; safe because DSP is guaranteed to be off here */
+ ret = regmap_update_bits(regmap, CS40L26_GLOBAL_ENABLES,
+ CS40L26_GLOBAL_EN_MASK, 0);
+ if (ret) {
+ dev_err(dev, "Failed to clear GLOBAL_EN\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2,
+ CS40L26_BST_CTL_SEL_MASK, CS40L26_BST_CTL_SEL_CLASS_H);
+ if (ret) {
+ dev_err(dev, "Failed to set VBST_CTL_2\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_1,
+ CS40L26_BST_CTL_MASK, CS40L26_BST_CTL_VP);
+ if (ret)
+ dev_err(dev, "Failed to set VBST_CTL_1\n");
+
+ return ret;
+}
+
+static int cs40l26_handle_errata(struct cs40l26_private *cs40l26)
+{
+ int ret, num_writes;
+
+ if (!cs40l26->pdata.expl_mode_enabled) {
+ ret = cs40l26_lbst_short_test(cs40l26);
+ if (ret)
+ return ret;
+
+ num_writes = CS40L26_ERRATA_A1_NUM_WRITES;
+ } else {
+ num_writes = CS40L26_ERRATA_A1_EXPL_EN_NUM_WRITES;
+ }
+
+ ret = regmap_register_patch(cs40l26->regmap, cs40l26_a1_errata,
+ num_writes);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to patch A1 errata\n");
+ return ret;
+ }
+
+ return cs40l26_pseq_multi_write(cs40l26, cs40l26_a1_errata, num_writes,
+ false, CS40L26_PSEQ_OP_WRITE_FULL);
+}
+
static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
{
struct regmap *regmap = cs40l26->regmap;
struct device *dev = cs40l26->dev;
unsigned int val;
- u32 reg, nwaves;
+ u32 reg, nwaves, value;
int ret;
ret = cs40l26_verify_fw(cs40l26);
@@ -3651,12 +3720,17 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
+
+ ret = cs40l26_handle_errata(cs40l26);
+ if (ret)
+ return ret;
+
ret = cs40l26_dsp_start(cs40l26);
if (ret)
return ret;
- ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
- BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR));
+ ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
+ CS40L26_PM_TIMEOUT_MS_MAX);
if (ret)
return ret;
@@ -3664,7 +3738,8 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
BIT(CS40L26_IRQ1_AMP_ERR) | BIT(CS40L26_IRQ1_TEMP_ERR) |
BIT(CS40L26_IRQ1_BST_SHORT_ERR) |
BIT(CS40L26_IRQ1_BST_DCM_UVP_ERR) |
- BIT(CS40L26_IRQ1_BST_OVP_ERR));
+ BIT(CS40L26_IRQ1_BST_OVP_ERR) |
+ BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR));
if (ret)
return ret;
@@ -3694,6 +3769,11 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
+ ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
+ cs40l26->pdata.pm_stdby_timeout_ms);
+ if (ret)
+ return ret;
+
/* ensure firmware running */
ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE",
CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
@@ -3725,7 +3805,11 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
cs40l26_pm_runtime_setup(cs40l26);
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
@@ -3753,6 +3837,21 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
goto pm_err;
+ value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) |
+ (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT);
+
+ if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ goto pm_err;
+
+ ret = regmap_write(cs40l26->regmap, reg, value);
+ if (ret)
+ dev_err(dev, "Failed to configure compensation\n");
+ }
+
pm_err:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
@@ -3760,6 +3859,28 @@ pm_err:
return ret;
}
+static void cs40l26_gain_adjust(struct cs40l26_private *cs40l26, s32 adjust)
+{
+ u16 total, asp, change;
+
+ asp = cs40l26->pdata.asp_scale_pct;
+
+ if (adjust < 0) {
+ change = (u16) ((adjust * -1) & 0xFFFF);
+ if (asp < change)
+ total = 0;
+ else
+ total = asp - change;
+ } else {
+ change = (u16) (adjust & 0xFFFF);
+ total = asp + change;
+ if (total > CS40L26_GAIN_FULL_SCALE)
+ total = CS40L26_GAIN_FULL_SCALE;
+ }
+
+ cs40l26->pdata.asp_scale_pct = total;
+}
+
static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26)
{
unsigned int reg, le = 0;
@@ -3768,7 +3889,11 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26)
char n_str[2];
int ret, i, j;
- pm_runtime_get_sync(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev);
+ return ret;
+ }
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_DSP_MBOX_CMD_LE_EST, CS40L26_DSP_MBOX_RESET);
@@ -3788,6 +3913,8 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26)
goto pm_err;
}
+ dev_dbg(cs40l26->dev, "Measured LE estimate = 0x%08X\n", le);
+
for (j = 0; j < cs40l26->num_svc_le_vals; j++) {
if (le >= cs40l26->svc_le_vals[j]->min &&
le <= cs40l26->svc_le_vals[j]->max) {
@@ -3804,6 +3931,9 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26)
strncat(svc_bin_file, n_str, 2);
strncat(wt_bin_file, n_str, 2);
+ cs40l26_gain_adjust(cs40l26,
+ cs40l26->svc_le_vals[j]->gain_adjust);
+
break;
}
}
@@ -3839,11 +3969,6 @@ static void cs40l26_coeff_load(struct cs40l26_private *cs40l26)
int i, ret;
for (i = 0; i < cs40l26->fw.num_coeff_files; i++) {
- if (strncmp(cs40l26->fw.coeff_files[i], CS40L26_WT_FILE_NAME,
- CS40L26_WT_FILE_NAME_LEN)
- && cs40l26->fw.id == CS40L26_FW_CALIB_ID)
- continue;
-
ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev);
if (ret) {
dev_warn(dev, "Continuing...");
@@ -3949,7 +4074,12 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
}
if (cs40l26->fw_loaded) {
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev);
+ return ret;
+ }
+
ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
CS40L26_PM_TIMEOUT_MS_MAX);
if (ret) {
@@ -4008,7 +4138,9 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26)
int i, ret = 0, init_count, node_count = 0;
struct fwnode_handle *child;
unsigned int min, max, index;
+ const char *gain_adjust_str;
const char *node_name;
+ s32 gain_adjust;
init_count = device_get_child_node_count(dev);
if (!init_count)
@@ -4041,6 +4173,17 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26)
continue;
}
+ if (fwnode_property_read_string(child, "cirrus,gain-adjust",
+ &gain_adjust_str)) {
+ gain_adjust = 0;
+ } else {
+ ret = kstrtos32(gain_adjust_str, 10, &gain_adjust);
+ if (ret) {
+ dev_warn(dev, "Failed to get gain adjust\n");
+ gain_adjust = 0;
+ }
+ }
+
if (fwnode_property_read_u32(child, "cirrus,index", &index)) {
dev_err(dev, "No index specified for SVC LE node\n");
continue;
@@ -4067,6 +4210,7 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26)
cs40l26->svc_le_vals[node_count]->min = min;
cs40l26->svc_le_vals[node_count]->max = max;
+ cs40l26->svc_le_vals[node_count]->gain_adjust = gain_adjust;
cs40l26->svc_le_vals[node_count]->n = index;
node_count++;
}
@@ -4108,6 +4252,16 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
if (of_property_read_bool(np, "cirrus,fw-defer"))
cs40l26->fw_mode = CS40L26_FW_MODE_NONE;
+ if (of_property_read_bool(np, "cirrus,vibe-state"))
+ cs40l26->pdata.vibe_state_reporting = true;
+ else
+ cs40l26->pdata.vibe_state_reporting = false;
+
+ if (of_property_read_bool(np, "cirrus,bst-expl-mode-disable"))
+ cs40l26->pdata.expl_mode_enabled = false;
+ else
+ cs40l26->pdata.expl_mode_enabled = true;
+
cs40l26->pdata.vbbr_en =
of_property_read_bool(np, "cirrus,vbbr-enable");
@@ -4421,6 +4575,17 @@ int cs40l26_sys_suspend_noirq(struct device *dev)
}
EXPORT_SYMBOL(cs40l26_sys_suspend_noirq);
+void cs40l26_resume_error_handle(struct device *dev)
+{
+ dev_err(dev, "PM Runtime Resume failed\n");
+
+ pm_runtime_set_active(dev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+EXPORT_SYMBOL(cs40l26_resume_error_handle);
+
int cs40l26_resume(struct device *dev)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 7e60234..c08c3ab 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -180,6 +180,7 @@
#define CS40L26_DCM_FORCE 0x3820
#define CS40L26_VBST_OVP 0x3830
#define CS40L26_BST_DCR 0x3840
+#define CS40L26_TEST_LBST 0x391C
#define CS40L26_VPI_LIMIT_MODE 0x3C04
#define CS40L26_VPI_LIMITING 0x3C08
#define CS40L26_VPI_VP_THLDS 0x3C0C
@@ -653,12 +654,13 @@
#define CS40L26_MEM_RDY_MASK BIT(1)
#define CS40L26_MEM_RDY_SHIFT 1
-#define CS40L26_PLL_REFCLK_DET_EN_MASK BIT(0)
-
#define CS40L26_DSP_HALO_STATE_RUN 2
#define CS40L26_NUM_PCT_MAP_VALUES 101
+#define CS40L26_TEST_KEY_UNLOCK_CODE1 0x00000055
+#define CS40L26_TEST_KEY_UNLOCK_CODE2 0x000000AA
+
/* DSP State */
#define CS40L26_DSP_STATE_HIBERNATE 0
#define CS40L26_DSP_STATE_SHUTDOWN 1
@@ -807,6 +809,7 @@
#define CS40L26_DSP_MBOX_COMPLETE_MBOX 0x01000000
#define CS40L26_DSP_MBOX_COMPLETE_GPIO 0x01000001
#define CS40L26_DSP_MBOX_COMPLETE_I2S 0x01000002
+#define CS40L26_DSP_MBOX_TRIGGER_CP 0x01000010
#define CS40L26_DSP_MBOX_TRIGGER_GPIO 0x01000011
#define CS40L26_DSP_MBOX_PM_AWAKE 0x02000002
#define CS40L26_DSP_MBOX_F0_EST_START 0x07000011
@@ -823,6 +826,8 @@
#define CS40L26_FW_CALIB_NAME "cs40l26-calib.wmfw"
#define CS40L26_TUNING_FILES_MAX 4
+#define CS40L26_TUNING_FILES_RT 4
+#define CS40L26_TUNING_FILES_CAL 2
#define CS40L26_WT_FILE_NAME "cs40l26.bin"
#define CS40L26_WT_FILE_NAME_LEN 12
@@ -840,6 +845,8 @@
#define CS40L26_TUNING_FILE_SUFFIX_LEN 4
#define CS40L26_DVL_FILE_NAME "cs40l26-dvl.bin"
#define CS40L26_DVL_FILE_NAME_LEN 16
+#define CS40L26_CALIB_BIN_FILE_NAME "cs40l26-calib.bin"
+#define CS40L26_CALIB_BIN_FILE_NAME_LEN 18
#define CS40L26_SVC_LE_MAX_ATTEMPTS 2
#define CS40L26_SVC_DT_PREFIX "svc-le"
@@ -847,7 +854,7 @@
#define CS40L26_FW_ID 0x1800D4
#define CS40L26_FW_ROM_MIN_REV 0x040000
#define CS40L26_FW_A0_RAM_MIN_REV 0x050004
-#define CS40L26_FW_A1_RAM_MIN_REV 0x070212
+#define CS40L26_FW_A1_RAM_MIN_REV 0x070218
#define CS40L26_FW_CALIB_ID 0x1800DA
#define CS40L26_FW_CALIB_MIN_REV 0x010000
#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21)
@@ -958,8 +965,15 @@
#define CS40L26_BST_VOLT_MAX 11000000
#define CS40L26_BST_CTL_DEFAULT 11000000
#define CS40L26_BST_VOLT_STEP 50000
+#define CS40L26_BST_CTL_VP 0x00
#define CS40L26_BST_CTL_MASK GENMASK(7, 0)
#define CS40L26_BST_CTL_SHIFT 0
+#define CS40L26_BST_CTL_SEL_MASK GENMASK(1, 0)
+#define CS40L26_BST_CTL_SEL_FIXED 0x0
+#define CS40L26_BST_CTL_SEL_CLASS_H 0x1
+
+#define CS40L26_BST_TIME_MIN_US 10000
+#define CS40L26_BST_TIME_MAX_US 10100
#define CS40L26_BST_CTL_LIM_EN_MASK BIT(2)
#define CS40L26_BST_CTL_LIM_EN_SHIFT 2
@@ -1192,11 +1206,35 @@
#define CS40L26_LOGGER_SRC_SIZE_MASK BIT(22)
#define CS40L26_LOGGER_SRC_SIZE_SHIFT 22
-#define CS40L26_LOGGER_SRC_ID_MASK GENMASK(20, 16)
+#define CS40L26_LOGGER_SRC_ID_MASK GENMASK(19, 16)
#define CS40L26_LOGGER_SRC_ID_SHIFT 16
+#define CS40L26_LOGGER_SRC_COUNT_CALIB 3
+#define CS40L26_LOGGER_SRC_COUNT 1
+#define CS40L26_LOGGER_SRC_ID_BEMF 1
+#define CS40L26_LOGGER_SRC_ID_VBST 2
+#define CS40L26_LOGGER_SRC_ID_VMON 3
+#define CS40L26_LOGGER_DATA_1_MIN_OFFSET 0
+#define CS40L26_LOGGER_DATA_1_MAX_OFFSET 4
+#define CS40L26_LOGGER_DATA_1_MEAN_OFFSET 8
+#define CS40L26_LOGGER_DATA_2_MIN_OFFSET 12
+#define CS40L26_LOGGER_DATA_2_MAX_OFFSET 16
+#define CS40L26_LOGGER_DATA_2_MEAN_OFFSET 20
+#define CS40L26_LOGGER_DATA_3_MIN_OFFSET 24
+#define CS40L26_LOGGER_DATA_3_MAX_OFFSET 28
+#define CS40L26_LOGGER_DATA_3_MEAN_OFFSET 32
#define CS40L26_UINT_24_BITS_MAX 16777215
+/* Compensation */
+#define CS40L26_COMP_EN_REDC_SHIFT 1
+#define CS40L26_COMP_EN_F0_SHIFT 0
+
+/* Errata */
+#define CS40L26_ERRATA_A1_NUM_WRITES 4
+#define CS40L26_ERRATA_A1_EXPL_EN_NUM_WRITES 1
+#define CS40L26_PLL_REFCLK_DET_EN 0x00000001
+#define CS40L26_DISABLE_EXPL_MODE 0x014DC080
+
/* MFD */
#define CS40L26_NUM_MFD_DEVS 1
@@ -1347,6 +1385,7 @@ struct cs40l26_pseq_op {
};
struct cs40l26_svc_le {
+ s32 gain_adjust;
u32 min;
u32 max;
u32 n;
@@ -1377,6 +1416,8 @@ struct cs40l26_platform_data {
u32 redc_default;
u32 q_default;
u32 boost_ctl;
+ bool vibe_state_reporting;
+ bool expl_mode_enabled;
};
struct cs40l26_owt {
@@ -1435,6 +1476,9 @@ struct cs40l26_private {
int upload_ret;
int erase_ret;
int effects_in_flight;
+ bool comp_enable_pend;
+ bool comp_enable_redc;
+ bool comp_enable_f0;
};
struct cs40l26_codec {
@@ -1477,6 +1521,7 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
enum cs40l26_pm_state state);
int cs40l26_ack_write(struct cs40l26_private *cs40l26, u32 reg, u32 write_val,
u32 reset_val);
+void cs40l26_resume_error_handle(struct device *dev);
int cs40l26_resume(struct device *dev);
int cs40l26_sys_resume(struct device *dev);
int cs40l26_sys_resume_noirq(struct device *dev);
@@ -1500,6 +1545,8 @@ extern const struct regmap_config cs40l26_regmap;
extern const struct mfd_cell cs40l26_devs[CS40L26_NUM_MFD_DEVS];
extern const u8 cs40l26_pseq_op_sizes[CS40L26_PSEQ_NUM_OPS][2];
extern const u32 cs40l26_attn_q21_2_vals[CS40L26_NUM_PCT_MAP_VALUES];
+extern const struct reg_sequence
+ cs40l26_a1_errata[CS40L26_ERRATA_A1_NUM_WRITES];
/* sysfs */