summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Peng <robinpeng@google.com>2022-06-06 18:52:29 +0800
committerRobin Peng <robinpeng@google.com>2022-06-06 18:52:29 +0800
commit9f2386ffbf1c84cf9a4941ae6d68464592054dec (patch)
treee50fc20189234fe97db428aac391f814991f2ab1
parent223604a73011fc0ad1b13b87ce7ce32127730978 (diff)
parent21a7d25270828dc0bac2db87c407b3b75e067313 (diff)
downloadamplifiers-9f2386ffbf1c84cf9a4941ae6d68464592054dec.tar.gz
Merge android13-gs-pixel-5.10-gs101-tm into android13-gs-pixel-5.10-gs101-tm-qpr1
Bug: 233569354 Signed-off-by: Robin Peng <robinpeng@google.com> Change-Id: Ief5924956895e216298d678f366880435319a3ea
-rw-r--r--cs40l26/cl_dsp.c4
-rw-r--r--cs40l26/cs40l26-codec.c170
-rw-r--r--cs40l26/cs40l26-sysfs.c404
-rw-r--r--cs40l26/cs40l26.c854
-rw-r--r--cs40l26/cs40l26.h91
5 files changed, 1022 insertions, 501 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c
index 1eda2d5..bcbee33 100644
--- a/cs40l26/cl_dsp.c
+++ b/cs40l26/cl_dsp.c
@@ -132,8 +132,10 @@ int cl_dsp_get_reg(struct cl_dsp *dsp, const char *coeff_name,
if (!dsp)
return -EPERM;
- if (list_empty(&dsp->coeff_desc_head))
+ if (list_empty(&dsp->coeff_desc_head)) {
+ dev_err(dsp->dev, "Coefficient list is empty\n");
return -ENOENT;
+ }
list_for_each_entry(coeff_desc, &dsp->coeff_desc_head, list) {
if (strncmp(coeff_desc->name, coeff_name,
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 1e3c086..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;
}
@@ -47,13 +47,13 @@ static ssize_t halo_heartbeat_show(struct device *dev,
int ret;
ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_HEARTBEAT",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, &reg);
if (ret)
return ret;
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;
}
@@ -181,8 +181,8 @@ 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");
+ if (!cs40l26->vibe_state_reporting) {
+ dev_err(cs40l26->dev, "vibe_state not supported\n");
return -EPERM;
}
@@ -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;
}
@@ -556,7 +556,7 @@ static ssize_t f0_comp_enable_show(struct device *dev,
mutex_lock(&cs40l26->lock);
- if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) {
ret = -EPERM;
goto err_mutex;
}
@@ -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;
}
@@ -601,7 +601,7 @@ static ssize_t f0_comp_enable_store(struct device *dev,
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) {
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) {
ret = -EPERM;
} else {
ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE",
@@ -636,7 +636,7 @@ static ssize_t redc_comp_enable_show(struct device *dev,
mutex_lock(&cs40l26->lock);
- if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) {
ret = -EPERM;
goto err_mutex;
}
@@ -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);
@@ -680,7 +680,7 @@ static ssize_t redc_comp_enable_store(struct device *dev,
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) {
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) {
ret = -EPERM;
} else {
ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE",
@@ -715,9 +715,9 @@ static ssize_t swap_firmware_show(struct device *dev,
mutex_lock(&cs40l26->lock);
- if (cs40l26->fw.id == CS40L26_FW_ID)
+ if (cs40l26->fw_id == CS40L26_FW_ID)
ret = snprintf(buf, PAGE_SIZE, "%d\n", 0);
- else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+ else if (cs40l26->fw_id == CS40L26_FW_CALIB_ID)
ret = snprintf(buf, PAGE_SIZE, "%d\n", 1);
else
ret = -EINVAL;
@@ -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;
}
@@ -819,33 +912,26 @@ static ssize_t dbc_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- unsigned int reg;
int ret;
u32 val;
ret = kstrtou32(buf, 10, &val);
- if (ret || (val != 0 && val != 1))
+ if (ret)
+ return ret;
+
+ if (val > 1)
return -EINVAL;
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);
- ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS", CL_DSP_XM_UNPACKED_TYPE,
- CS40L26_EXT_ALGO_ID, &reg);
- if (ret)
- goto err_pm;
-
- ret = regmap_update_bits(cs40l26->regmap, reg, CS40L26_DBC_ENABLE_MASK,
- val << CS40L26_DBC_ENABLE_SHIFT);
- if (ret)
- dev_err(cs40l26->dev, "Failed to update FLAGS\n");
+ ret = cs40l26_dbc_enable(cs40l26, val);
-err_pm:
mutex_unlock(&cs40l26->lock);
pm_runtime_mark_last_busy(cs40l26->dev);
@@ -871,12 +957,30 @@ static ssize_t dbc_env_rel_coef_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct device *cdev = cs40l26->dev;
+ u32 val;
int ret;
- ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_ENV_REL_COEF, buf);
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
- return ret ? ret : count;
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev, ret);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_ENV_REL_COEF, val);
+
+ mutex_unlock(&cs40l26->lock);
+
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
+ return ret ? ret : count;
}
static DEVICE_ATTR_RW(dbc_env_rel_coef);
@@ -896,9 +1000,28 @@ static ssize_t dbc_rise_headroom_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct device *cdev = cs40l26->dev;
+ u32 val;
int ret;
- ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_RISE_HEADROOM, buf);
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev, ret);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_RISE_HEADROOM, val);
+
+ mutex_unlock(&cs40l26->lock);
+
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
return ret ? ret : count;
}
@@ -920,9 +1043,28 @@ static ssize_t dbc_fall_headroom_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct device *cdev = cs40l26->dev;
+ u32 val;
int ret;
- ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_FALL_HEADROOM, buf);
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev, ret);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_FALL_HEADROOM, val);
+
+ mutex_unlock(&cs40l26->lock);
+
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
return ret ? ret : count;
}
@@ -944,9 +1086,28 @@ static ssize_t dbc_tx_lvl_thresh_fs_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct device *cdev = cs40l26->dev;
+ u32 val;
int ret;
- ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, buf);
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev, ret);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, val);
+
+ mutex_unlock(&cs40l26->lock);
+
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
return ret ? ret : count;
}
@@ -968,9 +1129,28 @@ static ssize_t dbc_tx_lvl_hold_off_ms_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ struct device *cdev = cs40l26->dev;
+ u32 val;
int ret;
- ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, buf);
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(cdev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cdev, ret);
+ return ret;
+ }
+
+ mutex_lock(&cs40l26->lock);
+
+ ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, val);
+
+ mutex_unlock(&cs40l26->lock);
+
+ pm_runtime_mark_last_busy(cdev);
+ pm_runtime_put_autosuspend(cdev);
return ret ? ret : count;
}
@@ -1015,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;
}
@@ -1047,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;
}
@@ -1084,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;
}
@@ -1121,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;
}
@@ -1158,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;
}
@@ -1200,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;
}
@@ -1237,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;
}
@@ -1282,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;
}
@@ -1319,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;
}
@@ -1362,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;
}
@@ -1399,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;
}
@@ -1441,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;
}
@@ -1473,39 +1653,69 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- int ret;
- u32 reg, freq_span, freq_centre, f0_and_q_cal_time_ms;
+ u32 reg, tone_dur_ms, freq_span_raw, freq_centre;
+ int ret, freq_span, f0_and_q_cal_time_ms;
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);
- ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_SPAN",
- CL_DSP_XM_UNPACKED_TYPE,
- CS40L26_F0_EST_ALGO_ID, &reg);
+ ret = cl_dsp_get_reg(cs40l26->dsp, "TONE_DURATION_MS",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_F0_EST_ALGO_ID, &reg);
if (ret)
goto err_mutex;
- ret = regmap_read(cs40l26->regmap, reg, &freq_span);
- if (ret)
+ ret = regmap_read(cs40l26->regmap, reg, &tone_dur_ms);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get tone duration\n");
goto err_mutex;
+ }
- ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_CENTRE",
- CL_DSP_XM_UNPACKED_TYPE,
- CS40L26_F0_EST_ALGO_ID, &reg);
- if (ret)
- goto err_mutex;
+ if (tone_dur_ms == 0) { /* Calculate value */
+ ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_SPAN",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_F0_EST_ALGO_ID, &reg);
+ if (ret)
+ goto err_mutex;
+
+ ret = regmap_read(cs40l26->regmap, reg, &freq_span_raw);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get FREQ_SPAN\n");
+ goto err_mutex;
+ }
+
+ if (freq_span_raw & CS40L26_F0_FREQ_SPAN_SIGN) /* Negative */
+ freq_span = (int) (0xFF000000 | freq_span_raw);
+ else
+ freq_span = (int) freq_span_raw;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_CENTRE",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_F0_EST_ALGO_ID, &reg);
+ if (ret)
+ goto err_mutex;
- ret = regmap_read(cs40l26->regmap, reg, &freq_centre);
+ ret = regmap_read(cs40l26->regmap, reg, &freq_centre);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get FREQ_CENTRE\n");
+ goto err_mutex;
+ }
- f0_and_q_cal_time_ms = ((CS40L26_F0_CHIRP_DURATION_FACTOR *
- (freq_span >> CS40L26_F0_EST_FREQ_SHIFT)) /
- (freq_centre >> CS40L26_F0_EST_FREQ_SHIFT)) +
- CS40L26_F0_AND_Q_CALIBRATION_BUFFER_MS;
+ f0_and_q_cal_time_ms =
+ ((CS40L26_F0_CHIRP_DURATION_FACTOR *
+ (int) (freq_span / CS40L26_F0_EST_FREQ_SCALE)) /
+ (int) (freq_centre / CS40L26_F0_EST_FREQ_SCALE));
+ } else if (tone_dur_ms < CS40L26_F0_AND_Q_CALIBRATION_MIN_MS) {
+ f0_and_q_cal_time_ms = CS40L26_F0_AND_Q_CALIBRATION_MIN_MS;
+ } else if (tone_dur_ms > CS40L26_F0_AND_Q_CALIBRATION_MAX_MS) {
+ f0_and_q_cal_time_ms = CS40L26_F0_AND_Q_CALIBRATION_MAX_MS;
+ } else {
+ f0_and_q_cal_time_ms = tone_dur_ms;
+ }
err_mutex:
mutex_unlock(&cs40l26->lock);
@@ -1529,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;
}
@@ -1537,7 +1747,7 @@ static ssize_t redc_cal_time_ms_show(struct device *dev,
ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_PLAYTIME_MS",
CL_DSP_XM_UNPACKED_TYPE,
- cs40l26->fw.id, &reg);
+ cs40l26->fw_id, &reg);
if (ret)
goto err_mutex;
@@ -1568,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;
}
@@ -1612,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;
}
@@ -1634,7 +1844,7 @@ static ssize_t logging_en_store(struct device *dev,
if (ret)
goto exit_mutex;
- if (cs40l26->fw.id == CS40L26_FW_ID) {
+ if (cs40l26->fw_id == CS40L26_FW_ID) {
if (src_count != CS40L26_LOGGER_SRC_COUNT) {
dev_err(cdev, "Unexpected source count %u\n",
src_count);
@@ -1656,7 +1866,7 @@ static ssize_t logging_en_store(struct device *dev,
ret = -EINVAL;
goto exit_mutex;
}
- } else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) {
+ } 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);
@@ -1684,7 +1894,7 @@ static ssize_t logging_en_store(struct device *dev,
}
} else {
dev_err(cdev, "Invalid firmware ID 0x%06X\n",
- cs40l26->fw.id);
+ cs40l26->fw_id);
goto exit_mutex;
}
@@ -1725,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;
}
@@ -1747,7 +1957,7 @@ 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) {
+ if (cs40l26->fw_id != CS40L26_FW_CALIB_ID) {
dev_err(cs40l26->dev, "Calib. FW required for BEMF logging\n");
return -EPERM;
}
@@ -1785,7 +1995,7 @@ 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) {
+ if (cs40l26->fw_id != CS40L26_FW_CALIB_ID) {
dev_err(cs40l26->dev, "Calib. FW required for VBST logging\n");
return -EPERM;
}
@@ -1824,7 +2034,7 @@ static ssize_t max_vmon_show(struct device *dev, struct device_attribute *attr,
u8 offset;
int ret;
- if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+ if (cs40l26->fw_id == CS40L26_FW_CALIB_ID)
offset = CS40L26_LOGGER_DATA_3_MAX_OFFSET;
else
offset = CS40L26_LOGGER_DATA_1_MAX_OFFSET;
@@ -1853,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 c123910..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;
}
@@ -211,10 +240,10 @@ err_pm:
EXPORT_SYMBOL(cs40l26_dbc_get);
int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
- const char *buf)
+ u32 val)
{
struct device *dev = cs40l26->dev;
- unsigned int val, reg, max;
+ unsigned int reg, max;
int ret;
if (dbc == CS40L26_DBC_TX_LVL_HOLD_OFF_MS)
@@ -222,40 +251,20 @@ int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
else
max = CS40L26_DBC_CONTROLS_MAX;
- ret = kstrtou32(buf, 10, &val);
- if (ret) {
- dev_err(dev, "Failed to kstrstou32()\n");
- return ret;
- }
-
if (val > max) {
dev_err(dev, "DBC input %u out of bounds\n", val);
return -EINVAL;
}
- ret = pm_runtime_get_sync(dev);
- if (ret < 0) {
- cs40l26_resume_error_handle(dev);
- return ret;
- }
-
- mutex_lock(&cs40l26->lock);
-
ret = cl_dsp_get_reg(cs40l26->dsp, cs40l26_dbc_names[dbc],
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
if (ret)
- goto err_pm;
+ return ret;
ret = regmap_write(cs40l26->regmap, reg, val);
if (ret)
dev_err(dev, "Failed to write Dynamic Boost Control value\n");
-err_pm:
- mutex_unlock(&cs40l26->lock);
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
-
return ret;
}
EXPORT_SYMBOL(cs40l26_dbc_set);
@@ -586,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;
@@ -718,8 +727,8 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
complete(&cs40l26->i2s_cont);
break;
case CS40L26_DSP_MBOX_TRIGGER_CP:
- if (!cs40l26->pdata.vibe_state_reporting) {
- dev_err(dev, "cirrus,vibe-state not in DT\n");
+ if (!cs40l26->vibe_state_reporting) {
+ dev_err(dev, "vibe_state not supported\n");
return -EPERM;
}
@@ -971,7 +980,7 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
CS40L26_WKSRC_STS_SHIFT);
ret = cl_dsp_get_reg(cs40l26->dsp, "LAST_WAKESRC_CTL",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, &reg);
if (ret)
goto err;
@@ -1219,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");
@@ -1338,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;
@@ -1351,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;
@@ -1464,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,
@@ -1639,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,
@@ -1729,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;
}
@@ -1774,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);
@@ -1822,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);
@@ -1892,7 +1940,7 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
goto err_mutex;
}
- if (!cs40l26->pdata.vibe_state_reporting)
+ if (!cs40l26->vibe_state_reporting)
cs40l26_vibe_state_update(cs40l26,
CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK);
err_mutex:
@@ -1909,19 +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)
- 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) {
@@ -2198,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;
}
@@ -2336,7 +2385,7 @@ static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26,
}
static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data,
- u32 in_data_nibbles, bool pwle, u8 **out_data)
+ u32 in_data_nibbles, bool pwle, bool svc_waveform, u8 **out_data)
{
u8 nsections, global_rep, out_nsections = 0;
int ret = 0, pos_byte = 0, in_pos_nib = 2;
@@ -2363,10 +2412,14 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data,
out_ch = cl_dsp_memchunk_create((void *) *out_data,
out_data_bytes);
cl_dsp_memchunk_write(&out_ch, 16,
- CS40L26_WT_HEADER_DEFAULT_FLAGS);
+ CS40L26_WT_HEADER_DEFAULT_FLAGS |
+ (svc_waveform ? CS40L26_OWT_SVC_METADATA : 0));
cl_dsp_memchunk_write(&out_ch, 8, WT_TYPE_V6_PWLE);
- cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET);
- cl_dsp_memchunk_write(&out_ch, 24, in_data_bytes / 4);
+ cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET +
+ (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0));
+ cl_dsp_memchunk_write(&out_ch, 24, (in_data_bytes / 4) -
+ (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0));
+
memcpy(*out_data + out_ch.bytes, in_data, in_data_bytes);
@@ -2615,10 +2668,11 @@ static void cs40l26_upload_worker(struct work_struct *work)
u32 trigger_index, min_index, max_index, nwaves;
struct ff_effect *effect;
u16 index, bank;
- bool pwle;
+ 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);
@@ -2632,13 +2686,16 @@ static void cs40l26_upload_worker(struct work_struct *work)
switch (effect->u.periodic.waveform) {
case FF_CUSTOM:
- pwle = (cs40l26->raw_custom_data[0] == 0x0000) ? false : true;
+ pwle = (cs40l26->raw_custom_data[0] ==
+ CS40L26_WT_TYPE10_COMP_BUFFER) ? false : true;
+ svc_waveform = (cs40l26->raw_custom_data[0] ==
+ CS40L26_SVC_ID) ? true : false;
len = effect->u.periodic.custom_len;
if (len > CS40L26_CUSTOM_DATA_SIZE) {
refactored_size = cs40l26_refactor_owt(cs40l26,
- cs40l26->raw_custom_data, len, pwle,
+ cs40l26->raw_custom_data, len, pwle, svc_waveform,
&refactored_data);
if (refactored_size <= 0) {
@@ -2902,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);
@@ -3080,83 +3138,6 @@ static int cs40l26_part_num_resolve(struct cs40l26_private *cs40l26)
return 0;
}
-static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
-{
- int ret = 0, i;
-
- if (cs40l26->dsp) {
- ret = cl_dsp_destroy(cs40l26->dsp);
- if (ret) {
- dev_err(cs40l26->dev,
- "Failed to destroy existing DSP structure\n");
- return ret;
- }
- cs40l26->dsp = NULL;
- }
-
- cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap);
- if (!cs40l26->dsp) {
- dev_err(cs40l26->dev, "Failed to allocate space for DSP\n");
- return -ENOMEM;
- }
-
- cs40l26->fw.id = id;
-
- if (id == CS40L26_FW_ID) {
- cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
- 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_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->fw.num_coeff_files, sizeof(char *),
- GFP_KERNEL);
-
- 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,
- CS40L26_TUNING_FILE_NAME_MAX_LEN,
- GFP_KERNEL);
- } else {
- memset(cs40l26->fw.coeff_files[i], 0,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
- }
- }
-
- strscpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
- CS40L26_WT_FILE_NAME_LEN);
-
- if (id == CS40L26_FW_ID) {
- strscpy(cs40l26->fw.coeff_files[1],
- CS40L26_A2H_TUNING_FILE_NAME,
- CS40L26_A2H_TUNING_FILE_NAME_LEN);
- strscpy(cs40l26->fw.coeff_files[2],
- CS40L26_SVC_TUNING_FILE_NAME,
- CS40L26_SVC_TUNING_FILE_NAME_LEN);
- strscpy(cs40l26->fw.coeff_files[3],
- CS40L26_DVL_FILE_NAME,
- CS40L26_DVL_FILE_NAME_LEN);
- } else {
- strscpy(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);
-
- return ret;
-}
-
static int cs40l26_wksrc_config(struct cs40l26_private *cs40l26)
{
u8 mask_wksrc;
@@ -3260,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;
@@ -3344,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;
}
@@ -3360,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);
@@ -3447,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;
}
@@ -3457,43 +3455,6 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
pseq_val, pseq_mask);
}
-static int cs40l26_verify_fw(struct cs40l26_private *cs40l26)
-{
- struct cs40l26_fw *fw = &cs40l26->fw;
- unsigned int val;
- int ret;
-
- ret = cl_dsp_fw_id_get(cs40l26->dsp, &val);
- if (ret)
- return ret;
-
- if (val != cs40l26->fw.id) {
- dev_err(cs40l26->dev, "Invalid firmware ID: 0x%X\n", val);
- return -EINVAL;
- }
-
- ret = cl_dsp_fw_rev_get(cs40l26->dsp, &val);
- if (ret)
- return ret;
-
- if ((val & ~CS40L26_FW_BRANCH_MASK) < fw->min_rev) {
- dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n",
- (int) CL_DSP_GET_MAJOR(val),
- (int) CL_DSP_GET_MINOR(val),
- (int) CL_DSP_GET_PATCH(val));
- return -EINVAL;
- }
-
- dev_info(cs40l26->dev, "Firmware revision %d.%d.%d\n",
- (int) CL_DSP_GET_MAJOR(val),
- (int) CL_DSP_GET_MINOR(val),
- (int) CL_DSP_GET_PATCH(val));
-
- cs40l26->fw.rev = val;
-
- return 0;
-}
-
static int cs40l26_asp_config(struct cs40l26_private *cs40l26)
{
struct reg_sequence *dsp1rx_config =
@@ -3553,6 +3514,26 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26)
return ret;
}
+static int cs40l26_zero_cross_config(struct cs40l26_private *cs40l26)
+{
+ int ret = 0;
+ u32 reg;
+
+ if (cs40l26->pdata.pwle_zero_cross) {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "PWLE_EXTEND_ZERO_CROSS",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(cs40l26->regmap, reg, 1);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to set PWLE_EXTEND_ZERO_CROSS\n");
+
+ }
+
+ return ret;
+}
+
static int calib_device_tree_config(struct cs40l26_private *cs40l26)
{
int ret = 0;
@@ -3797,6 +3778,51 @@ static int cs40l26_handle_errata(struct cs40l26_private *cs40l26)
false, CS40L26_PSEQ_OP_WRITE_FULL);
}
+int cs40l26_dbc_enable(struct cs40l26_private *cs40l26, u32 enable)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS", CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_EXT_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(cs40l26->regmap, reg, CS40L26_DBC_ENABLE_MASK,
+ enable << CS40L26_DBC_ENABLE_SHIFT);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to %s DBC\n",
+ (enable == 1) ? "enable" : "disable");
+
+ return ret;
+}
+EXPORT_SYMBOL(cs40l26_dbc_enable);
+
+static int cs40l26_handle_dbc_defaults(struct cs40l26_private *cs40l26)
+{
+ unsigned int i;
+ u32 val;
+ int ret;
+
+ for (i = 0; i < CS40L26_DBC_NUM_CONTROLS; i++) {
+ val = cs40l26->pdata.dbc_defaults[i];
+
+ if (val != CS40L26_DBC_USE_DEFAULT) {
+ ret = cs40l26_dbc_set(cs40l26, i, val);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (cs40l26->pdata.dbc_enable_default) {
+ ret = cs40l26_dbc_enable(cs40l26, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
{
struct regmap *regmap = cs40l26->regmap;
@@ -3805,10 +3831,6 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
u32 reg, nwaves, value;
int ret;
- ret = cs40l26_verify_fw(cs40l26);
- if (ret)
- return ret;
-
ret = regmap_update_bits(regmap, CS40L26_PWRMGT_CTL,
CS40L26_MEM_RDY_MASK, 1 << CS40L26_MEM_RDY_SHIFT);
if (ret) {
@@ -3817,7 +3839,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
}
ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, &reg);
if (ret)
return ret;
@@ -3835,7 +3857,6 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
-
ret = cs40l26_handle_errata(cs40l26);
if (ret)
return ret;
@@ -3851,7 +3872,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
/* ensure firmware running */
ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, &reg);
if (ret)
return ret;
@@ -3891,6 +3912,14 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
+ ret = cs40l26_handle_dbc_defaults(cs40l26);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_zero_cross_config(cs40l26);
+ if (ret)
+ return ret;
+
if (!cs40l26->vibe_init_success) {
ret = calib_device_tree_config(cs40l26);
if (ret)
@@ -3901,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;
}
@@ -3943,7 +3972,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
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) {
+ 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);
@@ -3984,79 +4013,74 @@ 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)
+int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le)
{
- unsigned int reg, le = 0;
- char svc_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN];
- char wt_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN];
- char n_str[2];
- 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);
+
+ if (le_est)
+ break;
+ }
+
+ *le = 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) {
- strscpy(svc_bin_file,
- CS40L26_SVC_TUNING_FILE_PREFIX,
- CS40L26_SVC_TUNING_FILE_PREFIX_LEN);
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_svc_le_estimate);
- strscpy(wt_bin_file, CS40L26_WT_FILE_PREFIX,
- CS40L26_WT_FILE_PREFIX_LEN);
+static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26,
+ u32 *tuning_num)
+{
+ unsigned int le;
+ int ret, i;
- snprintf(n_str, 2, "%d",
- cs40l26->svc_le_vals[j]->n);
+ ret = pm_runtime_get_sync(cs40l26->dev);
+ if (ret < 0) {
+ cs40l26_resume_error_handle(cs40l26->dev, ret);
+ return ret;
+ }
- strncat(svc_bin_file, n_str, 2);
- strncat(wt_bin_file, n_str, 2);
+ ret = cs40l26_svc_le_estimate(cs40l26, &le);
+ if (ret)
+ goto pm_err;
- cs40l26_gain_adjust(cs40l26,
- cs40l26->svc_le_vals[j]->gain_adjust);
+ 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");
- } else {
- strncat(svc_bin_file, CS40L26_TUNING_FILE_SUFFIX,
- CS40L26_TUNING_FILE_SUFFIX_LEN);
- strncat(wt_bin_file, CS40L26_TUNING_FILE_SUFFIX,
- CS40L26_TUNING_FILE_SUFFIX_LEN);
-
- strscpy(cs40l26->fw.coeff_files[0], wt_bin_file,
- CS40L26_WT_FILE_CONCAT_NAME_LEN);
- strscpy(cs40l26->fw.coeff_files[2], svc_bin_file,
- CS40L26_SVC_TUNING_FILE_NAME_LEN);
- }
pm_err:
pm_runtime_mark_last_busy(cs40l26->dev);
@@ -4065,29 +4089,103 @@ pm_err:
return ret;
}
-static void cs40l26_coeff_load(struct cs40l26_private *cs40l26)
+static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n,
+ u32 tuning)
+{
+ char svc_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN];
+ char wt_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN];
+ char **coeff_files, tuning_str[2];
+ int i;
+
+ coeff_files = kcalloc(n, sizeof(char *), GFP_KERNEL);
+ if (!coeff_files)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ coeff_files[i] =
+ kzalloc(CS40L26_TUNING_FILE_NAME_MAX_LEN, GFP_KERNEL);
+ if (!coeff_files[i])
+ goto err_free;
+ }
+
+ strscpy(svc_tuning, CS40L26_SVC_TUNING_FILE_PREFIX,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+
+ if (tuning) { /* Concatenate tuning number if required */
+ strscpy(wt_tuning, CS40L26_WT_FILE_PREFIX,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+
+ snprintf(tuning_str, 2, "%d", tuning);
+
+ strncat(wt_tuning, tuning_str, 2);
+ strncat(svc_tuning, tuning_str, 2);
+
+ strncat(wt_tuning, CS40L26_TUNING_FILE_SUFFIX,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ } else {
+ strscpy(wt_tuning, CS40L26_WT_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ }
+
+ strncat(svc_tuning, CS40L26_TUNING_FILE_SUFFIX,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+
+ strscpy(coeff_files[0], wt_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN);
+
+ if (cs40l26->fw_id == CS40L26_FW_ID) {
+ strscpy(coeff_files[1], CS40L26_A2H_TUNING_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ strscpy(coeff_files[2], svc_tuning,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ strscpy(coeff_files[3], CS40L26_DVL_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ } else {
+ strscpy(coeff_files[1], CS40L26_CALIB_BIN_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ }
+
+ return coeff_files;
+
+err_free:
+ kfree(coeff_files);
+ return NULL;
+}
+
+static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning)
{
struct device *dev = cs40l26->dev;
const struct firmware *coeff;
- int i, ret;
+ int i, ret, num_files;
+ char **coeff_files;
+
+ num_files = (cs40l26->fw_id == CS40L26_FW_ID) ?
+ CS40L26_TUNING_FILES_RUNTIME : CS40L26_TUNING_FILES_CAL;
- for (i = 0; i < cs40l26->fw.num_coeff_files; i++) {
- ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev);
+ coeff_files = cs40l26_get_tuning_names(cs40l26, num_files, tuning);
+ if (!coeff_files)
+ return -ENOMEM;
+
+ for (i = 0; i < num_files; i++) {
+ ret = request_firmware(&coeff, coeff_files[i], dev);
if (ret) {
- dev_warn(dev, "Continuing...");
+ dev_warn(dev, "Continuing...\n");
continue;
}
ret = cl_dsp_coeff_file_parse(cs40l26->dsp, coeff);
if (ret)
- dev_warn(dev, "Failed to load, %s, %d. Continuing...",
- cs40l26->fw.coeff_files[i], ret);
+ dev_warn(dev, "Failed to load %s, %d. Continuing...\n",
+ coeff_files[i], ret);
else
dev_info(dev, "%s Loaded Successfully\n",
- cs40l26->fw.coeff_files[i]);
+ coeff_files[i]);
release_firmware(coeff);
}
+
+ kfree(coeff_files);
+
+ return 0;
}
static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26)
@@ -4103,53 +4201,139 @@ static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26)
cs40l26->pdata.pm_active_timeout_ms);
}
-static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id)
+static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26)
{
- struct device *dev = cs40l26->dev;
- const struct firmware *fw;
- int ret;
+ u32 id, min_rev, rev, branch;
+ int ret, maj, min, patch;
- if (cs40l26->fw.id == CS40L26_FW_ID)
- ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev);
- else
- ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev);
+ 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)
return ret;
- ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true);
- release_firmware(fw);
+ switch (id) {
+ case CS40L26_FW_ID:
+ 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:
+ 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;
+ }
if (ret) {
- dev_err(dev, "cl_dsp_firmware_parse failed, %d", ret);
+ dev_err(cs40l26->dev, "Rev. Branch 0x%02X invalid\n", maj);
return ret;
}
- return cs40l26_change_fw_control_defaults(cs40l26);
+ if (rev < min_rev) {
+ dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n",
+ maj, min, patch);
+ return -EINVAL;
+ }
+
+ cs40l26->fw_id = id;
+
+ dev_info(cs40l26->dev, "Firmware revision %d.%d.%d\n", maj, min, patch);
+
+ return 0;
+}
+
+static int cs40l26_cl_dsp_reinit(struct cs40l26_private *cs40l26)
+{
+ int ret;
+
+ if (cs40l26->dsp) {
+ ret = cl_dsp_destroy(cs40l26->dsp);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to destroy DSP struct\n");
+ return ret;
+ }
+
+ cs40l26->dsp = NULL;
+ }
+
+ cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap);
+ if (!cs40l26->dsp)
+ return -ENOMEM;
+
+ return cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID,
+ CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
}
-static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id)
+static int cs40l26_fw_upload(struct cs40l26_private *cs40l26)
{
+ bool svc_le_required = cs40l26->num_svc_le_vals && !cs40l26->calib_fw;
+ struct device *dev = cs40l26->dev;
+ u32 tuning_num = 0;
+ const struct firmware *fw;
int ret;
- ret = cs40l26_cl_dsp_init(cs40l26, id);
+ cs40l26->fw_loaded = false;
+
+ ret = cs40l26_cl_dsp_reinit(cs40l26);
if (ret)
return ret;
+ if (cs40l26->calib_fw)
+ ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev);
+ else
+ ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev);
+
+ if (ret) {
+ release_firmware(fw);
+ return ret;
+ }
+
ret = cs40l26_dsp_pre_config(cs40l26);
if (ret)
return ret;
- ret = cs40l26_firmware_load(cs40l26, id);
+ ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true);
+ release_firmware(fw);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_change_fw_control_defaults(cs40l26);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_get_fw_params(cs40l26);
if (ret)
return ret;
- if (cs40l26->num_svc_le_vals && id != CS40L26_FW_CALIB_ID) {
+ if (svc_le_required) {
ret = cs40l26_dsp_config(cs40l26);
if (ret)
return ret;
- ret = cs40l26_tuning_select_from_svc_le(cs40l26);
+ ret = cs40l26_tuning_select_from_svc_le(cs40l26, &tuning_num);
if (ret)
return ret;
@@ -4160,7 +4344,9 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id)
return ret;
}
- cs40l26_coeff_load(cs40l26);
+ ret = cs40l26_coeff_load(cs40l26, tuning_num);
+ if (ret)
+ return ret;
return cs40l26_dsp_config(cs40l26);
}
@@ -4168,41 +4354,22 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id)
int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
{
struct device *dev = cs40l26->dev;
- bool register_irq = false;
+ bool deferred = cs40l26->fw_defer;
u32 pseq_rom_end_of_script_loc;
int ret;
- if (cs40l26->fw_loaded) {
- 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) {
- dev_err(cs40l26->dev, "Can't set pm_timeout %d\n", ret);
- return ret;
- }
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
- }
-
if (cs40l26->fw_defer) {
cs40l26->fw_defer = false;
- register_irq = true;
} else {
disable_irq(cs40l26->irq);
cs40l26_pm_runtime_teardown(cs40l26);
}
- cs40l26->fw_loaded = false;
-
if (cs40l26->revid != CS40L26_REVID_A1 &&
cs40l26->revid != CS40L26_REVID_B0) {
dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid);
- return -EINVAL;
+ ret = -EINVAL;
+ goto defer_err;
}
/* reset pseq END_OF_SCRIPT to location from ROM */
@@ -4212,27 +4379,38 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT);
if (ret) {
dev_err(dev, "Failed to reset pseq END_OF_SCRIPT %d\n", ret);
- return ret;
+ goto defer_err;
}
- ret = cs40l26_fw_upload(cs40l26, id);
+ if (id == CS40L26_FW_CALIB_ID)
+ cs40l26->calib_fw = true;
+ else
+ cs40l26->calib_fw = false;
+
+ ret = cs40l26_fw_upload(cs40l26);
if (ret)
- return ret;
+ goto defer_err;
- if (register_irq) {
+ if (deferred) {
ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL,
cs40l26_irq, IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_LOW, "cs40l26", cs40l26);
if (ret) {
dev_err(dev, "Failed to request threaded IRQ: %d\n",
ret);
- return ret;
+ goto defer_err;
}
} else {
enable_irq(cs40l26->irq);
}
return 0;
+
+defer_err:
+ if (deferred)
+ cs40l26->fw_defer = true;
+
+ return ret;
}
EXPORT_SYMBOL(cs40l26_fw_swap);
@@ -4362,10 +4540,8 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
if (of_property_read_bool(np, "cirrus,fw-defer"))
cs40l26->fw_defer = true;
- 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,calib-fw"))
+ cs40l26->calib_fw = true;
if (of_property_read_bool(np, "cirrus,bst-expl-mode-disable"))
cs40l26->pdata.expl_mode_enabled = false;
@@ -4376,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;
@@ -4403,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;
@@ -4480,6 +4656,47 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
if (!of_property_read_u32(np, "cirrus,q-default", &val))
cs40l26->pdata.q_default = val;
+ if (of_property_read_bool(np, "cirrus,dbc-enable"))
+ cs40l26->pdata.dbc_enable_default = true;
+ else
+ cs40l26->pdata.dbc_enable_default = false;
+
+ if (!of_property_read_u32(np, "cirrus,dbc-env-rel-coef", &val))
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_ENV_REL_COEF] = val;
+ else
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_ENV_REL_COEF] =
+ CS40L26_DBC_USE_DEFAULT;
+
+ if (!of_property_read_u32(np, "cirrus,dbc-fall-headroom", &val))
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_FALL_HEADROOM] = val;
+ else
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_FALL_HEADROOM] =
+ CS40L26_DBC_USE_DEFAULT;
+
+ if (!of_property_read_u32(np, "cirrus,dbc-rise-headroom", &val))
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_RISE_HEADROOM] = val;
+ else
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_RISE_HEADROOM] =
+ CS40L26_DBC_USE_DEFAULT;
+
+ if (!of_property_read_u32(np, "cirrus,dbc-tx-lvl-hold-off-ms", &val))
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_HOLD_OFF_MS] =
+ val;
+ else
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_HOLD_OFF_MS] =
+ CS40L26_DBC_USE_DEFAULT;
+
+ if (!of_property_read_u32(np, "cirrus,dbc-tx-lvl-thresh-fs", &val))
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_THRESH_FS] = val;
+ else
+ cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_THRESH_FS] =
+ CS40L26_DBC_USE_DEFAULT;
+
+ if (of_property_read_bool(np, "cirrus,pwle-zero-cross-en"))
+ cs40l26->pdata.pwle_zero_cross = true;
+ else
+ cs40l26->pdata.pwle_zero_cross = false;
+
return 0;
}
@@ -4547,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;
@@ -4558,12 +4796,11 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
}
cs40l26->pm_ready = false;
- cs40l26->fw_loaded = false;
init_completion(&cs40l26->i2s_cont);
if (!cs40l26->fw_defer) {
- ret = cs40l26_fw_upload(cs40l26, CS40L26_FW_ID);
+ ret = cs40l26_fw_upload(cs40l26);
if (ret)
goto err;
@@ -4574,7 +4811,6 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
dev_err(dev, "Failed to request threaded IRQ\n");
goto err;
}
-
}
ret = cs40l26_input_init(cs40l26);
@@ -4689,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 8a99371..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 */
@@ -830,38 +832,36 @@
#define CS40L26_FW_FILE_NAME "cs40l26.wmfw"
#define CS40L26_FW_CALIB_NAME "cs40l26-calib.wmfw"
-#define CS40L26_TUNING_FILES_MAX 4
-#define CS40L26_TUNING_FILES_RT 4
+#define CS40L26_TUNING_FILES_RUNTIME 4
#define CS40L26_TUNING_FILES_CAL 2
#define CS40L26_WT_FILE_NAME "cs40l26.bin"
-#define CS40L26_WT_FILE_NAME_LEN 12
#define CS40L26_WT_FILE_PREFIX "cs40l26-wt"
#define CS40L26_WT_FILE_PREFIX_LEN 11
-#define CS40L26_WT_FILE_CONCAT_NAME_LEN 16
#define CS40L26_SVC_TUNING_FILE_PREFIX "cs40l26-svc"
#define CS40L26_SVC_TUNING_FILE_PREFIX_LEN 12
#define CS40L26_SVC_TUNING_FILE_NAME "cs40l26-svc.bin"
-#define CS40L26_SVC_TUNING_FILE_NAME_LEN 17
#define CS40L26_A2H_TUNING_FILE_NAME "cs40l26-a2h.bin"
-#define CS40L26_A2H_TUNING_FILE_NAME_LEN 16
#define CS40L26_TUNING_FILE_NAME_MAX_LEN 20
#define CS40L26_TUNING_FILE_SUFFIX ".bin"
#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_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_ROM_MIN_REV 0x040000
-#define CS40L26_FW_A0_RAM_MIN_REV 0x050004
-#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 0x010000
+#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
@@ -879,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
@@ -1104,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
@@ -1182,9 +1185,12 @@
#define CS40L26_WT_MAX_TIME_VAL 16383 /* ms */
#define CS40L26_WT_HEADER_OFFSET 3
+#define CS40L26_WT_METADATA_OFFSET 3
#define CS40L26_WT_HEADER_DEFAULT_FLAGS 0x0000
#define CS40L26_WT_HEADER_PWLE_SIZE 12
#define CS40L26_WT_HEADER_COMP_SIZE 20
+#define CS40L26_OWT_SVC_METADATA BIT(10)
+#define CS40L26_SVC_ID 0x100
#define CS40L26_WT_TYPE10_SECTION_BYTES_MIN 8
#define CS40L26_WT_TYPE10_SECTION_BYTES_MAX 12
@@ -1192,6 +1198,7 @@
#define CS40L26_WT_TYPE10_WAVELEN_INDEF 0x400000
#define CS40L26_WT_TYPE10_WAVELEN_CALCULATED 0x800000
#define CS40L26_WT_TYPE10_COMP_DURATION_FLAG 0x8
+#define CS40L26_WT_TYPE10_COMP_BUFFER 0x0000
/* F0 Offset represented as Q10.14 format */
#define CS40L26_F0_OFFSET_MAX 0x190000 /* +100 Hz */
@@ -1203,14 +1210,17 @@
#define CS40L26_Q_EST_MIN 0
#define CS40L26_Q_EST_MAX 0x7FFFFF
-#define CS40L26_F0_EST_FREQ_SHIFT 14 /* centre, span, and f0 in Q10.14 */
+#define CS40L26_F0_EST_FREQ_SCALE 16384
-#define CS40L26_SVC_INITIALIZATION_PERIOD_MS 6
-#define CS40L26_REDC_CALIBRATION_BUFFER_MS 10
-#define CS40L26_F0_AND_Q_CALIBRATION_BUFFER_MS 100
-#define CS40L26_F0_CHIRP_DURATION_FACTOR 3662 /* t=factor*span/center */
-#define CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q BIT(0)
-#define CS40L26_CALIBRATION_CONTROL_REQUEST_REDC BIT(1)
+#define CS40L26_SVC_INITIALIZATION_PERIOD_MS 6
+#define CS40L26_REDC_CALIBRATION_BUFFER_MS 10
+#define CS40L26_F0_AND_Q_CALIBRATION_MIN_MS 100
+#define CS40L26_F0_AND_Q_CALIBRATION_MAX_MS 1800
+#define CS40L26_F0_CHIRP_DURATION_FACTOR 3750
+#define CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q BIT(0)
+#define CS40L26_CALIBRATION_CONTROL_REQUEST_REDC BIT(1)
+#define CS40L26_F0_FREQ_SPAN_MASK GENMASK(23, 0)
+#define CS40L26_F0_FREQ_SPAN_SIGN BIT(23)
#define CS40L26_LOGGER_SRC_SIZE_MASK BIT(22)
#define CS40L26_LOGGER_SRC_SIZE_SHIFT 22
@@ -1250,6 +1260,7 @@
#define CS40L26_DBC_FALL_HEADROOM_NAME "DBC_FALL_HEADROOM"
#define CS40L26_DBC_TX_LVL_THRESH_FS_NAME "DBC_TX_LVL_THRESH_FS"
#define CS40L26_DBC_TX_LVL_HOLD_OFF_MS_NAME "DBC_TX_LVL_HOLD_OFF_MS"
+#define CS40L26_DBC_USE_DEFAULT 0xFFFFFFFF
/* Errata */
#define CS40L26_ERRATA_A1_NUM_WRITES 4
@@ -1385,13 +1396,6 @@ enum cs40l26_pm_state {
};
/* structs */
-struct cs40l26_fw {
- unsigned int id;
- unsigned int rev;
- unsigned int min_rev;
- unsigned int num_coeff_files;
- char **coeff_files;
-};
struct cs40l26_owt_section {
u8 flags;
@@ -1420,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;
@@ -1442,8 +1446,10 @@ struct cs40l26_platform_data {
u32 redc_default;
u32 q_default;
u32 boost_ctl;
- bool vibe_state_reporting;
bool expl_mode_enabled;
+ bool dbc_enable_default;
+ u32 dbc_defaults[CS40L26_DBC_NUM_CONTROLS];
+ bool pwle_zero_cross;
};
struct cs40l26_owt {
@@ -1480,11 +1486,12 @@ struct cs40l26_private {
u32 pseq_base;
struct list_head pseq_op_head;
enum cs40l26_pm_state pm_state;
+ u32 fw_id;
bool fw_defer;
- enum cs40l26_vibe_state vibe_state;
- int num_loaded_coeff_files;
- struct cs40l26_fw fw;
bool fw_loaded;
+ bool calib_fw;
+ enum cs40l26_vibe_state vibe_state;
+ bool vibe_state_reporting;
bool pm_ready;
bool asp_enable;
u8 last_wksrc_pol;
@@ -1506,6 +1513,7 @@ struct cs40l26_private {
bool comp_enable_redc;
bool comp_enable_f0;
struct completion i2s_cont;
+ u8 vpbr_thld;
};
struct cs40l26_codec {
@@ -1520,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;
};
@@ -1531,10 +1537,14 @@ 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);
int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc,
- const char *buf);
+ u32 val);
int cs40l26_asp_start(struct cs40l26_private *cs40l26);
int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves);
int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id);
@@ -1552,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);
@@ -1566,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];
@@ -1580,7 +1592,6 @@ extern const struct reg_sequence
cs40l26_a1_errata[CS40L26_ERRATA_A1_NUM_WRITES];
extern const char * const cs40l26_dbc_names[CS40L26_DBC_NUM_CONTROLS];
-
/* sysfs */
extern struct attribute_group cs40l26_dev_attr_group;
extern struct attribute_group cs40l26_dev_attr_cal_group;