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