summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2022-03-04 15:05:33 +0800
committerTai Kuo <taikuo@google.com>2022-03-04 09:33:10 +0000
commitd58df7442c6163bd2233ab38814565f5f2d7539c (patch)
treebdda15e209f632ba97b453fb937bbab3f99ee7e4
parent4edf770fd08f672a72131cf2fc60b36edd46e773 (diff)
downloadamplifiers-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.c11
-rw-r--r--cs40l26/cl_dsp.h6
-rw-r--r--cs40l26/cs40l26-codec.c4
-rw-r--r--cs40l26/cs40l26-sysfs.c217
-rw-r--r--cs40l26/cs40l26-tables.c8
-rw-r--r--cs40l26/cs40l26.c158
-rw-r--r--cs40l26/cs40l26.h39
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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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__ */