diff options
author | Robin Peng <robinpeng@google.com> | 2022-06-06 18:52:29 +0800 |
---|---|---|
committer | Robin Peng <robinpeng@google.com> | 2022-06-06 18:52:29 +0800 |
commit | 9f2386ffbf1c84cf9a4941ae6d68464592054dec (patch) | |
tree | e50fc20189234fe97db428aac391f814991f2ab1 | |
parent | 223604a73011fc0ad1b13b87ce7ce32127730978 (diff) | |
parent | 21a7d25270828dc0bac2db87c407b3b75e067313 (diff) | |
download | amplifiers-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.c | 4 | ||||
-rw-r--r-- | cs40l26/cs40l26-codec.c | 170 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 404 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 854 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 91 |
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, ®); - 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, ®); - 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); 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, ®); - 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, ®); + ret = cl_dsp_get_reg(cs40l26->dsp, "TONE_DURATION_MS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_F0_EST_ALGO_ID, ®); 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, ®); - 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, ®); + 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, ®); + 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, ®); + cs40l26->fw_id, ®); 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, ®); 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, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); 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, ®); + 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, ®); + 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, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); 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, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); 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, ®); @@ -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, ®); 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; |