summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2022-05-23 12:45:21 +0800
committerTai Kuo <taikuo@google.com>2022-05-31 10:10:18 +0000
commit571c10e7eeeaa0ba602382ba02d5352c7d9ce6ea (patch)
treee50fc20189234fe97db428aac391f814991f2ab1
parent3792416c75504ce61a1ee0b9a66f6dcd4c9ebdc1 (diff)
downloadamplifiers-571c10e7eeeaa0ba602382ba02d5352c7d9ce6ea.tar.gz
cs40l26: merge cs40l26 v5.2.1
Branch: v5.10-cs40l26 Tag: cs40l26-v5.2.1_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 (No changes) drivers/input/misc/cs40l26.c include/linux/mfd/cs40l26.h sound/soc/codecs/cs40l26.c -> cs40l26-codec.c Features: - Add vpbr_thld sysfs control - Add svc_le_est sysfs control in calibration directory Bug fixes: - Fix issue where invert and SVC were not applied to streaming data on first playback - Fix vibe_state issue when stopping an SVC effect immediately after triggering - Work around PSEQ_WRITE_REG full sign extention bug - Fix bug in SVC and invert kcontrols where driver attempts to access control before ensuring device is awake Major API changes: - None Commits: 217a3c0 ASoC: cs40l26: Use pm_runtime for kcontrols b6e471f ASoC: cs40l26: Report return value upon PM Runtime Resume Failure 112d30b input: cs40l26: Report return value upon PM Runtime Resume Failure 0b5f633 ASoC: cs40l26: PM Resume error handle for CODEC 40792b3 input: cs40l26: Improvements to SVC Le Estimation fc06825 input: cs40l26: Support for maintenance firmware branch 3952b3b input: cs40l26: Address PSEQ WRITE_FULL issue aaaf562 input: cs40l26: Add sysfs control for vpbr_thld caa3f56 input: cs40l26: Move stop delay outside mutex (Skip) 63b3998 Documentation: cs40l26: Fix timeout maximum values 2075e2f ASoC: cs40l26: Move PMU-time register writes to mixer controls (Skip) e376a83 Documentation: cs40l26: Fix "cirrus,redc-default" calculation 0bb9162 ASoC: cs40l26: Use set_pll_loop() during RECLK config 4b341ed input: cs40l26: Avoid DSP lock during driver probe Bug: 231410838 Test: Copy texts and adjust alarm Test: NFC, dumpstate, keyboard vibration Test: idlcli commands Test: Switch firmware continuous Test: Switch firmware and check the first tick effect Signed-off-by: Tai Kuo <taikuo@google.com> Change-Id: Ide206f8ea173e79267fa4506dece31f6e26a9356
-rw-r--r--cs40l26/cs40l26-codec.c170
-rw-r--r--cs40l26/cs40l26-sysfs.c195
-rw-r--r--cs40l26/cs40l26.c332
-rw-r--r--cs40l26/cs40l26.h34
4 files changed, 523 insertions, 208 deletions
diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c
index 4cf74bd..d126149 100644
--- a/cs40l26/cs40l26-codec.c
+++ b/cs40l26/cs40l26-codec.c
@@ -60,13 +60,10 @@ static int cs40l26_swap_ext_clk(struct cs40l26_codec *codec, u8 clk_src)
return ret;
}
- ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT,
- CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 1 <<
- CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT);
- if (ret) {
- dev_err(dev, "Failed to set Open-Loop PLL\n");
+ ret = cs40l26_set_pll_loop(codec->core,
+ CS40L26_PLL_REFCLK_SET_OPEN_LOOP);
+ if (ret)
return ret;
- }
ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT,
CS40L26_PLL_REFCLK_FREQ_MASK |
@@ -77,11 +74,8 @@ static int cs40l26_swap_ext_clk(struct cs40l26_codec *codec, u8 clk_src)
return ret;
}
- ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT,
- CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 0 <<
- CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT);
- if (ret)
- dev_err(dev, "Failed to close PLL loop\n");
+ ret = cs40l26_set_pll_loop(codec->core,
+ CS40L26_PLL_REFCLK_SET_CLOSED_LOOP);
return ret;
}
@@ -203,7 +197,7 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
struct device *dev = cs40l26->dev;
u32 asp_en_mask = CS40L26_ASP_TX1_EN_MASK | CS40L26_ASP_TX2_EN_MASK |
CS40L26_ASP_RX1_EN_MASK | CS40L26_ASP_RX2_EN_MASK;
- u32 asp_enables, reg;
+ u32 asp_enables;
u8 data_src;
int ret;
@@ -242,30 +236,6 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
goto err_mutex;
}
- ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS",
- CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
- if (ret)
- goto err_mutex;
-
- ret = regmap_update_bits(regmap, reg,
- CS40L26_SVC_FOR_STREAMING_MASK,
- codec->svc_for_streaming_data);
- if (ret) {
- dev_err(dev, "Failed to specify SVC for streaming\n");
- goto err_mutex;
- }
-
- ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
- CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
- if (ret)
- goto err_mutex;
-
- ret = regmap_write(regmap, reg, codec->invert_streaming_data);
- if (ret) {
- dev_err(dev, "Failed to specify SVC for streaming\n");
- goto err_mutex;
- }
-
break;
case SND_SOC_DAPM_PRE_PMD:
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
@@ -308,7 +278,7 @@ static int cs40l26_i2s_vmon_get(struct snd_kcontrol *kcontrol,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -379,17 +349,37 @@ static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol,
struct cs40l26_codec *codec =
snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
struct cs40l26_private *cs40l26 = codec->core;
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *dev = cs40l26->dev;
+ unsigned int val = 0, reg;
+ int ret = 0;
- mutex_lock(&cs40l26->lock);
+ ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+ if (ret)
+ return ret;
- if (codec->svc_for_streaming_data)
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev, ret);
+ return ret;
+ }
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to read FLAGS\n");
+ return ret;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (val & CS40L26_SVC_FOR_STREAMING_MASK)
ucontrol->value.enumerated.item[0] = 1;
else
ucontrol->value.enumerated.item[0] = 0;
- mutex_unlock(&cs40l26->lock);
-
- return 0;
+ return ret;
}
static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -397,17 +387,32 @@ static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol,
struct cs40l26_codec *codec =
snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
struct cs40l26_private *cs40l26 = codec->core;
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *dev = cs40l26->dev;
+ int ret = 0;
+ unsigned int reg;
- mutex_lock(&cs40l26->lock);
+ ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+ if (ret)
+ return ret;
- if (ucontrol->value.enumerated.item[0])
- codec->svc_for_streaming_data = true;
- else
- codec->svc_for_streaming_data = false;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev, ret);
+ return ret;
+ }
- mutex_unlock(&cs40l26->lock);
+ ret = regmap_update_bits(regmap, reg,
+ CS40L26_SVC_FOR_STREAMING_MASK,
+ ucontrol->value.enumerated.item[0]);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to specify SVC for streaming\n");
- return 0;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
}
static int cs40l26_invert_streaming_data_get(struct snd_kcontrol *kcontrol,
@@ -416,17 +421,37 @@ static int cs40l26_invert_streaming_data_get(struct snd_kcontrol *kcontrol,
struct cs40l26_codec *codec =
snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
struct cs40l26_private *cs40l26 = codec->core;
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *dev = cs40l26->dev;
+ unsigned int val = 0, reg;
+ int ret = 0;
- mutex_lock(&cs40l26->lock);
+ ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev, ret);
+ return ret;
+ }
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to read SOURCE_INVERT\n");
+ return ret;
+ }
- if (codec->invert_streaming_data)
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (val)
ucontrol->value.enumerated.item[0] = 1;
else
ucontrol->value.enumerated.item[0] = 0;
- mutex_unlock(&cs40l26->lock);
-
- return 0;
+ return ret;
}
static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol,
@@ -435,17 +460,30 @@ static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol,
struct cs40l26_codec *codec =
snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
struct cs40l26_private *cs40l26 = codec->core;
+ struct regmap *regmap = cs40l26->regmap;
+ struct device *dev = cs40l26->dev;
+ int ret = 0;
+ unsigned int reg;
- mutex_lock(&cs40l26->lock);
+ ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+ if (ret)
+ return ret;
- if (ucontrol->value.enumerated.item[0])
- codec->invert_streaming_data = true;
- else
- codec->invert_streaming_data = false;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev, ret);
+ return ret;
+ }
- mutex_unlock(&cs40l26->lock);
+ ret = regmap_write(regmap, reg, ucontrol->value.enumerated.item[0]);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to specify invert streaming data\n");
- return 0;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
}
static int cs40l26_tuning_get(struct snd_kcontrol *kcontrol,
@@ -504,7 +542,7 @@ static int cs40l26_a2h_volume_get(struct snd_kcontrol *kcontrol,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -545,7 +583,7 @@ static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -602,7 +640,7 @@ static int cs40l26_a2h_delay_get(struct snd_kcontrol *kcontrol,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -646,7 +684,7 @@ static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -800,7 +838,7 @@ static int cs40l26_pcm_hw_params(struct snd_pcm_substream *substream,
ret = pm_runtime_get_sync(codec->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(codec->dev);
+ cs40l26_resume_error_handle(codec->dev, ret);
return ret;
}
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 2cc99b6..ce4f711 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -22,7 +22,7 @@ static ssize_t dsp_state_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -53,7 +53,7 @@ static ssize_t halo_heartbeat_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -78,7 +78,7 @@ static ssize_t pm_stdby_timeout_ms_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -106,7 +106,7 @@ static ssize_t pm_stdby_timeout_ms_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -131,7 +131,7 @@ static ssize_t pm_active_timeout_ms_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -159,7 +159,7 @@ static ssize_t pm_active_timeout_ms_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -261,7 +261,7 @@ static ssize_t owt_free_space_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -297,7 +297,7 @@ static ssize_t die_temp_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -342,7 +342,7 @@ static ssize_t num_waves_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -370,7 +370,7 @@ static ssize_t boost_disable_delay_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -410,7 +410,7 @@ static ssize_t boost_disable_delay_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -441,7 +441,7 @@ static ssize_t f0_offset_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -484,7 +484,7 @@ static ssize_t f0_offset_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -589,7 +589,7 @@ static ssize_t f0_comp_enable_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -669,7 +669,7 @@ static ssize_t redc_comp_enable_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
mutex_lock(&cs40l26->lock);
@@ -750,6 +750,98 @@ static ssize_t swap_firmware_store(struct device *dev,
}
static DEVICE_ATTR_RW(swap_firmware);
+static ssize_t vpbr_thld_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;
+ }
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->vpbr_thld);
+
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+ return ret;
+}
+
+static ssize_t vpbr_thld_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct regmap *regmap = cs40l26->regmap;
+ int ret;
+ u8 sysfs_val;
+ u32 vpbr_config_reg_val;
+
+ ret = kstrtou8(buf, 10, &sysfs_val);
+ if (ret ||
+ sysfs_val > CS40L26_VPBR_THLD_MAX ||
+ sysfs_val < CS40L26_VPBR_THLD_MIN)
+ return -EINVAL;
+
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
+ return ret;
+ }
+ mutex_lock(&cs40l26->lock);
+
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) {
+ ret = -EPERM;
+ goto err_mutex;
+ }
+
+ cs40l26->vpbr_thld = sysfs_val;
+
+ ret = regmap_read(regmap, CS40L26_VPBR_CONFIG, &vpbr_config_reg_val);
+ if (ret) {
+ dev_err(dev, "Failed to read VPBR_CONFIG reg\n");
+ goto err_mutex;
+ }
+
+ vpbr_config_reg_val &= ~CS40L26_VPBR_THLD_MASK;
+ vpbr_config_reg_val |= (cs40l26->vpbr_thld & CS40L26_VPBR_THLD_MASK);
+
+ ret = regmap_write(regmap, CS40L26_VPBR_CONFIG, vpbr_config_reg_val);
+ if (ret) {
+ dev_err(dev, "Failed to write VPBR config.\n");
+ goto err_mutex;
+ }
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG,
+ (vpbr_config_reg_val & GENMASK(31, 16)) >> 16,
+ true, CS40L26_PSEQ_OP_WRITE_H16);
+ if (ret) {
+ dev_err(dev, "Failed to update VPBR config PSEQ H16\n");
+ goto err_mutex;
+ }
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG,
+ (vpbr_config_reg_val & GENMASK(15, 0)),
+ true, CS40L26_PSEQ_OP_WRITE_L16);
+ if (ret) {
+ dev_err(dev, "Failed to update VPBR config PSEQ L16\n");
+ goto err_mutex;
+ }
+
+ ret = count;
+
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ return ret;
+}
+static DEVICE_ATTR_RW(vpbr_thld);
+
static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_num_waves.attr,
&dev_attr_die_temp.attr,
@@ -766,6 +858,7 @@ static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_f0_comp_enable.attr,
&dev_attr_redc_comp_enable.attr,
&dev_attr_swap_firmware.attr,
+ &dev_attr_vpbr_thld.attr,
NULL,
};
@@ -783,7 +876,7 @@ static ssize_t dbc_enable_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -831,7 +924,7 @@ static ssize_t dbc_enable_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -874,7 +967,7 @@ static ssize_t dbc_env_rel_coef_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -917,7 +1010,7 @@ static ssize_t dbc_rise_headroom_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -960,7 +1053,7 @@ static ssize_t dbc_fall_headroom_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -1003,7 +1096,7 @@ static ssize_t dbc_tx_lvl_thresh_fs_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -1046,7 +1139,7 @@ static ssize_t dbc_tx_lvl_hold_off_ms_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -1102,7 +1195,7 @@ static ssize_t trigger_calibration_store(struct device *dev,
/* pm_runtime_put occurs is irq_handler after diagnostic is finished */
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1134,7 +1227,7 @@ static ssize_t f0_measured_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1171,7 +1264,7 @@ static ssize_t q_measured_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1208,7 +1301,7 @@ static ssize_t redc_measured_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1245,7 +1338,7 @@ static ssize_t redc_est_show(struct device *dev, struct device_attribute *attr,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1287,7 +1380,7 @@ static ssize_t redc_est_store(struct device *dev, struct device_attribute *attr,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1324,7 +1417,7 @@ static ssize_t f0_stored_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1369,7 +1462,7 @@ static ssize_t f0_stored_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1406,7 +1499,7 @@ static ssize_t q_stored_show(struct device *dev, struct device_attribute *attr,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1449,7 +1542,7 @@ static ssize_t q_stored_store(struct device *dev, struct device_attribute *attr,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1486,7 +1579,7 @@ static ssize_t redc_stored_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1528,7 +1621,7 @@ static ssize_t redc_stored_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1565,7 +1658,7 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1646,7 +1739,7 @@ static ssize_t redc_cal_time_ms_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1685,7 +1778,7 @@ static ssize_t logging_en_show(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1729,7 +1822,7 @@ static ssize_t logging_en_store(struct device *dev,
ret = pm_runtime_get_sync(cdev);
if (ret < 0) {
- cs40l26_resume_error_handle(cdev);
+ cs40l26_resume_error_handle(cdev, ret);
return ret;
}
@@ -1842,7 +1935,7 @@ static ssize_t logging_max_reset_store(struct device *dev,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1970,7 +2063,31 @@ err_mutex:
}
static DEVICE_ATTR_RO(max_vmon);
+static ssize_t svc_le_est_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ unsigned int le;
+ int ret;
+
+ pm_runtime_get_sync(cs40l26->dev);
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_svc_le_estimate(cs40l26, &le);
+
+ 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, "%u\n", le);
+}
+static DEVICE_ATTR_RO(svc_le_est);
+
static struct attribute *cs40l26_dev_attrs_cal[] = {
+ &dev_attr_svc_le_est.attr,
&dev_attr_max_vbst.attr,
&dev_attr_max_bemf.attr,
&dev_attr_max_vmon.attr,
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 3be1832..93fe669 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -176,6 +176,35 @@ int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state)
}
EXPORT_SYMBOL(cs40l26_dsp_state_get);
+int cs40l26_set_pll_loop(struct cs40l26_private *cs40l26, unsigned int pll_loop)
+{
+ int ret, i;
+
+ if (pll_loop != CS40L26_PLL_REFCLK_SET_OPEN_LOOP &&
+ pll_loop != CS40L26_PLL_REFCLK_SET_CLOSED_LOOP) {
+ dev_err(cs40l26->dev, "Invalid PLL Loop setting: %u\n",
+ pll_loop);
+ return -EINVAL;
+ }
+
+ /* Retry in case DSP is hibernating */
+ for (i = 0; i < CS40L26_PLL_REFCLK_SET_ATTEMPTS; i++) {
+ ret = regmap_update_bits(cs40l26->regmap, CS40L26_REFCLK_INPUT,
+ CS40L26_PLL_REFCLK_LOOP_MASK, pll_loop <<
+ CS40L26_PLL_REFCLK_LOOP_SHIFT);
+ if (!ret)
+ break;
+ }
+
+ if (i == CS40L26_PLL_REFCLK_SET_ATTEMPTS) {
+ dev_err(cs40l26->dev, "Failed to configure PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_set_pll_loop);
+
int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
unsigned int *val)
{
@@ -185,7 +214,7 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -566,7 +595,7 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26)
return -EINVAL;
}
- ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms);
+ ret = cs40l26_pm_active_timeout_ms_get(cs40l26, &timeout_ms);
if (ret)
return ret;
@@ -1199,8 +1228,9 @@ static irqreturn_t cs40l26_irq(int irq, void *data)
unsigned long num_irq;
int ret;
- if (pm_runtime_get_sync(dev) < 0) {
- cs40l26_resume_error_handle(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(dev, ret);
dev_err(dev, "Interrupts missed\n");
@@ -1318,7 +1348,7 @@ static int cs40l26_pseq_find_end(struct cs40l26_private *cs40l26,
return 0;
}
-static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
+int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
u32 data, bool update, u8 op_code)
{
struct device *dev = cs40l26->dev;
@@ -1331,6 +1361,22 @@ static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
u32 *op_words;
int ret;
+
+ /*
+ * Due to a bug in the DSP ROM, if data[23] = 1 for a WRITE_FULL
+ * operation, then, when the DSP power-on write sequencer
+ * actually applies these writes when coming out of hibernate,
+ * the DSP sign-extends bit 23 to bits[31:24]. So, warn if it
+ * appears the PSEQ will not function as expected.
+ */
+ if ((op_code == CS40L26_PSEQ_OP_WRITE_FULL) &&
+ (data & BIT(23)) &&
+ (((data & GENMASK(31, 24)) >> 24) != 0xFF)) {
+ dev_warn(dev,
+ "PSEQ to set data[31:24] to 0xFF reg: %08X, data: %08X",
+ addr, data);
+ }
+
if (op_code == CS40L26_PSEQ_OP_WRITE_FULL) {
num_op_words = CS40L26_PSEQ_OP_WRITE_FULL_WORDS;
l_addr_shift = CS40L26_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT;
@@ -1444,6 +1490,7 @@ op_words_free:
return ret;
}
+EXPORT_SYMBOL(cs40l26_pseq_write);
static int cs40l26_pseq_multi_write(struct cs40l26_private *cs40l26,
const struct reg_sequence *reg_seq, int num_regs, bool update,
@@ -1619,8 +1666,27 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg,
return ret;
}
- return cs40l26_pseq_write(cs40l26, reg,
- new_mask, true, CS40L26_PSEQ_OP_WRITE_FULL);
+ if (bit_mask & GENMASK(31, 16)) {
+ ret = cs40l26_pseq_write(cs40l26, reg,
+ (new_mask & GENMASK(31, 16)) >> 16,
+ true, CS40L26_PSEQ_OP_WRITE_H16);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to update IRQ mask H16");
+ return ret;
+ }
+ }
+
+ if (bit_mask & GENMASK(15, 0)) {
+ ret = cs40l26_pseq_write(cs40l26, reg,
+ (new_mask & GENMASK(15, 0)),
+ true, CS40L26_PSEQ_OP_WRITE_L16);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to update IRQ mask L16");
+ return ret;
+ }
+ }
+
+ return ret;
}
static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
@@ -1709,7 +1775,7 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
ret = pm_runtime_get_sync(cs40l26->dev);
if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
return ret;
}
@@ -1754,8 +1820,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work)
u32 reg;
int ret;
- if (pm_runtime_get_sync(cs40l26->dev) < 0)
- return cs40l26_resume_error_handle(cs40l26->dev);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev, ret);
mutex_lock(&cs40l26->lock);
@@ -1802,8 +1869,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
dev_dbg(dev, "%s\n", __func__);
- if (pm_runtime_get_sync(dev) < 0)
- return cs40l26_resume_error_handle(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return cs40l26_resume_error_handle(dev, ret);
mutex_lock(&cs40l26->lock);
@@ -1889,20 +1957,20 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work)
dev_dbg(cs40l26->dev, "%s\n", __func__);
- 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 &&
- !cs40l26->vibe_state_reporting)
- goto mutex_exit;
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev, ret);
/* wait for SVC init phase to complete */
if (cs40l26->delay_before_stop_playback_us)
usleep_range(cs40l26->delay_before_stop_playback_us,
cs40l26->delay_before_stop_playback_us + 100);
+ mutex_lock(&cs40l26->lock);
+
+ if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC)
+ goto mutex_exit;
+
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET);
if (ret) {
@@ -2179,7 +2247,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data,
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -2602,8 +2670,9 @@ static void cs40l26_upload_worker(struct work_struct *work)
u16 index, bank;
bool pwle, svc_waveform;
- if (pm_runtime_get_sync(cdev) < 0)
- return cs40l26_resume_error_handle(cdev);
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0)
+ return cs40l26_resume_error_handle(cdev, ret);
mutex_lock(&cs40l26->lock);
@@ -2890,8 +2959,9 @@ 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);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0)
+ return cs40l26_resume_error_handle(cs40l26->dev, ret);
mutex_lock(&cs40l26->lock);
@@ -3171,15 +3241,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- if (cs40l26->pdata.vbbr_thld) {
- if (cs40l26->pdata.vbbr_thld
+ if (cs40l26->pdata.vbbr_thld_mv) {
+ if (cs40l26->pdata.vbbr_thld_mv
>= CS40L26_VBBR_THLD_MV_MAX)
vbbr_thld = CS40L26_VBBR_THLD_MAX;
- else if (cs40l26->pdata.vbbr_thld
+ else if (cs40l26->pdata.vbbr_thld_mv
<= CS40L26_VBBR_THLD_MV_MIN)
vbbr_thld = CS40L26_VBBR_THLD_MIN;
else
- vbbr_thld = cs40l26->pdata.vbbr_thld /
+ vbbr_thld = cs40l26->pdata.vbbr_thld_mv /
CS40L26_VBBR_THLD_MV_STEP;
val &= ~CS40L26_VBBR_THLD_MASK;
@@ -3255,8 +3325,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, val,
- true, CS40L26_PSEQ_OP_WRITE_FULL);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG,
+ (val & GENMASK(31, 16)) >> 16,
+ true, CS40L26_PSEQ_OP_WRITE_H16);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG,
+ (val & GENMASK(15, 0)),
+ true, CS40L26_PSEQ_OP_WRITE_L16);
if (ret)
return ret;
}
@@ -3271,17 +3348,20 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
pseq_mask |= BIT(CS40L26_IRQ2_VPBR_ATT_CLR) |
BIT(CS40L26_IRQ2_VPBR_FLAG);
- if (cs40l26->pdata.vpbr_thld) {
- if (cs40l26->pdata.vpbr_thld
- >= CS40L26_VPBR_THLD_MV_MAX)
+ if (cs40l26->pdata.vpbr_thld_mv) {
+ if (cs40l26->pdata.vpbr_thld_mv
+ >= CS40L26_VPBR_THLD_MV_MAX) {
vpbr_thld = CS40L26_VPBR_THLD_MAX;
- else if (cs40l26->pdata.vpbr_thld
- <= CS40L26_VPBR_THLD_MV_MIN)
+ } else if (cs40l26->pdata.vpbr_thld_mv
+ <= CS40L26_VPBR_THLD_MV_MIN) {
vpbr_thld = CS40L26_VPBR_THLD_MIN;
- else
- vpbr_thld = (cs40l26->pdata.vpbr_thld /
+ } else {
+ vpbr_thld = (cs40l26->pdata.vpbr_thld_mv /
CS40L26_VPBR_THLD_MV_DIV)
- CS40L26_VPBR_THLD_OFFSET;
+ }
+
+ cs40l26->vpbr_thld = vpbr_thld & CS40L26_VPBR_THLD_MASK;
val &= ~CS40L26_VPBR_THLD_MASK;
val |= (vpbr_thld & CS40L26_VPBR_THLD_MASK);
@@ -3358,8 +3438,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, val,
- true, CS40L26_PSEQ_OP_WRITE_FULL);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG,
+ (val & GENMASK(31, 16)) >> 16,
+ true, CS40L26_PSEQ_OP_WRITE_H16);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG,
+ (val & GENMASK(15, 0)),
+ true, CS40L26_PSEQ_OP_WRITE_L16);
if (ret)
return ret;
}
@@ -3843,16 +3930,16 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
+ cs40l26_pm_runtime_setup(cs40l26);
+
ret = cs40l26_pm_state_transition(cs40l26,
CS40L26_PM_STATE_ALLOW_HIBERNATE);
if (ret)
return ret;
- cs40l26_pm_runtime_setup(cs40l26);
-
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- cs40l26_resume_error_handle(dev);
+ cs40l26_resume_error_handle(dev, ret);
return ret;
}
@@ -3926,54 +4013,73 @@ static void cs40l26_gain_adjust(struct cs40l26_private *cs40l26, s32 adjust)
cs40l26->pdata.asp_scale_pct = total;
}
-static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26,
- u32 *tuning_num)
+int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le)
{
- unsigned int reg, le = 0;
- int ret, i, j;
-
- ret = pm_runtime_get_sync(cs40l26->dev);
- if (ret < 0) {
- cs40l26_resume_error_handle(cs40l26->dev);
- return ret;
- }
+ struct device *dev = cs40l26->dev;
+ unsigned int reg, le_est = 0;
+ int ret, i;
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_DSP_MBOX_CMD_LE_EST, CS40L26_DSP_MBOX_RESET);
if (ret)
- goto pm_err;
+ return ret;
ret = cl_dsp_get_reg(cs40l26->dsp, "LE_EST_STATUS",
CL_DSP_YM_UNPACKED_TYPE, CS40l26_SVC_ALGO_ID, &reg);
if (ret)
- goto pm_err;
+ return ret;
for (i = 0; i < CS40L26_SVC_LE_MAX_ATTEMPTS; i++) {
- usleep_range(5000, 5100);
- ret = regmap_read(cs40l26->regmap, reg, &le);
+ usleep_range(CS40L26_SVC_LE_EST_TIME_US,
+ CS40L26_SVC_LE_EST_TIME_US + 100);
+ ret = regmap_read(cs40l26->regmap, reg, &le_est);
if (ret) {
- dev_err(cs40l26->dev, "Failed to get LE_EST_STATUS\n");
- goto pm_err;
+ dev_err(dev, "Failed to get LE_EST_STATUS\n");
+ return ret;
}
- dev_dbg(cs40l26->dev, "Measured LE estimate = 0x%08X\n", le);
+ dev_info(dev, "Measured Le Estimation = %u\n", le_est);
- 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) {
- *tuning_num = cs40l26->svc_le_vals[j]->n;
+ if (le_est)
+ break;
+ }
- cs40l26_gain_adjust(cs40l26,
- cs40l26->svc_le_vals[j]->gain_adjust);
+ *le = le_est;
+
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_svc_le_estimate);
+
+static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26,
+ u32 *tuning_num)
+{
+ unsigned int le;
+ int ret, i;
+
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
+ return ret;
+ }
+ ret = cs40l26_svc_le_estimate(cs40l26, &le);
+ if (ret)
+ goto pm_err;
+
+ if (le) {
+ for (i = 0; i < cs40l26->num_svc_le_vals; i++) {
+ if (le >= cs40l26->svc_le_vals[i]->min &&
+ le <= cs40l26->svc_le_vals[i]->max) {
+ *tuning_num = cs40l26->svc_le_vals[i]->n;
+
+ cs40l26_gain_adjust(cs40l26,
+ cs40l26->svc_le_vals[i]->gain_adjust);
break;
}
}
- if (j < cs40l26->num_svc_le_vals)
- break;
}
- if (i == CS40L26_SVC_LE_MAX_ATTEMPTS)
+ if (!le || i == cs40l26->num_svc_le_vals)
dev_warn(cs40l26->dev, "Using default tunings\n");
pm_err:
@@ -4097,8 +4203,17 @@ static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26)
static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26)
{
+ u32 id, min_rev, rev, branch;
int ret, maj, min, patch;
- u32 id, min_rev, rev;
+
+ ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev);
+ if (ret)
+ return ret;
+
+ branch = CL_DSP_GET_MAJOR(rev);
+ maj = (int) branch;
+ min = (int) CL_DSP_GET_MINOR(rev);
+ patch = (int) CL_DSP_GET_PATCH(rev);
ret = cl_dsp_fw_id_get(cs40l26->dsp, &id);
if (ret)
@@ -4106,35 +4221,43 @@ static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26)
switch (id) {
case CS40L26_FW_ID:
- min_rev = CS40L26_FW_A1_RAM_MIN_REV;
+ if (branch == CS40L26_FW_BRANCH) {
+ min_rev = CS40L26_FW_MIN_REV;
+ cs40l26->vibe_state_reporting = true;
+ } else if (branch == CS40L26_FW_MAINT_BRANCH) {
+ min_rev = CS40L26_FW_MAINT_MIN_REV;
+ cs40l26->vibe_state_reporting = false;
+ } else {
+ ret = -EINVAL;
+ }
break;
case CS40L26_FW_CALIB_ID:
- min_rev = CS40L26_FW_CALIB_MIN_REV;
+ if (branch == CS40L26_FW_CALIB_BRANCH) {
+ min_rev = CS40L26_FW_CALIB_MIN_REV;
+ cs40l26->vibe_state_reporting = true;
+ } else if (branch == CS40L26_FW_MAINT_CALIB_BRANCH) {
+ min_rev = CS40L26_FW_MAINT_CALIB_MIN_REV;
+ cs40l26->vibe_state_reporting = false;
+ } else {
+ ret = -EINVAL;
+ }
break;
default:
dev_err(cs40l26->dev, "Invalid FW ID: 0x%06X\n", id);
+ return -EINVAL;
}
- ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev);
- if (ret)
+ if (ret) {
+ dev_err(cs40l26->dev, "Rev. Branch 0x%02X invalid\n", maj);
return ret;
+ }
- maj = (int) CL_DSP_GET_MAJOR(rev);
- min = (int) CL_DSP_GET_MINOR(rev);
- patch = (int) CL_DSP_GET_PATCH(rev);
-
- if ((rev & ~CS40L26_FW_BRANCH_MASK) < min_rev) {
+ if (rev < min_rev) {
dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n",
maj, min, patch);
return -EINVAL;
}
- if ((rev & CS40L26_FW_BRANCH_MASK) ==
- (CS40L26_FW_A1_RAM_MIN_REV & CS40L26_FW_BRANCH_MASK))
- cs40l26->vibe_state_reporting = true;
- else
- cs40l26->vibe_state_reporting = false;
-
cs40l26->fw_id = id;
dev_info(cs40l26->dev, "Firmware revision %d.%d.%d\n", maj, min, patch);
@@ -4172,14 +4295,13 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26)
const struct firmware *fw;
int ret;
-start:
cs40l26->fw_loaded = false;
ret = cs40l26_cl_dsp_reinit(cs40l26);
if (ret)
return ret;
- if (svc_le_required || cs40l26->calib_fw)
+ if (cs40l26->calib_fw)
ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev);
else
ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev);
@@ -4207,8 +4329,6 @@ start:
return ret;
if (svc_le_required) {
- svc_le_required = false;
-
ret = cs40l26_dsp_config(cs40l26);
if (ret)
return ret;
@@ -4218,7 +4338,10 @@ start:
return ret;
cs40l26_pm_runtime_teardown(cs40l26);
- goto start;
+
+ ret = cs40l26_dsp_pre_config(cs40l26);
+ if (ret)
+ return ret;
}
ret = cs40l26_coeff_load(cs40l26, tuning_num);
@@ -4429,7 +4552,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
of_property_read_bool(np, "cirrus,vbbr-enable");
if (!of_property_read_u32(np, "cirrus,vbbr-thld-mv", &val))
- cs40l26->pdata.vbbr_thld = val;
+ cs40l26->pdata.vbbr_thld_mv = val;
if (!of_property_read_u32(np, "cirrus,vbbr-max-att-db", &val))
cs40l26->pdata.vbbr_max_att = val;
@@ -4456,7 +4579,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
of_property_read_bool(np, "cirrus,vpbr-enable");
if (!of_property_read_u32(np, "cirrus,vpbr-thld-mv", &val))
- cs40l26->pdata.vpbr_thld = val;
+ cs40l26->pdata.vpbr_thld_mv = val;
if (!of_property_read_u32(np, "cirrus,vpbr-max-att-db", &val))
cs40l26->pdata.vpbr_max_att = val;
@@ -4641,6 +4764,27 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
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.
+ */
+
+ ret = cs40l26_set_pll_loop(cs40l26, CS40L26_PLL_REFCLK_SET_OPEN_LOOP);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_1);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_2);
+ if (ret)
+ return ret;
+
ret = cs40l26_part_num_resolve(cs40l26);
if (ret)
goto err;
@@ -4781,9 +4925,9 @@ int cs40l26_sys_suspend_noirq(struct device *dev)
}
EXPORT_SYMBOL(cs40l26_sys_suspend_noirq);
-void cs40l26_resume_error_handle(struct device *dev)
+void cs40l26_resume_error_handle(struct device *dev, int ret)
{
- dev_err(dev, "PM Runtime Resume failed\n");
+ dev_err(dev, "PM Runtime Resume failed: %d\n", ret);
pm_runtime_set_active(dev);
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 23c6b53..639c309 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -689,6 +689,8 @@
#define CS40L26_A1_PM_TIMEOUT_TICKS_STATIC_REG 0x02800350
#define CS40L26_A1_DSP_HALO_STATE_REG 0x02800fa8
#define CS40L26_A1_DSP_REQ_ACTIVE_REG 0x02800c08
+#define CS40L26_A1_EVENT_MAP_1 0x02806FC4
+#define CS40L26_A1_EVENT_MAP_2 0x02806FC8
/* algorithms */
@@ -846,13 +848,20 @@
#define CS40L26_DVL_FILE_NAME "cs40l26-dvl.bin"
#define CS40L26_CALIB_BIN_FILE_NAME "cs40l26-calib.bin"
+#define CS40L26_SVC_LE_EST_TIME_US 8000
#define CS40L26_SVC_LE_MAX_ATTEMPTS 2
#define CS40L26_SVC_DT_PREFIX "svc-le"
#define CS40L26_FW_ID 0x1800D4
-#define CS40L26_FW_A1_RAM_MIN_REV 0x07021C
+#define CS40L26_FW_MIN_REV 0x07021C
+#define CS40L26_FW_BRANCH 0x07
#define CS40L26_FW_CALIB_ID 0x1800DA
#define CS40L26_FW_CALIB_MIN_REV 0x010014
+#define CS40L26_FW_CALIB_BRANCH 0x01
+#define CS40L26_FW_MAINT_MIN_REV 0x27021A
+#define CS40L26_FW_MAINT_BRANCH 0x27
+#define CS40L26_FW_MAINT_CALIB_MIN_REV 0x210112
+#define CS40L26_FW_MAINT_CALIB_BRANCH 0x21
#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21)
#define CS40L26_CCM_CORE_RESET 0x00000200
@@ -870,7 +879,7 @@
#define CS40L26_VA_SUPPLY_NAME "VA"
#define CS40L26_MIN_RESET_PULSE_WIDTH 1500
-#define CS40L26_CONTROL_PORT_READY_DELAY 3000
+#define CS40L26_CONTROL_PORT_READY_DELAY 6000
/* haptic triggering */
#define CS40L26_TRIGGER_EFFECT 1
@@ -1095,8 +1104,11 @@
#define CS40L26_PLL_REFCLK_EN_SHIFT 4
#define CS40L26_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
#define CS40L26_PLL_REFCLK_FREQ_SHIFT 5
-#define CS40L26_PLL_REFCLK_OPEN_LOOP_MASK BIT(11)
-#define CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT 11
+#define CS40L26_PLL_REFCLK_LOOP_MASK BIT(11)
+#define CS40L26_PLL_REFCLK_LOOP_SHIFT 11
+#define CS40L26_PLL_REFCLK_SET_OPEN_LOOP 1
+#define CS40L26_PLL_REFCLK_SET_CLOSED_LOOP 0
+#define CS40L26_PLL_REFCLK_SET_ATTEMPTS 5
#define CS40L26_PLL_REFCLK_FORCE_EN_MASK BIT(16)
#define CS40L26_PLL_REFCLK_FORCE_EN_SHIFT 16
@@ -1412,14 +1424,14 @@ struct cs40l26_svc_le {
struct cs40l26_platform_data {
const char *device_name;
bool vbbr_en;
- u32 vbbr_thld;
+ u32 vbbr_thld_mv;
u32 vbbr_max_att;
u32 vbbr_atk_step;
u32 vbbr_atk_rate;
u32 vbbr_wait;
u32 vbbr_rel_rate;
bool vpbr_en;
- u32 vpbr_thld;
+ u32 vpbr_thld_mv;
u32 vpbr_max_att;
u32 vpbr_atk_step;
u32 vpbr_atk_rate;
@@ -1501,6 +1513,7 @@ struct cs40l26_private {
bool comp_enable_redc;
bool comp_enable_f0;
struct completion i2s_cont;
+ u8 vpbr_thld;
};
struct cs40l26_codec {
@@ -1515,8 +1528,6 @@ struct cs40l26_codec {
int tdm_width;
int tdm_slots;
int tdm_slot[2];
- bool svc_for_streaming_data;
- bool invert_streaming_data;
bool bypass_dsp;
};
@@ -1526,6 +1537,9 @@ struct cs40l26_pll_sysclk_config {
};
/* exported function prototypes */
+int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le);
+int cs40l26_set_pll_loop(struct cs40l26_private *cs40l26,
+ unsigned int pll_loop);
int cs40l26_dbc_enable(struct cs40l26_private *cs40l26, u32 enable);
int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
unsigned int *val);
@@ -1548,7 +1562,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);
+void cs40l26_resume_error_handle(struct device *dev, int ret);
int cs40l26_resume(struct device *dev);
int cs40l26_sys_resume(struct device *dev);
int cs40l26_sys_resume_noirq(struct device *dev);
@@ -1562,6 +1576,8 @@ int cs40l26_remove(struct cs40l26_private *cs40l26);
bool cs40l26_precious_reg(struct device *dev, unsigned int ret);
bool cs40l26_readable_reg(struct device *dev, unsigned int reg);
bool cs40l26_volatile_reg(struct device *dev, unsigned int reg);
+int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
+ u32 data, bool update, u8 op_code);
/* external tables */
extern const struct of_device_id cs40l26_of_match[CS40L26_NUM_DEVS + 1];