diff options
author | Tai Kuo <taikuo@google.com> | 2022-05-04 16:44:56 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2022-05-31 10:10:18 +0000 |
commit | d89b037202ab1cdfa0396f33863e2e5efb86eed1 (patch) | |
tree | ddf18f2ff5114bc18012522d99d1e9ce7de0f81f | |
parent | 6283d49db6285c0fd63e6703dc999c5236319248 (diff) | |
download | amplifiers-d89b037202ab1cdfa0396f33863e2e5efb86eed1.tar.gz |
cs40l26: merge dsp v3.1.8 and cs40l26 v5.1.1
Branch: v5.10-cirrus-dsp-fw
Tag: cl-dsp-fw-v3.1.8_5.10
Files:
drivers/firmware/cirrus/cl_dsp.c
include/linux/firmware/cirrus/cl_dsp.h (No changes)
Features:
- Add error print statement in case of the firmware controls linked
list being empty.
Commits:
3bbeced firmware: cirrus: Add print for empty list error condition
...
Branch: v5.10-cs40l26
Tag: cs40l26-v5.1.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 (No changes)
Features:
- Support for reverse frequency sweep in F0 and Q-factor calibration
- Add ability to load calibration firmware at boot time
- Improved firmware loading procedure, code readability
- Support for PWLE zero-crossing
- Support for Dynamic Boost Control via devicetree defaults
Bug fixes:
- f0_and_q_cal_time_ms can be read directly from
"TONE_DURATION_MS" if it is non-zero. In the case
where this was zero, the calculation for the time was incorrect.
- Avoid race condition in case where an effect is stopped prior to
the DSP registering the start of playback and vibe_state reporting
is enabled.
Major API changes:
- Devicetree controls for default DBC parameters "cirrus,dbc-*" added.
- The sysfs control "f0_and_q_cal_time_ms" now has support for negative
(i.e. reverse frequency sweep) values. This is dependent on the "FREQ_SPAN"
firmware control which can only be set via the calibration tuning file.
- Devicetree boolean "cirrus,pwle-zero-cross-en" added. Enables to PWLE
zero-cross feature.
- Devicetree boolean "cirrus,calib-fw" added; loads calibration instead
of runtime firmware at boot.
- Devicetree boolean "cirrus,vibe-state" removed. The driver will automatically
detect whether or not to enable vibe_state tracking.
Commits:
24187f6 input: cs40l26: Support negative FREQ_SPAN values
3bc43e3 Documentation: cs40l26: Remove cirrus,vibe-state devicetree entry
bce94c1 input: cs40l26: Remove cirrus,vibe-state devicetree entry
2040c5c Documentation: cs40l26: Load calibration firmware at boot
c88c071 input: cs40l26: Load calibration firmware at boot
16cb878 input: cs40l26: Refactor firmware loading procedure
f63db44 Documentation: cs40l26: Add PWLE zero crossing control
870ac63 input: cs40l26: Add PWLE zero crossing control
da51a2d input: cs40l26: Add support for SVC metadata in OWT
61e85dc Documentation: cs40l26: Default Dynamic Boost Control Settings
059a235 input: cs40l26: Default Dynamic Boost Control Settings
39324ea input: cs40l26: Update reporting of calibration time
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: Ib2f99f67469f67be0ce33ac031e553a409a569e4
-rw-r--r-- | cs40l26/cl_dsp.c | 4 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 219 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 594 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 57 |
4 files changed, 540 insertions, 334 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c index 1eda2d5..bcbee33 100644 --- a/cs40l26/cl_dsp.c +++ b/cs40l26/cl_dsp.c @@ -132,8 +132,10 @@ int cl_dsp_get_reg(struct cl_dsp *dsp, const char *coeff_name, if (!dsp) return -EPERM; - if (list_empty(&dsp->coeff_desc_head)) + if (list_empty(&dsp->coeff_desc_head)) { + dev_err(dsp->dev, "Coefficient list is empty\n"); return -ENOENT; + } list_for_each_entry(coeff_desc, &dsp->coeff_desc_head, list) { if (strncmp(coeff_desc->name, coeff_name, diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c index 1e3c086..2cc99b6 100644 --- a/cs40l26/cs40l26-sysfs.c +++ b/cs40l26/cs40l26-sysfs.c @@ -47,7 +47,7 @@ static ssize_t halo_heartbeat_show(struct device *dev, int ret; ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_HEARTBEAT", - CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); if (ret) return ret; @@ -181,8 +181,8 @@ static ssize_t vibe_state_show(struct device *dev, struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); unsigned int state; - if (!cs40l26->pdata.vibe_state_reporting) { - dev_err(cs40l26->dev, "cirrus,vibe-state not in DT\n"); + if (!cs40l26->vibe_state_reporting) { + dev_err(cs40l26->dev, "vibe_state not supported\n"); return -EPERM; } @@ -556,7 +556,7 @@ static ssize_t f0_comp_enable_show(struct device *dev, mutex_lock(&cs40l26->lock); - if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { ret = -EPERM; goto err_mutex; } @@ -601,7 +601,7 @@ static ssize_t f0_comp_enable_store(struct device *dev, value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) | (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT); - if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { ret = -EPERM; } else { ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE", @@ -636,7 +636,7 @@ static ssize_t redc_comp_enable_show(struct device *dev, mutex_lock(&cs40l26->lock); - if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { ret = -EPERM; goto err_mutex; } @@ -680,7 +680,7 @@ static ssize_t redc_comp_enable_store(struct device *dev, value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) | (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT); - if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { ret = -EPERM; } else { ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE", @@ -715,9 +715,9 @@ static ssize_t swap_firmware_show(struct device *dev, mutex_lock(&cs40l26->lock); - if (cs40l26->fw.id == CS40L26_FW_ID) + if (cs40l26->fw_id == CS40L26_FW_ID) ret = snprintf(buf, PAGE_SIZE, "%d\n", 0); - else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) + else if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) ret = snprintf(buf, PAGE_SIZE, "%d\n", 1); else ret = -EINVAL; @@ -819,12 +819,14 @@ static ssize_t dbc_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); - unsigned int reg; int ret; u32 val; ret = kstrtou32(buf, 10, &val); - if (ret || (val != 0 && val != 1)) + if (ret) + return ret; + + if (val > 1) return -EINVAL; ret = pm_runtime_get_sync(cs40l26->dev); @@ -835,17 +837,8 @@ static ssize_t dbc_enable_store(struct device *dev, mutex_lock(&cs40l26->lock); - ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS", CL_DSP_XM_UNPACKED_TYPE, - CS40L26_EXT_ALGO_ID, ®); - if (ret) - goto err_pm; - - ret = regmap_update_bits(cs40l26->regmap, reg, CS40L26_DBC_ENABLE_MASK, - val << CS40L26_DBC_ENABLE_SHIFT); - if (ret) - dev_err(cs40l26->dev, "Failed to update FLAGS\n"); + ret = cs40l26_dbc_enable(cs40l26, val); -err_pm: mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(cs40l26->dev); @@ -871,12 +864,30 @@ static ssize_t dbc_env_rel_coef_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + struct device *cdev = cs40l26->dev; + u32 val; int ret; - ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_ENV_REL_COEF, buf); + ret = kstrtou32(buf, 10, &val); + if (ret) + return ret; - return ret ? ret : count; + ret = pm_runtime_get_sync(cdev); + if (ret < 0) { + cs40l26_resume_error_handle(cdev); + return ret; + } + + mutex_lock(&cs40l26->lock); + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_ENV_REL_COEF, val); + + mutex_unlock(&cs40l26->lock); + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); + + return ret ? ret : count; } static DEVICE_ATTR_RW(dbc_env_rel_coef); @@ -896,9 +907,28 @@ static ssize_t dbc_rise_headroom_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + struct device *cdev = cs40l26->dev; + u32 val; int ret; - ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_RISE_HEADROOM, buf); + ret = kstrtou32(buf, 10, &val); + if (ret) + return ret; + + ret = pm_runtime_get_sync(cdev); + if (ret < 0) { + cs40l26_resume_error_handle(cdev); + return ret; + } + + mutex_lock(&cs40l26->lock); + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_RISE_HEADROOM, val); + + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); return ret ? ret : count; } @@ -920,9 +950,28 @@ static ssize_t dbc_fall_headroom_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + struct device *cdev = cs40l26->dev; + u32 val; int ret; - ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_FALL_HEADROOM, buf); + ret = kstrtou32(buf, 10, &val); + if (ret) + return ret; + + ret = pm_runtime_get_sync(cdev); + if (ret < 0) { + cs40l26_resume_error_handle(cdev); + return ret; + } + + mutex_lock(&cs40l26->lock); + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_FALL_HEADROOM, val); + + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); return ret ? ret : count; } @@ -944,9 +993,28 @@ static ssize_t dbc_tx_lvl_thresh_fs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + struct device *cdev = cs40l26->dev; + u32 val; int ret; - ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, buf); + ret = kstrtou32(buf, 10, &val); + if (ret) + return ret; + + ret = pm_runtime_get_sync(cdev); + if (ret < 0) { + cs40l26_resume_error_handle(cdev); + return ret; + } + + mutex_lock(&cs40l26->lock); + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, val); + + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); return ret ? ret : count; } @@ -968,9 +1036,28 @@ static ssize_t dbc_tx_lvl_hold_off_ms_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + struct device *cdev = cs40l26->dev; + u32 val; int ret; - ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, buf); + ret = kstrtou32(buf, 10, &val); + if (ret) + return ret; + + ret = pm_runtime_get_sync(cdev); + if (ret < 0) { + cs40l26_resume_error_handle(cdev); + return ret; + } + + mutex_lock(&cs40l26->lock); + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, val); + + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); return ret ? ret : count; } @@ -1473,8 +1560,8 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); - int ret; - u32 reg, freq_span, freq_centre, f0_and_q_cal_time_ms; + u32 reg, tone_dur_ms, freq_span_raw, freq_centre; + int ret, freq_span, f0_and_q_cal_time_ms; ret = pm_runtime_get_sync(cs40l26->dev); if (ret < 0) { @@ -1484,28 +1571,58 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev, mutex_lock(&cs40l26->lock); - ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_SPAN", - CL_DSP_XM_UNPACKED_TYPE, - CS40L26_F0_EST_ALGO_ID, ®); + ret = cl_dsp_get_reg(cs40l26->dsp, "TONE_DURATION_MS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_F0_EST_ALGO_ID, ®); if (ret) goto err_mutex; - ret = regmap_read(cs40l26->regmap, reg, &freq_span); - if (ret) + ret = regmap_read(cs40l26->regmap, reg, &tone_dur_ms); + if (ret) { + dev_err(cs40l26->dev, "Failed to get tone duration\n"); goto err_mutex; + } - ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_CENTRE", - CL_DSP_XM_UNPACKED_TYPE, - CS40L26_F0_EST_ALGO_ID, ®); - if (ret) - goto err_mutex; + if (tone_dur_ms == 0) { /* Calculate value */ + ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_SPAN", + CL_DSP_XM_UNPACKED_TYPE, + CS40L26_F0_EST_ALGO_ID, ®); + if (ret) + goto err_mutex; + + ret = regmap_read(cs40l26->regmap, reg, &freq_span_raw); + if (ret) { + dev_err(cs40l26->dev, "Failed to get FREQ_SPAN\n"); + goto err_mutex; + } + + if (freq_span_raw & CS40L26_F0_FREQ_SPAN_SIGN) /* Negative */ + freq_span = (int) (0xFF000000 | freq_span_raw); + else + freq_span = (int) freq_span_raw; + + ret = cl_dsp_get_reg(cs40l26->dsp, "FREQ_CENTRE", + CL_DSP_XM_UNPACKED_TYPE, + CS40L26_F0_EST_ALGO_ID, ®); + if (ret) + goto err_mutex; - ret = regmap_read(cs40l26->regmap, reg, &freq_centre); + ret = regmap_read(cs40l26->regmap, reg, &freq_centre); + if (ret) { + dev_err(cs40l26->dev, "Failed to get FREQ_CENTRE\n"); + goto err_mutex; + } - f0_and_q_cal_time_ms = ((CS40L26_F0_CHIRP_DURATION_FACTOR * - (freq_span >> CS40L26_F0_EST_FREQ_SHIFT)) / - (freq_centre >> CS40L26_F0_EST_FREQ_SHIFT)) + - CS40L26_F0_AND_Q_CALIBRATION_BUFFER_MS; + f0_and_q_cal_time_ms = + ((CS40L26_F0_CHIRP_DURATION_FACTOR * + (int) (freq_span / CS40L26_F0_EST_FREQ_SCALE)) / + (int) (freq_centre / CS40L26_F0_EST_FREQ_SCALE)); + } else if (tone_dur_ms < CS40L26_F0_AND_Q_CALIBRATION_MIN_MS) { + f0_and_q_cal_time_ms = CS40L26_F0_AND_Q_CALIBRATION_MIN_MS; + } else if (tone_dur_ms > CS40L26_F0_AND_Q_CALIBRATION_MAX_MS) { + f0_and_q_cal_time_ms = CS40L26_F0_AND_Q_CALIBRATION_MAX_MS; + } else { + f0_and_q_cal_time_ms = tone_dur_ms; + } err_mutex: mutex_unlock(&cs40l26->lock); @@ -1537,7 +1654,7 @@ static ssize_t redc_cal_time_ms_show(struct device *dev, ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_PLAYTIME_MS", CL_DSP_XM_UNPACKED_TYPE, - cs40l26->fw.id, ®); + cs40l26->fw_id, ®); if (ret) goto err_mutex; @@ -1634,7 +1751,7 @@ static ssize_t logging_en_store(struct device *dev, if (ret) goto exit_mutex; - if (cs40l26->fw.id == CS40L26_FW_ID) { + if (cs40l26->fw_id == CS40L26_FW_ID) { if (src_count != CS40L26_LOGGER_SRC_COUNT) { dev_err(cdev, "Unexpected source count %u\n", src_count); @@ -1656,7 +1773,7 @@ static ssize_t logging_en_store(struct device *dev, ret = -EINVAL; goto exit_mutex; } - } else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) { + } else if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { if (src_count != CS40L26_LOGGER_SRC_COUNT_CALIB) { dev_err(cdev, "Unexpected source count %u\n", src_count); @@ -1684,7 +1801,7 @@ static ssize_t logging_en_store(struct device *dev, } } else { dev_err(cdev, "Invalid firmware ID 0x%06X\n", - cs40l26->fw.id); + cs40l26->fw_id); goto exit_mutex; } @@ -1747,7 +1864,7 @@ static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr, u32 reg, max_bemf; int ret; - if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id != CS40L26_FW_CALIB_ID) { dev_err(cs40l26->dev, "Calib. FW required for BEMF logging\n"); return -EPERM; } @@ -1785,7 +1902,7 @@ static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr, u32 reg, max_vbst; int ret; - if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id != CS40L26_FW_CALIB_ID) { dev_err(cs40l26->dev, "Calib. FW required for VBST logging\n"); return -EPERM; } @@ -1824,7 +1941,7 @@ static ssize_t max_vmon_show(struct device *dev, struct device_attribute *attr, u8 offset; int ret; - if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) + if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) offset = CS40L26_LOGGER_DATA_3_MAX_OFFSET; else offset = CS40L26_LOGGER_DATA_1_MAX_OFFSET; diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index c123910..3be1832 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -211,10 +211,10 @@ err_pm: EXPORT_SYMBOL(cs40l26_dbc_get); int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, - const char *buf) + u32 val) { struct device *dev = cs40l26->dev; - unsigned int val, reg, max; + unsigned int reg, max; int ret; if (dbc == CS40L26_DBC_TX_LVL_HOLD_OFF_MS) @@ -222,40 +222,20 @@ int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, else max = CS40L26_DBC_CONTROLS_MAX; - ret = kstrtou32(buf, 10, &val); - if (ret) { - dev_err(dev, "Failed to kstrstou32()\n"); - return ret; - } - if (val > max) { dev_err(dev, "DBC input %u out of bounds\n", val); return -EINVAL; } - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev); - return ret; - } - - mutex_lock(&cs40l26->lock); - ret = cl_dsp_get_reg(cs40l26->dsp, cs40l26_dbc_names[dbc], CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); if (ret) - goto err_pm; + return ret; ret = regmap_write(cs40l26->regmap, reg, val); if (ret) dev_err(dev, "Failed to write Dynamic Boost Control value\n"); -err_pm: - mutex_unlock(&cs40l26->lock); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - return ret; } EXPORT_SYMBOL(cs40l26_dbc_set); @@ -718,8 +698,8 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) complete(&cs40l26->i2s_cont); break; case CS40L26_DSP_MBOX_TRIGGER_CP: - if (!cs40l26->pdata.vibe_state_reporting) { - dev_err(dev, "cirrus,vibe-state not in DT\n"); + if (!cs40l26->vibe_state_reporting) { + dev_err(dev, "vibe_state not supported\n"); return -EPERM; } @@ -971,7 +951,7 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, CS40L26_WKSRC_STS_SHIFT); ret = cl_dsp_get_reg(cs40l26->dsp, "LAST_WAKESRC_CTL", - CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); if (ret) goto err; @@ -1892,7 +1872,7 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) goto err_mutex; } - if (!cs40l26->pdata.vibe_state_reporting) + if (!cs40l26->vibe_state_reporting) cs40l26_vibe_state_update(cs40l26, CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK); err_mutex: @@ -1914,7 +1894,8 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) mutex_lock(&cs40l26->lock); - if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) + if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC && + !cs40l26->vibe_state_reporting) goto mutex_exit; /* wait for SVC init phase to complete */ @@ -2336,7 +2317,7 @@ static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26, } static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, - u32 in_data_nibbles, bool pwle, u8 **out_data) + u32 in_data_nibbles, bool pwle, bool svc_waveform, u8 **out_data) { u8 nsections, global_rep, out_nsections = 0; int ret = 0, pos_byte = 0, in_pos_nib = 2; @@ -2363,10 +2344,14 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, out_ch = cl_dsp_memchunk_create((void *) *out_data, out_data_bytes); cl_dsp_memchunk_write(&out_ch, 16, - CS40L26_WT_HEADER_DEFAULT_FLAGS); + CS40L26_WT_HEADER_DEFAULT_FLAGS | + (svc_waveform ? CS40L26_OWT_SVC_METADATA : 0)); cl_dsp_memchunk_write(&out_ch, 8, WT_TYPE_V6_PWLE); - cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET); - cl_dsp_memchunk_write(&out_ch, 24, in_data_bytes / 4); + cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET + + (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0)); + cl_dsp_memchunk_write(&out_ch, 24, (in_data_bytes / 4) - + (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0)); + memcpy(*out_data + out_ch.bytes, in_data, in_data_bytes); @@ -2615,7 +2600,7 @@ static void cs40l26_upload_worker(struct work_struct *work) u32 trigger_index, min_index, max_index, nwaves; struct ff_effect *effect; u16 index, bank; - bool pwle; + bool pwle, svc_waveform; if (pm_runtime_get_sync(cdev) < 0) return cs40l26_resume_error_handle(cdev); @@ -2632,13 +2617,16 @@ static void cs40l26_upload_worker(struct work_struct *work) switch (effect->u.periodic.waveform) { case FF_CUSTOM: - pwle = (cs40l26->raw_custom_data[0] == 0x0000) ? false : true; + pwle = (cs40l26->raw_custom_data[0] == + CS40L26_WT_TYPE10_COMP_BUFFER) ? false : true; + svc_waveform = (cs40l26->raw_custom_data[0] == + CS40L26_SVC_ID) ? true : false; len = effect->u.periodic.custom_len; if (len > CS40L26_CUSTOM_DATA_SIZE) { refactored_size = cs40l26_refactor_owt(cs40l26, - cs40l26->raw_custom_data, len, pwle, + cs40l26->raw_custom_data, len, pwle, svc_waveform, &refactored_data); if (refactored_size <= 0) { @@ -3080,83 +3068,6 @@ static int cs40l26_part_num_resolve(struct cs40l26_private *cs40l26) return 0; } -static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id) -{ - int ret = 0, i; - - if (cs40l26->dsp) { - ret = cl_dsp_destroy(cs40l26->dsp); - if (ret) { - dev_err(cs40l26->dev, - "Failed to destroy existing DSP structure\n"); - return ret; - } - cs40l26->dsp = NULL; - } - - cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap); - if (!cs40l26->dsp) { - dev_err(cs40l26->dev, "Failed to allocate space for DSP\n"); - return -ENOMEM; - } - - cs40l26->fw.id = id; - - if (id == CS40L26_FW_ID) { - cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV; - cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_RT; - } else if (id == CS40L26_FW_CALIB_ID) { - cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV; - cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_CAL; - } else { - dev_err(cs40l26->dev, "Invalid firmware ID 0x%06X\n", - id); - return -EINVAL; - } - - if (!cs40l26->fw.coeff_files) - cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev, - cs40l26->fw.num_coeff_files, sizeof(char *), - GFP_KERNEL); - - for (i = 0; i < cs40l26->fw.num_coeff_files; i++) { - if (!cs40l26->fw.coeff_files[i]) { - cs40l26->fw.coeff_files[i] = - devm_kzalloc(cs40l26->dev, - CS40L26_TUNING_FILE_NAME_MAX_LEN, - GFP_KERNEL); - } else { - memset(cs40l26->fw.coeff_files[i], 0, - CS40L26_TUNING_FILE_NAME_MAX_LEN); - } - } - - strscpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME, - CS40L26_WT_FILE_NAME_LEN); - - if (id == CS40L26_FW_ID) { - strscpy(cs40l26->fw.coeff_files[1], - CS40L26_A2H_TUNING_FILE_NAME, - CS40L26_A2H_TUNING_FILE_NAME_LEN); - strscpy(cs40l26->fw.coeff_files[2], - CS40L26_SVC_TUNING_FILE_NAME, - CS40L26_SVC_TUNING_FILE_NAME_LEN); - strscpy(cs40l26->fw.coeff_files[3], - CS40L26_DVL_FILE_NAME, - CS40L26_DVL_FILE_NAME_LEN); - } else { - strscpy(cs40l26->fw.coeff_files[1], - CS40L26_CALIB_BIN_FILE_NAME, - CS40L26_CALIB_BIN_FILE_NAME_LEN); - } - - ret = cl_dsp_wavetable_create(cs40l26->dsp, - CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM, - CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); - - return ret; -} - static int cs40l26_wksrc_config(struct cs40l26_private *cs40l26) { u8 mask_wksrc; @@ -3457,43 +3368,6 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) pseq_val, pseq_mask); } -static int cs40l26_verify_fw(struct cs40l26_private *cs40l26) -{ - struct cs40l26_fw *fw = &cs40l26->fw; - unsigned int val; - int ret; - - ret = cl_dsp_fw_id_get(cs40l26->dsp, &val); - if (ret) - return ret; - - if (val != cs40l26->fw.id) { - dev_err(cs40l26->dev, "Invalid firmware ID: 0x%X\n", val); - return -EINVAL; - } - - ret = cl_dsp_fw_rev_get(cs40l26->dsp, &val); - if (ret) - return ret; - - if ((val & ~CS40L26_FW_BRANCH_MASK) < fw->min_rev) { - dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n", - (int) CL_DSP_GET_MAJOR(val), - (int) CL_DSP_GET_MINOR(val), - (int) CL_DSP_GET_PATCH(val)); - return -EINVAL; - } - - dev_info(cs40l26->dev, "Firmware revision %d.%d.%d\n", - (int) CL_DSP_GET_MAJOR(val), - (int) CL_DSP_GET_MINOR(val), - (int) CL_DSP_GET_PATCH(val)); - - cs40l26->fw.rev = val; - - return 0; -} - static int cs40l26_asp_config(struct cs40l26_private *cs40l26) { struct reg_sequence *dsp1rx_config = @@ -3553,6 +3427,26 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26) return ret; } +static int cs40l26_zero_cross_config(struct cs40l26_private *cs40l26) +{ + int ret = 0; + u32 reg; + + if (cs40l26->pdata.pwle_zero_cross) { + ret = cl_dsp_get_reg(cs40l26->dsp, "PWLE_EXTEND_ZERO_CROSS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); + if (ret) + return ret; + + ret = regmap_write(cs40l26->regmap, reg, 1); + if (ret) + dev_err(cs40l26->dev, "Failed to set PWLE_EXTEND_ZERO_CROSS\n"); + + } + + return ret; +} + static int calib_device_tree_config(struct cs40l26_private *cs40l26) { int ret = 0; @@ -3797,6 +3691,51 @@ static int cs40l26_handle_errata(struct cs40l26_private *cs40l26) false, CS40L26_PSEQ_OP_WRITE_FULL); } +int cs40l26_dbc_enable(struct cs40l26_private *cs40l26, u32 enable) +{ + unsigned int reg; + int ret; + + ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS", CL_DSP_XM_UNPACKED_TYPE, + CS40L26_EXT_ALGO_ID, ®); + if (ret) + return ret; + + ret = regmap_update_bits(cs40l26->regmap, reg, CS40L26_DBC_ENABLE_MASK, + enable << CS40L26_DBC_ENABLE_SHIFT); + if (ret) + dev_err(cs40l26->dev, "Failed to %s DBC\n", + (enable == 1) ? "enable" : "disable"); + + return ret; +} +EXPORT_SYMBOL(cs40l26_dbc_enable); + +static int cs40l26_handle_dbc_defaults(struct cs40l26_private *cs40l26) +{ + unsigned int i; + u32 val; + int ret; + + for (i = 0; i < CS40L26_DBC_NUM_CONTROLS; i++) { + val = cs40l26->pdata.dbc_defaults[i]; + + if (val != CS40L26_DBC_USE_DEFAULT) { + ret = cs40l26_dbc_set(cs40l26, i, val); + if (ret) + return ret; + } + } + + if (cs40l26->pdata.dbc_enable_default) { + ret = cs40l26_dbc_enable(cs40l26, 1); + if (ret) + return ret; + } + + return 0; +} + static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) { struct regmap *regmap = cs40l26->regmap; @@ -3805,10 +3744,6 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) u32 reg, nwaves, value; int ret; - ret = cs40l26_verify_fw(cs40l26); - if (ret) - return ret; - ret = regmap_update_bits(regmap, CS40L26_PWRMGT_CTL, CS40L26_MEM_RDY_MASK, 1 << CS40L26_MEM_RDY_SHIFT); if (ret) { @@ -3817,7 +3752,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) } ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT", - CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); if (ret) return ret; @@ -3835,7 +3770,6 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; - ret = cs40l26_handle_errata(cs40l26); if (ret) return ret; @@ -3851,7 +3785,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) /* ensure firmware running */ ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE", - CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw_id, ®); if (ret) return ret; @@ -3891,6 +3825,14 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; + ret = cs40l26_handle_dbc_defaults(cs40l26); + if (ret) + return ret; + + ret = cs40l26_zero_cross_config(cs40l26); + if (ret) + return ret; + if (!cs40l26->vibe_init_success) { ret = calib_device_tree_config(cs40l26); if (ret) @@ -3943,7 +3885,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) | (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT); - if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) { + if (cs40l26->fw_id != CS40L26_FW_CALIB_ID) { ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -3984,12 +3926,10 @@ 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) +static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, + u32 *tuning_num) { unsigned int reg, le = 0; - char svc_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN]; - char wt_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN]; - char n_str[2]; int ret, i, j; ret = pm_runtime_get_sync(cs40l26->dev); @@ -4021,18 +3961,7 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) for (j = 0; j < cs40l26->num_svc_le_vals; j++) { if (le >= cs40l26->svc_le_vals[j]->min && le <= cs40l26->svc_le_vals[j]->max) { - strscpy(svc_bin_file, - CS40L26_SVC_TUNING_FILE_PREFIX, - CS40L26_SVC_TUNING_FILE_PREFIX_LEN); - - strscpy(wt_bin_file, CS40L26_WT_FILE_PREFIX, - CS40L26_WT_FILE_PREFIX_LEN); - - snprintf(n_str, 2, "%d", - cs40l26->svc_le_vals[j]->n); - - strncat(svc_bin_file, n_str, 2); - strncat(wt_bin_file, n_str, 2); + *tuning_num = cs40l26->svc_le_vals[j]->n; cs40l26_gain_adjust(cs40l26, cs40l26->svc_le_vals[j]->gain_adjust); @@ -4044,19 +3973,8 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) break; } - if (i == CS40L26_SVC_LE_MAX_ATTEMPTS) { + if (i == CS40L26_SVC_LE_MAX_ATTEMPTS) dev_warn(cs40l26->dev, "Using default tunings\n"); - } else { - strncat(svc_bin_file, CS40L26_TUNING_FILE_SUFFIX, - CS40L26_TUNING_FILE_SUFFIX_LEN); - strncat(wt_bin_file, CS40L26_TUNING_FILE_SUFFIX, - CS40L26_TUNING_FILE_SUFFIX_LEN); - - strscpy(cs40l26->fw.coeff_files[0], wt_bin_file, - CS40L26_WT_FILE_CONCAT_NAME_LEN); - strscpy(cs40l26->fw.coeff_files[2], svc_bin_file, - CS40L26_SVC_TUNING_FILE_NAME_LEN); - } pm_err: pm_runtime_mark_last_busy(cs40l26->dev); @@ -4065,29 +3983,103 @@ pm_err: return ret; } -static void cs40l26_coeff_load(struct cs40l26_private *cs40l26) +static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, + u32 tuning) +{ + char svc_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN]; + char wt_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN]; + char **coeff_files, tuning_str[2]; + int i; + + coeff_files = kcalloc(n, sizeof(char *), GFP_KERNEL); + if (!coeff_files) + return NULL; + + for (i = 0; i < n; i++) { + coeff_files[i] = + kzalloc(CS40L26_TUNING_FILE_NAME_MAX_LEN, GFP_KERNEL); + if (!coeff_files[i]) + goto err_free; + } + + strscpy(svc_tuning, CS40L26_SVC_TUNING_FILE_PREFIX, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + + if (tuning) { /* Concatenate tuning number if required */ + strscpy(wt_tuning, CS40L26_WT_FILE_PREFIX, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + + snprintf(tuning_str, 2, "%d", tuning); + + strncat(wt_tuning, tuning_str, 2); + strncat(svc_tuning, tuning_str, 2); + + strncat(wt_tuning, CS40L26_TUNING_FILE_SUFFIX, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + } else { + strscpy(wt_tuning, CS40L26_WT_FILE_NAME, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + } + + strncat(svc_tuning, CS40L26_TUNING_FILE_SUFFIX, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + + strscpy(coeff_files[0], wt_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN); + + if (cs40l26->fw_id == CS40L26_FW_ID) { + strscpy(coeff_files[1], CS40L26_A2H_TUNING_FILE_NAME, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + strscpy(coeff_files[2], svc_tuning, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + strscpy(coeff_files[3], CS40L26_DVL_FILE_NAME, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + } else { + strscpy(coeff_files[1], CS40L26_CALIB_BIN_FILE_NAME, + CS40L26_TUNING_FILE_NAME_MAX_LEN); + } + + return coeff_files; + +err_free: + kfree(coeff_files); + return NULL; +} + +static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning) { struct device *dev = cs40l26->dev; const struct firmware *coeff; - int i, ret; + int i, ret, num_files; + char **coeff_files; - for (i = 0; i < cs40l26->fw.num_coeff_files; i++) { - ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev); + num_files = (cs40l26->fw_id == CS40L26_FW_ID) ? + CS40L26_TUNING_FILES_RUNTIME : CS40L26_TUNING_FILES_CAL; + + coeff_files = cs40l26_get_tuning_names(cs40l26, num_files, tuning); + if (!coeff_files) + return -ENOMEM; + + for (i = 0; i < num_files; i++) { + ret = request_firmware(&coeff, coeff_files[i], dev); if (ret) { - dev_warn(dev, "Continuing..."); + dev_warn(dev, "Continuing...\n"); continue; } ret = cl_dsp_coeff_file_parse(cs40l26->dsp, coeff); if (ret) - dev_warn(dev, "Failed to load, %s, %d. Continuing...", - cs40l26->fw.coeff_files[i], ret); + dev_warn(dev, "Failed to load %s, %d. Continuing...\n", + coeff_files[i], ret); else dev_info(dev, "%s Loaded Successfully\n", - cs40l26->fw.coeff_files[i]); + coeff_files[i]); release_firmware(coeff); } + + kfree(coeff_files); + + return 0; } static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26) @@ -4103,64 +4095,135 @@ static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26) cs40l26->pdata.pm_active_timeout_ms); } -static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id) +static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26) { - struct device *dev = cs40l26->dev; - const struct firmware *fw; - int ret; - - if (cs40l26->fw.id == CS40L26_FW_ID) - ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev); - else - ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev); + int ret, maj, min, patch; + u32 id, min_rev, rev; + ret = cl_dsp_fw_id_get(cs40l26->dsp, &id); if (ret) return ret; - ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true); - release_firmware(fw); + switch (id) { + case CS40L26_FW_ID: + min_rev = CS40L26_FW_A1_RAM_MIN_REV; + break; + case CS40L26_FW_CALIB_ID: + min_rev = CS40L26_FW_CALIB_MIN_REV; + break; + default: + dev_err(cs40l26->dev, "Invalid FW ID: 0x%06X\n", id); + } - if (ret) { - dev_err(dev, "cl_dsp_firmware_parse failed, %d", ret); + ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev); + if (ret) 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) { + 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); + + return 0; +} + +static int cs40l26_cl_dsp_reinit(struct cs40l26_private *cs40l26) +{ + int ret; + + if (cs40l26->dsp) { + ret = cl_dsp_destroy(cs40l26->dsp); + if (ret) { + dev_err(cs40l26->dev, "Failed to destroy DSP struct\n"); + return ret; + } + + cs40l26->dsp = NULL; } - return cs40l26_change_fw_control_defaults(cs40l26); + cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap); + if (!cs40l26->dsp) + return -ENOMEM; + + return cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID, + CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); } -static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id) +static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) { + bool svc_le_required = cs40l26->num_svc_le_vals && !cs40l26->calib_fw; + struct device *dev = cs40l26->dev; + u32 tuning_num = 0; + const struct firmware *fw; int ret; - ret = cs40l26_cl_dsp_init(cs40l26, id); +start: + cs40l26->fw_loaded = false; + + ret = cs40l26_cl_dsp_reinit(cs40l26); if (ret) return ret; + if (svc_le_required || cs40l26->calib_fw) + ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev); + else + ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev); + + if (ret) { + release_firmware(fw); + return ret; + } + ret = cs40l26_dsp_pre_config(cs40l26); if (ret) return ret; - ret = cs40l26_firmware_load(cs40l26, id); + ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true); + release_firmware(fw); if (ret) return ret; - if (cs40l26->num_svc_le_vals && id != CS40L26_FW_CALIB_ID) { + ret = cs40l26_change_fw_control_defaults(cs40l26); + if (ret) + return ret; + + ret = cs40l26_get_fw_params(cs40l26); + if (ret) + return ret; + + if (svc_le_required) { + svc_le_required = false; + ret = cs40l26_dsp_config(cs40l26); if (ret) return ret; - ret = cs40l26_tuning_select_from_svc_le(cs40l26); + ret = cs40l26_tuning_select_from_svc_le(cs40l26, &tuning_num); if (ret) return ret; cs40l26_pm_runtime_teardown(cs40l26); - - ret = cs40l26_dsp_pre_config(cs40l26); - if (ret) - return ret; + goto start; } - cs40l26_coeff_load(cs40l26); + ret = cs40l26_coeff_load(cs40l26, tuning_num); + if (ret) + return ret; return cs40l26_dsp_config(cs40l26); } @@ -4168,41 +4231,22 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id) int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) { struct device *dev = cs40l26->dev; - bool register_irq = false; + bool deferred = cs40l26->fw_defer; u32 pseq_rom_end_of_script_loc; int ret; - if (cs40l26->fw_loaded) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev); - return ret; - } - - ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, - CS40L26_PM_TIMEOUT_MS_MAX); - if (ret) { - dev_err(cs40l26->dev, "Can't set pm_timeout %d\n", ret); - return ret; - } - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - } - if (cs40l26->fw_defer) { cs40l26->fw_defer = false; - register_irq = true; } else { disable_irq(cs40l26->irq); cs40l26_pm_runtime_teardown(cs40l26); } - cs40l26->fw_loaded = false; - if (cs40l26->revid != CS40L26_REVID_A1 && cs40l26->revid != CS40L26_REVID_B0) { dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid); - return -EINVAL; + ret = -EINVAL; + goto defer_err; } /* reset pseq END_OF_SCRIPT to location from ROM */ @@ -4212,27 +4256,38 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT); if (ret) { dev_err(dev, "Failed to reset pseq END_OF_SCRIPT %d\n", ret); - return ret; + goto defer_err; } - ret = cs40l26_fw_upload(cs40l26, id); + if (id == CS40L26_FW_CALIB_ID) + cs40l26->calib_fw = true; + else + cs40l26->calib_fw = false; + + ret = cs40l26_fw_upload(cs40l26); if (ret) - return ret; + goto defer_err; - if (register_irq) { + if (deferred) { ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, "cs40l26", cs40l26); if (ret) { dev_err(dev, "Failed to request threaded IRQ: %d\n", ret); - return ret; + goto defer_err; } } else { enable_irq(cs40l26->irq); } return 0; + +defer_err: + if (deferred) + cs40l26->fw_defer = true; + + return ret; } EXPORT_SYMBOL(cs40l26_fw_swap); @@ -4362,10 +4417,8 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) if (of_property_read_bool(np, "cirrus,fw-defer")) cs40l26->fw_defer = true; - if (of_property_read_bool(np, "cirrus,vibe-state")) - cs40l26->pdata.vibe_state_reporting = true; - else - cs40l26->pdata.vibe_state_reporting = false; + if (of_property_read_bool(np, "cirrus,calib-fw")) + cs40l26->calib_fw = true; if (of_property_read_bool(np, "cirrus,bst-expl-mode-disable")) cs40l26->pdata.expl_mode_enabled = false; @@ -4480,6 +4533,47 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) if (!of_property_read_u32(np, "cirrus,q-default", &val)) cs40l26->pdata.q_default = val; + if (of_property_read_bool(np, "cirrus,dbc-enable")) + cs40l26->pdata.dbc_enable_default = true; + else + cs40l26->pdata.dbc_enable_default = false; + + if (!of_property_read_u32(np, "cirrus,dbc-env-rel-coef", &val)) + cs40l26->pdata.dbc_defaults[CS40L26_DBC_ENV_REL_COEF] = val; + else + cs40l26->pdata.dbc_defaults[CS40L26_DBC_ENV_REL_COEF] = + CS40L26_DBC_USE_DEFAULT; + + if (!of_property_read_u32(np, "cirrus,dbc-fall-headroom", &val)) + cs40l26->pdata.dbc_defaults[CS40L26_DBC_FALL_HEADROOM] = val; + else + cs40l26->pdata.dbc_defaults[CS40L26_DBC_FALL_HEADROOM] = + CS40L26_DBC_USE_DEFAULT; + + if (!of_property_read_u32(np, "cirrus,dbc-rise-headroom", &val)) + cs40l26->pdata.dbc_defaults[CS40L26_DBC_RISE_HEADROOM] = val; + else + cs40l26->pdata.dbc_defaults[CS40L26_DBC_RISE_HEADROOM] = + CS40L26_DBC_USE_DEFAULT; + + if (!of_property_read_u32(np, "cirrus,dbc-tx-lvl-hold-off-ms", &val)) + cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_HOLD_OFF_MS] = + val; + else + cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_HOLD_OFF_MS] = + CS40L26_DBC_USE_DEFAULT; + + if (!of_property_read_u32(np, "cirrus,dbc-tx-lvl-thresh-fs", &val)) + cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_THRESH_FS] = val; + else + cs40l26->pdata.dbc_defaults[CS40L26_DBC_TX_LVL_THRESH_FS] = + CS40L26_DBC_USE_DEFAULT; + + if (of_property_read_bool(np, "cirrus,pwle-zero-cross-en")) + cs40l26->pdata.pwle_zero_cross = true; + else + cs40l26->pdata.pwle_zero_cross = false; + return 0; } @@ -4558,12 +4652,11 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, } cs40l26->pm_ready = false; - cs40l26->fw_loaded = false; init_completion(&cs40l26->i2s_cont); if (!cs40l26->fw_defer) { - ret = cs40l26_fw_upload(cs40l26, CS40L26_FW_ID); + ret = cs40l26_fw_upload(cs40l26); if (ret) goto err; @@ -4574,7 +4667,6 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, dev_err(dev, "Failed to request threaded IRQ\n"); goto err; } - } ret = cs40l26_input_init(cs40l26); diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index 8a99371..23c6b53 100644 --- a/cs40l26/cs40l26.h +++ b/cs40l26/cs40l26.h @@ -830,38 +830,29 @@ #define CS40L26_FW_FILE_NAME "cs40l26.wmfw" #define CS40L26_FW_CALIB_NAME "cs40l26-calib.wmfw" -#define CS40L26_TUNING_FILES_MAX 4 -#define CS40L26_TUNING_FILES_RT 4 +#define CS40L26_TUNING_FILES_RUNTIME 4 #define CS40L26_TUNING_FILES_CAL 2 #define CS40L26_WT_FILE_NAME "cs40l26.bin" -#define CS40L26_WT_FILE_NAME_LEN 12 #define CS40L26_WT_FILE_PREFIX "cs40l26-wt" #define CS40L26_WT_FILE_PREFIX_LEN 11 -#define CS40L26_WT_FILE_CONCAT_NAME_LEN 16 #define CS40L26_SVC_TUNING_FILE_PREFIX "cs40l26-svc" #define CS40L26_SVC_TUNING_FILE_PREFIX_LEN 12 #define CS40L26_SVC_TUNING_FILE_NAME "cs40l26-svc.bin" -#define CS40L26_SVC_TUNING_FILE_NAME_LEN 17 #define CS40L26_A2H_TUNING_FILE_NAME "cs40l26-a2h.bin" -#define CS40L26_A2H_TUNING_FILE_NAME_LEN 16 #define CS40L26_TUNING_FILE_NAME_MAX_LEN 20 #define CS40L26_TUNING_FILE_SUFFIX ".bin" #define CS40L26_TUNING_FILE_SUFFIX_LEN 4 #define CS40L26_DVL_FILE_NAME "cs40l26-dvl.bin" -#define CS40L26_DVL_FILE_NAME_LEN 16 #define CS40L26_CALIB_BIN_FILE_NAME "cs40l26-calib.bin" -#define CS40L26_CALIB_BIN_FILE_NAME_LEN 18 #define CS40L26_SVC_LE_MAX_ATTEMPTS 2 #define CS40L26_SVC_DT_PREFIX "svc-le" #define CS40L26_FW_ID 0x1800D4 -#define CS40L26_FW_ROM_MIN_REV 0x040000 -#define CS40L26_FW_A0_RAM_MIN_REV 0x050004 #define CS40L26_FW_A1_RAM_MIN_REV 0x07021C #define CS40L26_FW_CALIB_ID 0x1800DA -#define CS40L26_FW_CALIB_MIN_REV 0x010000 +#define CS40L26_FW_CALIB_MIN_REV 0x010014 #define CS40L26_FW_BRANCH_MASK GENMASK(23, 21) #define CS40L26_CCM_CORE_RESET 0x00000200 @@ -1182,9 +1173,12 @@ #define CS40L26_WT_MAX_TIME_VAL 16383 /* ms */ #define CS40L26_WT_HEADER_OFFSET 3 +#define CS40L26_WT_METADATA_OFFSET 3 #define CS40L26_WT_HEADER_DEFAULT_FLAGS 0x0000 #define CS40L26_WT_HEADER_PWLE_SIZE 12 #define CS40L26_WT_HEADER_COMP_SIZE 20 +#define CS40L26_OWT_SVC_METADATA BIT(10) +#define CS40L26_SVC_ID 0x100 #define CS40L26_WT_TYPE10_SECTION_BYTES_MIN 8 #define CS40L26_WT_TYPE10_SECTION_BYTES_MAX 12 @@ -1192,6 +1186,7 @@ #define CS40L26_WT_TYPE10_WAVELEN_INDEF 0x400000 #define CS40L26_WT_TYPE10_WAVELEN_CALCULATED 0x800000 #define CS40L26_WT_TYPE10_COMP_DURATION_FLAG 0x8 +#define CS40L26_WT_TYPE10_COMP_BUFFER 0x0000 /* F0 Offset represented as Q10.14 format */ #define CS40L26_F0_OFFSET_MAX 0x190000 /* +100 Hz */ @@ -1203,14 +1198,17 @@ #define CS40L26_Q_EST_MIN 0 #define CS40L26_Q_EST_MAX 0x7FFFFF -#define CS40L26_F0_EST_FREQ_SHIFT 14 /* centre, span, and f0 in Q10.14 */ +#define CS40L26_F0_EST_FREQ_SCALE 16384 -#define CS40L26_SVC_INITIALIZATION_PERIOD_MS 6 -#define CS40L26_REDC_CALIBRATION_BUFFER_MS 10 -#define CS40L26_F0_AND_Q_CALIBRATION_BUFFER_MS 100 -#define CS40L26_F0_CHIRP_DURATION_FACTOR 3662 /* t=factor*span/center */ -#define CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q BIT(0) -#define CS40L26_CALIBRATION_CONTROL_REQUEST_REDC BIT(1) +#define CS40L26_SVC_INITIALIZATION_PERIOD_MS 6 +#define CS40L26_REDC_CALIBRATION_BUFFER_MS 10 +#define CS40L26_F0_AND_Q_CALIBRATION_MIN_MS 100 +#define CS40L26_F0_AND_Q_CALIBRATION_MAX_MS 1800 +#define CS40L26_F0_CHIRP_DURATION_FACTOR 3750 +#define CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q BIT(0) +#define CS40L26_CALIBRATION_CONTROL_REQUEST_REDC BIT(1) +#define CS40L26_F0_FREQ_SPAN_MASK GENMASK(23, 0) +#define CS40L26_F0_FREQ_SPAN_SIGN BIT(23) #define CS40L26_LOGGER_SRC_SIZE_MASK BIT(22) #define CS40L26_LOGGER_SRC_SIZE_SHIFT 22 @@ -1250,6 +1248,7 @@ #define CS40L26_DBC_FALL_HEADROOM_NAME "DBC_FALL_HEADROOM" #define CS40L26_DBC_TX_LVL_THRESH_FS_NAME "DBC_TX_LVL_THRESH_FS" #define CS40L26_DBC_TX_LVL_HOLD_OFF_MS_NAME "DBC_TX_LVL_HOLD_OFF_MS" +#define CS40L26_DBC_USE_DEFAULT 0xFFFFFFFF /* Errata */ #define CS40L26_ERRATA_A1_NUM_WRITES 4 @@ -1385,13 +1384,6 @@ enum cs40l26_pm_state { }; /* structs */ -struct cs40l26_fw { - unsigned int id; - unsigned int rev; - unsigned int min_rev; - unsigned int num_coeff_files; - char **coeff_files; -}; struct cs40l26_owt_section { u8 flags; @@ -1442,8 +1434,10 @@ struct cs40l26_platform_data { u32 redc_default; u32 q_default; u32 boost_ctl; - bool vibe_state_reporting; bool expl_mode_enabled; + bool dbc_enable_default; + u32 dbc_defaults[CS40L26_DBC_NUM_CONTROLS]; + bool pwle_zero_cross; }; struct cs40l26_owt { @@ -1480,11 +1474,12 @@ struct cs40l26_private { u32 pseq_base; struct list_head pseq_op_head; enum cs40l26_pm_state pm_state; + u32 fw_id; bool fw_defer; - enum cs40l26_vibe_state vibe_state; - int num_loaded_coeff_files; - struct cs40l26_fw fw; bool fw_loaded; + bool calib_fw; + enum cs40l26_vibe_state vibe_state; + bool vibe_state_reporting; bool pm_ready; bool asp_enable; u8 last_wksrc_pol; @@ -1531,10 +1526,11 @@ struct cs40l26_pll_sysclk_config { }; /* exported function prototypes */ +int cs40l26_dbc_enable(struct cs40l26_private *cs40l26, u32 enable); int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, unsigned int *val); int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, - const char *buf); + u32 val); int cs40l26_asp_start(struct cs40l26_private *cs40l26); int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves); int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id); @@ -1580,7 +1576,6 @@ extern const struct reg_sequence cs40l26_a1_errata[CS40L26_ERRATA_A1_NUM_WRITES]; extern const char * const cs40l26_dbc_names[CS40L26_DBC_NUM_CONTROLS]; - /* sysfs */ extern struct attribute_group cs40l26_dev_attr_group; extern struct attribute_group cs40l26_dev_attr_cal_group; |