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