diff options
author | Tai Kuo <taikuo@google.com> | 2022-03-04 15:05:33 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2022-03-04 09:33:10 +0000 |
commit | d58df7442c6163bd2233ab38814565f5f2d7539c (patch) | |
tree | bdda15e209f632ba97b453fb937bbab3f99ee7e4 | |
parent | 4edf770fd08f672a72131cf2fc60b36edd46e773 (diff) | |
download | amplifiers-d58df7442c6163bd2233ab38814565f5f2d7539c.tar.gz |
cs40l26: merge dsp v3.1.7, cs40l26 v4.0.2 and v4.0.3
Branch: v5.10-cirrus-dsp-fw
Tag: cl-dsp-fw-v3.1.7_5.10
Files:
drivers/firmware/cirrus/cl_dsp.c
include/linux/firmware/cirrus/cl_dsp.h
Bug fixes:
- Replace strlcpy() instances with strscpy()
- Improved error reporting for invalid algo. rev
- Minor typo fix
Commits:
0a2a7f7 firmware: cirrus: Fix typo
332111a firmware: cirrus: Improve error reporting for invalid algo. rev.
ec97039 firmware: cirrus: Use strscpy for string copying
---
Branch: v5.10-cs40l26
Tag: cs40l26-v4.0.2_5.10, cs40l26-v4.0.3_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:
- Allow swapping firmwares of the same type (runtime/calibration)
- Add support for Dynamic Boost Control via sysfs controls
- Extend range of PM timeouts
Bug fixes:
- Fixes bug in which PSEQ list terminator was not being reset between
firmware swaps.
- Appropriately clear GPI mapping after erasing waveform
Commits:
tag: cs40l26-v4.0.3_5.10
0c520ef input: cs40l26: Reset PSEQ END_OF_SCRIPT on fw swap
956d534 input: cs40l26: Check pseq mem before adding op
tag: cs40l26-v4.0.2_5.10
4a9f5e3 input: cs40l26: Update SVC tuning file name size
882895f input: cs40l26: Loosen restriction on firmware swapping
5c7f770 input: cs40l26: Use strscpy for string copying
0700a83 ASoC: cs40l26: Update Handling of SVC for ASP Streaming
cfeac58 input: cs40l26: Add user controls for Dynamic Boost
6068def input: cs40l26: Update PM Transition Timeout Limits
b65b511 input: cs40l26: Fix value to disable gpi triggers
Bug: 222620491
Test: idlcli commands.
Test: Firmware switching and then trigger haptics
Test: Continuous index 0 haptics
Test: Continuous index 9 hatpcis
Signed-off-by: Tai Kuo <taikuo@google.com>
Change-Id: Ic2eaca8fe2cce9ef0c4b03460ddca791ad57d852
-rw-r--r-- | cs40l26/cl_dsp.c | 11 | ||||
-rw-r--r-- | cs40l26/cl_dsp.h | 6 | ||||
-rw-r--r-- | cs40l26/cs40l26-codec.c | 4 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 217 | ||||
-rw-r--r-- | cs40l26/cs40l26-tables.c | 8 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 158 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 39 |
7 files changed, 406 insertions, 37 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c index e93879f..1eda2d5 100644 --- a/cs40l26/cl_dsp.c +++ b/cs40l26/cl_dsp.c @@ -286,7 +286,7 @@ static int cl_dsp_coeff_header_parse(struct cl_dsp *dsp, if (header.fw_revision != dsp->algo_info[0].rev) { dev_warn(dev, - "Coeff. rev. 0x%06X mistmatches 0x%06X, continuing..\n", + "Coeff. rev. 0x%06X mismatches 0x%06X, continuing..\n", header.fw_revision, dsp->algo_info[0].rev); } @@ -390,8 +390,9 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) goto err_free; } - algo_rev = - CL_DSP_SHIFT_REV(data_block.header.algo_rev); + algo_rev = data_block.header.algo_rev >> + CL_DSP_REV_OFFSET_SHIFT; + if (CL_DSP_GET_MAJOR(algo_rev) != CL_DSP_GET_MAJOR(dsp->algo_info[i].rev)) { dev_err(dev, @@ -548,10 +549,10 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) if (wt_found) { if (*wt_date != '\0') - strlcpy(dsp->wt_desc->wt_date, wt_date, + strscpy(dsp->wt_desc->wt_date, wt_date, CL_DSP_WMDR_DATE_LEN); else - strlcpy(dsp->wt_desc->wt_date, + strscpy(dsp->wt_desc->wt_date, CL_DSP_WMDR_FILE_DATE_MISSING, CL_DSP_WMDR_DATE_LEN); } diff --git a/cs40l26/cl_dsp.h b/cs40l26/cl_dsp.h index 7c85e27..eb01682 100644 --- a/cs40l26/cl_dsp.h +++ b/cs40l26/cl_dsp.h @@ -67,8 +67,7 @@ #define CL_DSP_ALGO_LIST_TERM 0xBEDEAD -#define CL_DSP_REV_OFFSET_MASK GENMASK(23, 16) -#define CL_DSP_REV_OFFSET_SHIFT 8 +#define CL_DSP_REV_OFFSET_SHIFT 8 #define CL_DSP_REV_MAJOR_MASK GENMASK(23, 16) #define CL_DSP_REV_MAJOR_SHIFT 16 @@ -145,9 +144,6 @@ (((n) / CL_DSP_BYTES_PER_WORD) *\ CL_DSP_BYTES_PER_WORD)) -#define CL_DSP_SHIFT_REV(n) (((n) >> CL_DSP_REV_OFFSET_SHIFT) &\ - CL_DSP_REV_OFFSET_MASK) - #define CL_DSP_GET_MAJOR(n) (((n) & CL_DSP_REV_MAJOR_MASK) >>\ CL_DSP_REV_MAJOR_SHIFT) diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c index 1b02580..80dd854 100644 --- a/cs40l26/cs40l26-codec.c +++ b/cs40l26/cs40l26-codec.c @@ -240,7 +240,9 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, if (ret) goto err_mutex; - ret = regmap_write(regmap, reg, codec->svc_for_streaming_data); + ret = regmap_update_bits(regmap, reg, + CS40L26_SVC_FOR_STREAMING_MASK, + codec->svc_for_streaming_data); if (ret) { dev_err(dev, "Failed to specify SVC for streaming\n"); goto err_mutex; diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c index 93c1818..77ee9f5 100644 --- a/cs40l26/cs40l26-sysfs.c +++ b/cs40l26/cs40l26-sysfs.c @@ -758,6 +758,223 @@ struct attribute_group cs40l26_dev_attr_group = { .attrs = cs40l26_dev_attrs, }; +static ssize_t dbc_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + u32 val, reg; + int ret; + + 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, "FLAGS", CL_DSP_XM_UNPACKED_TYPE, + CS40L26_EXT_ALGO_ID, ®); + if (ret) + goto err_pm; + + ret = regmap_read(cs40l26->regmap, reg, &val); + if (ret) { + dev_err(cs40l26->dev, "Failed to get FLAGS\n"); + goto err_pm; + } + + + val &= CS40L26_DBC_ENABLE_MASK; + val >>= CS40L26_DBC_ENABLE_SHIFT; + + ret = snprintf(buf, PAGE_SIZE, "%u\n", val); + +err_pm: + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + + return ret; +} + +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)) + return -EINVAL; + + 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, "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"); + +err_pm: + mutex_unlock(&cs40l26->lock); + + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + + return ret ? ret : count; +} +static DEVICE_ATTR_RW(dbc_enable); + +static ssize_t dbc_env_rel_coef_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = cs40l26_dbc_get(cs40l26, CS40L26_DBC_ENV_REL_COEF, &val); + + return ret ? ret : snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +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); + int ret; + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_ENV_REL_COEF, buf); + + return ret ? ret : count; + +} +static DEVICE_ATTR_RW(dbc_env_rel_coef); + +static ssize_t dbc_rise_headroom_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = cs40l26_dbc_get(cs40l26, CS40L26_DBC_RISE_HEADROOM, &val); + + return ret ? ret : snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +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); + int ret; + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_RISE_HEADROOM, buf); + + return ret ? ret : count; +} +static DEVICE_ATTR_RW(dbc_rise_headroom); + +static ssize_t dbc_fall_headroom_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = cs40l26_dbc_get(cs40l26, CS40L26_DBC_FALL_HEADROOM, &val); + + return ret ? ret : snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +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); + int ret; + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_FALL_HEADROOM, buf); + + return ret ? ret : count; +} +static DEVICE_ATTR_RW(dbc_fall_headroom); + +static ssize_t dbc_tx_lvl_thresh_fs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = cs40l26_dbc_get(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, &val); + + return ret ? ret : snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +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); + int ret; + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_THRESH_FS, buf); + + return ret ? ret : count; +} +static DEVICE_ATTR_RW(dbc_tx_lvl_thresh_fs); + +static ssize_t dbc_tx_lvl_hold_off_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = cs40l26_dbc_get(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, &val); + + return ret ? ret : snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +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); + int ret; + + ret = cs40l26_dbc_set(cs40l26, CS40L26_DBC_TX_LVL_HOLD_OFF_MS, buf); + + return ret ? ret : count; +} +static DEVICE_ATTR_RW(dbc_tx_lvl_hold_off_ms); + +static struct attribute *cs40l26_dev_attrs_dbc[] = { + &dev_attr_dbc_enable.attr, + &dev_attr_dbc_env_rel_coef.attr, + &dev_attr_dbc_rise_headroom.attr, + &dev_attr_dbc_fall_headroom.attr, + &dev_attr_dbc_tx_lvl_thresh_fs.attr, + &dev_attr_dbc_tx_lvl_hold_off_ms.attr, + NULL, +}; + +struct attribute_group cs40l26_dev_attr_dbc_group = { + .name = "dbc", + .attrs = cs40l26_dev_attrs_dbc, +}; + static ssize_t trigger_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c index d9dbe1d..eb40114 100644 --- a/cs40l26/cs40l26-tables.c +++ b/cs40l26/cs40l26-tables.c @@ -34,6 +34,14 @@ const struct regmap_config cs40l26_regmap = { .cache_type = REGCACHE_NONE, }; +const char * const cs40l26_dbc_names[CS40L26_DBC_NUM_CONTROLS] = { + CS40L26_DBC_ENV_REL_COEF_NAME, + CS40L26_DBC_RISE_HEADROOM_NAME, + CS40L26_DBC_FALL_HEADROOM_NAME, + CS40L26_DBC_TX_LVL_THRESH_FS_NAME, + CS40L26_DBC_TX_LVL_HOLD_OFF_MS_NAME, +}; + 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}, diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index 5f8f6da..af905d6 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -176,6 +176,90 @@ int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state) } EXPORT_SYMBOL(cs40l26_dsp_state_get); +int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, + unsigned int *val) +{ + struct device *dev = cs40l26->dev; + unsigned int reg; + int ret; + + 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; + + ret = regmap_read(cs40l26->regmap, reg, val); + if (ret) + dev_err(dev, "Failed to read 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_get); + +int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, + const char *buf) +{ + struct device *dev = cs40l26->dev; + unsigned int val, reg, max; + int ret; + + if (dbc == CS40L26_DBC_TX_LVL_HOLD_OFF_MS) + max = CS40L26_DBC_TX_LVL_HOLD_OFF_MS_MAX; + 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; + + 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); + static int cs40l26_pm_timeout_ticks_write(struct cs40l26_private *cs40l26, u32 ms, unsigned int lower_offset, unsigned int upper_offset) { @@ -185,11 +269,8 @@ static int cs40l26_pm_timeout_ticks_write(struct cs40l26_private *cs40l26, u8 upper_val; int ret; - if (ms < CS40L26_PM_TIMEOUT_MS_MIN) { - dev_warn(dev, "Timeout out of bounds, using minimum\n"); - ticks = CS40L26_PM_TIMEOUT_MS_MIN * CS40L26_PM_TICKS_MS_DIV; - } else if (ms > CS40L26_PM_TIMEOUT_MS_MAX) { - dev_warn(dev, "Timeout out of bounds, using maximum\n"); + if (ms > CS40L26_PM_TIMEOUT_MS_MAX) { + dev_warn(dev, "Timeout (%u ms) invalid, using maximum\n", ms); ticks = CS40L26_PM_TIMEOUT_MS_MAX * CS40L26_PM_TICKS_MS_DIV; } else { ticks = ms * CS40L26_PM_TICKS_MS_DIV; @@ -1325,6 +1406,13 @@ static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, if (ret) goto op_new_free; + if (((CS40L26_PSEQ_MAX_WORDS * CL_DSP_BYTES_PER_WORD) - op_end->offset) + < (op_new->size * CL_DSP_BYTES_PER_WORD)) { + dev_err(dev, "Not enough space in pseq to add op\n"); + ret = -ENOMEM; + goto op_new_free; + } + if (is_new) { op_new->offset = op_end->offset; op_end->offset += (num_op_words * CL_DSP_BYTES_PER_WORD); @@ -2699,6 +2787,7 @@ out_free: const struct attribute_group *cs40l26_dev_attr_groups[] = { &cs40l26_dev_attr_group, &cs40l26_dev_attr_cal_group, + &cs40l26_dev_attr_dbc_group, NULL, }; #endif @@ -2708,7 +2797,7 @@ static int cs40l26_clear_gpi_event_reg(struct cs40l26_private *cs40l26, u32 reg) struct regmap *regmap = cs40l26->regmap; int ret; - ret = regmap_write(regmap, reg, 0); + ret = regmap_write(regmap, reg, CS40L26_EVENT_MAP_GPI_EVENT_DISABLE); if (ret) dev_err(cs40l26->dev, "Failed to clear gpi reg: %08X", reg); @@ -2863,8 +2952,8 @@ static int cs40l26_erase_effect(struct input_dev *dev, int effect_id) static int cs40l26_input_init(struct cs40l26_private *cs40l26) { - int ret; struct device *dev = cs40l26->dev; + int ret; cs40l26->input = devm_input_allocate_device(dev); if (!cs40l26->input) @@ -2917,6 +3006,12 @@ static int cs40l26_input_init(struct cs40l26_private *cs40l26) dev_err(dev, "Failed to create cal sysfs group: %d\n", ret); return ret; } + ret = sysfs_create_group(&cs40l26->input->dev.kobj, + &cs40l26_dev_attr_dbc_group); + if (ret) { + dev_err(dev, "Failed to create DBC sysfs group\n"); + return ret; + } #else ret = sysfs_create_groups(&cs40l26->dev->kobj, cs40l26_dev_attr_groups); if (ret) { @@ -3027,21 +3122,21 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id) } } - strncpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME, + strscpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME, CS40L26_WT_FILE_NAME_LEN); if (id == CS40L26_FW_ID) { - strncpy(cs40l26->fw.coeff_files[1], + strscpy(cs40l26->fw.coeff_files[1], CS40L26_A2H_TUNING_FILE_NAME, CS40L26_A2H_TUNING_FILE_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[2], + strscpy(cs40l26->fw.coeff_files[2], CS40L26_SVC_TUNING_FILE_NAME, CS40L26_SVC_TUNING_FILE_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[3], + strscpy(cs40l26->fw.coeff_files[3], CS40L26_DVL_FILE_NAME, CS40L26_DVL_FILE_NAME_LEN); } else { - strncpy(cs40l26->fw.coeff_files[1], + strscpy(cs40l26->fw.coeff_files[1], CS40L26_CALIB_BIN_FILE_NAME, CS40L26_CALIB_BIN_FILE_NAME_LEN); } @@ -3374,6 +3469,8 @@ static int cs40l26_verify_fw(struct cs40l26_private *cs40l26) (int) CL_DSP_GET_MINOR(val), (int) CL_DSP_GET_PATCH(val)); + cs40l26->fw.rev = val; + return 0; } @@ -3918,11 +4015,11 @@ 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) { - strncpy(svc_bin_file, + strscpy(svc_bin_file, CS40L26_SVC_TUNING_FILE_PREFIX, CS40L26_SVC_TUNING_FILE_PREFIX_LEN); - strncpy(wt_bin_file, CS40L26_WT_FILE_PREFIX, + strscpy(wt_bin_file, CS40L26_WT_FILE_PREFIX, CS40L26_WT_FILE_PREFIX_LEN); snprintf(n_str, 2, "%d", @@ -3949,9 +4046,9 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) strncat(wt_bin_file, CS40L26_TUNING_FILE_SUFFIX, CS40L26_TUNING_FILE_SUFFIX_LEN); - strncpy(cs40l26->fw.coeff_files[0], wt_bin_file, + strscpy(cs40l26->fw.coeff_files[0], wt_bin_file, CS40L26_WT_FILE_CONCAT_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[2], svc_bin_file, + strscpy(cs40l26->fw.coeff_files[2], svc_bin_file, CS40L26_SVC_TUNING_FILE_NAME_LEN); } @@ -4066,13 +4163,9 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) { struct device *dev = cs40l26->dev; bool register_irq = false; + u32 pseq_rom_end_of_script_offset_words; int ret; - if (id == cs40l26->fw.id) { - dev_warn(dev, "Cannot swap to same ID as running firmware\n"); - return 0; - } - if (cs40l26->fw_loaded) { ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -4100,6 +4193,25 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) cs40l26->fw_loaded = false; + /* reset pseq END_OF_SCRIPT to location from ROM */ + if (cs40l26->revid == CS40L26_REVID_A1) { + pseq_rom_end_of_script_offset_words = + CS40L26_PSEQ_ROM_OFFSET_WORDS_A1; + } else { + dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid); + return -EINVAL; + } + + ret = regmap_write(cs40l26->regmap, + cs40l26->pseq_base + + pseq_rom_end_of_script_offset_words * + CL_DSP_BYTES_PER_WORD, + 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; + } + ret = cs40l26_fw_upload(cs40l26, id); if (ret) return ret; @@ -4332,7 +4444,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) cs40l26->pdata.pm_stdby_timeout_ms = val; else cs40l26->pdata.pm_stdby_timeout_ms = - CS40L26_PM_TIMEOUT_MS_MIN; + CS40L26_PM_STDBY_TIMEOUT_MS_DEFAULT; if (!of_property_read_u32(np, "cirrus,pm-active-timeout-ms", &val)) cs40l26->pdata.pm_active_timeout_ms = val; @@ -4521,6 +4633,8 @@ int cs40l26_remove(struct cs40l26_private *cs40l26) &cs40l26_dev_attr_group); sysfs_remove_group(&cs40l26->input->dev.kobj, &cs40l26_dev_attr_cal_group); + sysfs_remove_group(&cs40l26->input->dev.kobj, + &cs40l26_dev_attr_dbc_group); #else sysfs_remove_groups(&cs40l26->dev->kobj, cs40l26_dev_attr_groups); diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index c08c3ab..5ac0a19 100644 --- a/cs40l26/cs40l26.h +++ b/cs40l26/cs40l26.h @@ -703,6 +703,7 @@ #define CS40L26_EXT_ALGO_ID 0x0004013C /* power management */ +#define CS40L26_PSEQ_ROM_OFFSET_WORDS_A1 24 #define CS40L26_PSEQ_MAX_WORDS_PER_OP CS40L26_PSEQ_OP_WRITE_FIELD_WORDS #define CS40L26_PSEQ_MAX_WORDS 129 #define CS40L26_PSEQ_NUM_OPS 8 @@ -747,9 +748,8 @@ #define CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET 16 #define CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET 20 -#define CS40L26_PM_STDBY_TIMEOUT_MS_DEFAULT 5000 -#define CS40L26_PM_TIMEOUT_MS_MIN 100 -#define CS40L26_PM_TIMEOUT_MS_MAX 4880 +#define CS40L26_PM_STDBY_TIMEOUT_MS_DEFAULT 100 +#define CS40L26_PM_TIMEOUT_MS_MAX 10000 #define CS40L26_PM_ACTIVE_TIMEOUT_LOWER_OFFSET 24 #define CS40L26_PM_ACTIVE_TIMEOUT_UPPER_OFFSET 28 #define CS40L26_PM_ACTIVE_TIMEOUT_MS_DEFAULT 250 @@ -837,7 +837,7 @@ #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 16 +#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 @@ -926,6 +926,7 @@ #define CS40L26_GPIO1 1 #define CS40L26_EVENT_MAP_INDEX_MASK GENMASK(8, 0) #define CS40L26_EVENT_MAP_NUM_GPI_REGS 4 +#define CS40L26_EVENT_MAP_GPI_EVENT_DISABLE 0x1FF #define CS40L26_BTN_INDEX_MASK GENMASK(7, 0) #define CS40L26_BTN_BUZZ_MASK BIT(7) @@ -1229,6 +1230,20 @@ #define CS40L26_COMP_EN_REDC_SHIFT 1 #define CS40L26_COMP_EN_F0_SHIFT 0 +/* FW EXT */ +#define CS40L26_SVC_FOR_STREAMING_MASK BIT(0) + +/* DBC */ +#define CS40L26_DBC_ENABLE_MASK BIT(1) +#define CS40L26_DBC_ENABLE_SHIFT 1 +#define CS40L26_DBC_TX_LVL_HOLD_OFF_MS_MAX 1000 +#define CS40L26_DBC_CONTROLS_MAX 0x7FFFFF +#define CS40L26_DBC_ENV_REL_COEF_NAME "DBC_ENV_REL_COEF" +#define CS40L26_DBC_RISE_HEADROOM_NAME "DBC_RISE_HEADROOM" +#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" + /* Errata */ #define CS40L26_ERRATA_A1_NUM_WRITES 4 #define CS40L26_ERRATA_A1_EXPL_EN_NUM_WRITES 1 @@ -1253,6 +1268,15 @@ #define CS40L26_SAMPS_TO_MS(n) ((n) / 8) /* enums */ +enum cs40l26_dbc { + CS40L26_DBC_ENV_REL_COEF, /* 0 */ + CS40L26_DBC_RISE_HEADROOM, + CS40L26_DBC_FALL_HEADROOM, + CS40L26_DBC_TX_LVL_THRESH_FS, + CS40L26_DBC_TX_LVL_HOLD_OFF_MS, + CS40L26_DBC_NUM_CONTROLS, /* 5 */ +}; + enum cs40l26_vibe_state { CS40L26_VIBE_STATE_STOPPED, CS40L26_VIBE_STATE_HAPTIC, @@ -1362,6 +1386,7 @@ 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; @@ -1504,6 +1529,10 @@ struct cs40l26_pll_sysclk_config { }; /* exported function prototypes */ +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); 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); @@ -1547,10 +1576,12 @@ 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]; +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; +extern struct attribute_group cs40l26_dev_attr_dbc_group; #endif /* __CS40L26_H__ */ |