diff options
author | Tai Kuo <taikuo@google.com> | 2022-10-04 17:43:27 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2022-10-24 08:21:47 +0000 |
commit | a446aef40c377974eb834f781e1b82562d673a4f (patch) | |
tree | ef6a010378a5f1f22b4663f85f097f632d18b285 | |
parent | 4fbab05a66f7e9ed54ef9afac64cdc4a0588a7de (diff) | |
download | amplifiers-a446aef40c377974eb834f781e1b82562d673a4f.tar.gz |
cs40l26: merge cs40l26 v5.5.0, 5.6.0, 5.6.1 and dsp v3.1.10
Branch: v5.15-cirrus-dsp-fw and v5.15-cs40l26
Tag: cl-dsp-fw-v3.1.10_5.15, cs40l26-v5.6.1_5.15, cs40l26-v5.6.0_5.15
and cs40l26-v5.5.0_5.15
Files:
drivers/firmware/cirrus/cl_dsp.c
include/linux/firmware/cirrus/cl_dsp.h
drivers/input/misc/cs40l26-i2c.c
drivers/input/misc/cs40l26-spi.c
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
...
Tag: cl-dsp-fw-v3.1.10_5.10: Cirrus DSP Firmware Driver v3.1.10
- Error reporting from memchunk functions
- Resolved issues causing KASAN warnings
- Improved code readability
Commits:
b8c9ea5 firmware: cirrus: Add function cl_dsp_wavetable_check()
2e99074 firmware: cirrus: Use strscpy() instead of memcpy() for string copy
7b1080c firmware: cirrus: Incorporate ERR_PTR and related macros
0642715 firmware: cirrus: Add memchunk_read error reporting
00861ea Revert "firmware: cirrus: Add memchunk_read error reporting"
02deb1b firmware: cirrus: Add memchunk_read error reporting
...
Tag: cs40l26-v5.6.1_5.10: CS40L26 v5.6.1
Bug fixes:
- Resolve potential for NULL pointer derefence
leading to kernel panic
Commits:
cd45047 input: cs40l26: Fix dangling pointer
59aea6b input: cs40l26: Check for empty list
...
Tag: cs40l26-v5.6.0_5.10: CS40L26 v5.6.0
Features:
- Load SVC tuning file when calibration firmware is loaded
Bug fixes:
- Avoid possibility of infinite loop in asp_start()
- Remove PDATA_PRESENT check to set DT properties correctly
- Prevent user from starting calibration if calibration firmware is not loaded
- Explicitly prevent user from editing OWT effects
- Address KASAN warnings
Commits:
8d8ec58 input: cs40l26: Fix typo in sysfs file header
a2bb173 input: cs40l26: Re-format lines > 80 characters
e98056f input: cs40l26: Load SVC tuning file along with calib. F/W
db62b28 input: cs40l26: Disallow editing of Open Wavetable effects
4b1cac8 input: cs40l26: Remove extraneous lines
8a2f865 input: cs40l26: Disallow calibration if correct firmware not loaded
074b436 input: cs40l26: Fix DT property not being set
e2f016b input: cs40l26: Remove regmap writes before DSP start
8542587 input: cs40l26: Incorporate ERR_PTR and related macros
d8d24a4 input: cs40l26: Avoid infinite loop in asp_start
a6b6046 input: cs40l26: Add memchunk_read error reporting
...
Tag: cs40l26-v5.5.0_5.10: CS40L26 v5.5.0
Features:
- Move SVC LE Estimation procedure from probe time to runtime
- API CHANGE: svc_le_stored added as a sysfs control
Bug fixes:
- OWT Nested Composites were not expanding properly
- Added missing documentation from .yaml file cirrus,fw-defer (Skip)
- Remove check of logger source count when performing BEMF, VBST, or VMON logging
Commits:
d79fc88 Documentation: cs40l26: Add documentation for cirrus,fw-defer (Skip)
d0744f7 ASoC: cs40l26: Add pm_enter/pm_exit functions
69cc3fb input: cs40l26: Add pm_enter/pm_exit functions
f1d7c12 input: cs40l26: Fix typo in SVC algo ID macro
d9eb712 ASoC: cs40l26: Add dmesg for a2h tuning file load
fdad29f input: cs40l26: Remove check for logger src count
432dacf ASoC: cs40l26: Update year in header comments
abb2a2c input: cs40l26: Update year in header comments
2eb2548 input: cs40l26: Remove unneeded function call
53c5613 input: cs40l26: Avoid pm teardown if fw is not loaded
b44e61d ASoC: cs40l26: Fix mutex and pm_runtime in kcontrols
00be432 input: cs40l26: Add support for Le est on production line
35bfaee input: cs40l26: Remove le est from tuning select
849939b input: cs40l26: Clamp effects_in_flight to minimum of 0
Bug: 249456529
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
Test: atest PtsVibratorHalTestSuite \
PtsHapticsTestCases \
VibratorHalCs40l26TestSuite \
VtsHalVibratorManagerTargetTest \
VtsHalVibratorTargetTest \
android.os.cts.VibratorTest \
android.os.cts.VibratorManagerTest \
android.os.cts.VibrationEffectTest \
android.os.cts.VibrationAttributesTest \
android.os.cts.CombinedVibrationTest
Signed-off-by: Tai Kuo <taikuo@google.com>
Change-Id: I262c3d4dd3d5205cc0cd1228f9f67607a86b2cb1
-rw-r--r-- | cs40l26/cl_dsp.c | 212 | ||||
-rw-r--r-- | cs40l26/cl_dsp.h | 3 | ||||
-rw-r--r-- | cs40l26/cs40l26-codec.c | 151 | ||||
-rw-r--r-- | cs40l26/cs40l26-i2c.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26-spi.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 484 | ||||
-rw-r--r-- | cs40l26/cs40l26-tables.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 528 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 14 |
9 files changed, 710 insertions, 688 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c index bcbee33..6549351 100644 --- a/cs40l26/cl_dsp.c +++ b/cs40l26/cl_dsp.c @@ -30,35 +30,45 @@ static inline bool cl_dsp_memchunk_valid_addr(struct cl_dsp_memchunk *ch, return (u8 *)addr <= ch->max; } -int cl_dsp_memchunk_read(struct cl_dsp_memchunk *ch, int nbits) +int cl_dsp_memchunk_read(struct cl_dsp *dsp, struct cl_dsp_memchunk *ch, + int nbits, void *val) { - int nread, i; - u32 result; + int nbytes = nbits / 8, nread, i; + u32 result = 0; - if (!ch->cachebits) { - if (cl_dsp_memchunk_end(ch)) - return -ENOSPC; + if (nbits > 32) { + dev_err(dsp->dev, "Exceeded maximum read length: %d > 32\n", nbits); + return -EINVAL; + } - ch->cache = 0; - ch->cachebits = 24; + while (nbits) { + if (!ch->cachebits) { + if (cl_dsp_memchunk_end(ch)) { + dev_err(dsp->dev, "Read past end of memory chunk\n"); + return -ENOSPC; + } - for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= 8) - ch->cache |= *ch->data++; + ch->cache = 0; + ch->cachebits = 24; - ch->bytes += sizeof(ch->cache); - } + for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= 8) + ch->cache |= *ch->data++; - nread = min(ch->cachebits, nbits); - nbits -= nread; + ch->bytes += sizeof(ch->cache); + } - result = ch->cache >> (32 - nread); - ch->cache <<= nread; - ch->cachebits -= nread; + nread = min(ch->cachebits, nbits); + nbits -= nread; - if (nbits) - result = (result << nbits) | cl_dsp_memchunk_read(ch, nbits); + result |= ((ch->cache >> (32 - nread)) << nbits); + ch->cache <<= nread; + ch->cachebits -= nread; + } + + if (val) + memcpy(val, &result, nbytes); - return result; + return 0; } EXPORT_SYMBOL(cl_dsp_memchunk_read); @@ -241,11 +251,16 @@ static int cl_dsp_read_wt(struct cl_dsp *dsp, int pos, int size) void *buf = (void *)(dsp->wt_desc->owt.raw_data + pos); struct cl_dsp_memchunk ch = cl_dsp_memchunk_create(buf, size); u32 *wbuf = buf, *max = buf; - int i; + int i, ret; for (i = 0; i < ARRAY_SIZE(dsp->wt_desc->owt.waves); i++, entry++) { - entry->flags = cl_dsp_memchunk_read(&ch, 16); - entry->type = cl_dsp_memchunk_read(&ch, 8); + ret = cl_dsp_memchunk_read(dsp, &ch, 16, &entry->flags); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(dsp, &ch, 8, &entry->type); + if (ret) + return ret; if (entry->type == WT_TYPE_TERMINATOR) { dsp->wt_desc->owt.nwaves = i; @@ -255,8 +270,14 @@ static int cl_dsp_read_wt(struct cl_dsp *dsp, int pos, int size) return dsp->wt_desc->owt.bytes; } - entry->offset = cl_dsp_memchunk_read(&ch, 24); - entry->size = cl_dsp_memchunk_read(&ch, 24); + ret = cl_dsp_memchunk_read(dsp, &ch, 24, &entry->offset); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(dsp, &ch, 24, &entry->size); + if (ret) + return ret; + entry->data = wbuf + entry->offset; if (wbuf + entry->offset + entry->size > max) { @@ -331,6 +352,56 @@ static void cl_dsp_coeff_handle_info_text(struct cl_dsp *dsp, const u8 *data, kfree(info_str); } +static int cl_dsp_wavetable_check(struct cl_dsp *dsp, const struct firmware *fw, + unsigned int reg, unsigned int pos, u32 data_len, u32 type) +{ + u32 data_len_bytes = data_len / 4 * 3; + unsigned int limit, wt_reg; + bool is_xm; + int ret; + + if (type == CL_DSP_XM_UNPACKED_TYPE) { + is_xm = true; + limit = dsp->wt_desc->wt_limit_xm; + ret = cl_dsp_get_reg(dsp, dsp->wt_desc->wt_name_xm, + type, dsp->wt_desc->id, &wt_reg); + } else if (type == CL_DSP_YM_UNPACKED_TYPE) { + is_xm = false; + limit = dsp->wt_desc->wt_limit_ym; + ret = cl_dsp_get_reg(dsp, dsp->wt_desc->wt_name_ym, + type, dsp->wt_desc->id, &wt_reg); + } else { + dev_err(dsp->dev, "Invalid wavetable memory type 0x%04X\n", + type); + ret = -EINVAL; + } + if (ret) + return ret; + + if (reg == wt_reg) { + if (data_len > limit) { + dev_err(dsp->dev, "%s too large: %d bytes\n", + is_xm ? "XM" : "YM", data_len_bytes); + return -EFBIG; + } + + ret = cl_dsp_owt_init(dsp, fw); + if (ret) + return ret; + + ret = cl_dsp_read_wt(dsp, pos, data_len); + if (ret < 0) + return ret; + + dsp->wt_desc->is_xm = is_xm; + + dev_info(dsp->dev, "Wavetable found: %d bytes (XM)\n", + data_len_bytes); + } + + return 0; +} + int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) { unsigned int pos = CL_DSP_COEFF_FILE_HEADER_SIZE; @@ -339,9 +410,10 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) struct cl_dsp_coeff_data_block data_block; union cl_dsp_wmdr_header wmdr_header; char wt_date[CL_DSP_WMDR_DATE_LEN]; - unsigned int reg, wt_reg, algo_rev; + unsigned int reg, algo_rev; u16 algo_id, parent_id; struct device *dev; + u32 data_len; int i; if (!dsp) @@ -367,13 +439,12 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) CL_DSP_COEFF_DBLK_HEADER_SIZE); pos += CL_DSP_COEFF_DBLK_HEADER_SIZE; - data_block.payload = kmalloc(data_block.header.data_len, - GFP_KERNEL); + data_len = data_block.header.data_len; + data_block.payload = kmalloc(data_len, GFP_KERNEL); if (!data_block.payload) return -ENOMEM; - memcpy(data_block.payload, &fw->data[pos], - data_block.header.data_len); + memcpy(data_block.payload, &fw->data[pos], data_len); algo_id = data_block.header.algo_id & 0xFFFF; @@ -417,9 +488,9 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) reg = 0; cl_dsp_coeff_handle_info_text(dsp, data_block.payload, - data_block.header.data_len); + data_len); - if (data_block.header.data_len < CL_DSP_WMDR_DATE_LEN) + if (data_len < CL_DSP_WMDR_DATE_LEN) break; if (memcmp(&fw->data[pos], CL_DSP_WMDR_DATE_PREFIX, @@ -441,39 +512,11 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) CL_DSP_BYTES_PER_WORD; if (wt_found) { - ret = cl_dsp_get_reg(dsp, - dsp->wt_desc->wt_name_xm, - CL_DSP_XM_UNPACKED_TYPE, - dsp->wt_desc->id, &wt_reg); + ret = cl_dsp_wavetable_check(dsp, + fw, reg, pos, data_len, + CL_DSP_XM_UNPACKED_TYPE); if (ret) goto err_free; - - if (reg == wt_reg) { - if (data_block.header.data_len > - dsp->wt_desc->wt_limit_xm) { - dev_err(dev, - "XM too large: %d bytes\n", - data_block.header.data_len - / 4 * 3); - - ret = -EINVAL; - goto err_free; - } else { - ret = cl_dsp_owt_init(dsp, fw); - if (ret) - goto err_free; - - ret = cl_dsp_read_wt(dsp, pos, - data_block.header.data_len); - if (ret < 0) - goto err_free; - dsp->wt_desc->is_xm = true; - } - - dev_info(dev, - "Wavetable found: %d bytes (XM)\n", - data_block.header.data_len / 4 * 3); - } } break; case CL_DSP_XM_PACKED_TYPE: @@ -489,35 +532,11 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) dsp->algo_info[i].ym_base * CL_DSP_UNPACKED_NUM_BYTES; if (wt_found) { - ret = cl_dsp_get_reg(dsp, - dsp->wt_desc->wt_name_ym, - CL_DSP_YM_UNPACKED_TYPE, - dsp->wt_desc->id, &wt_reg); + ret = cl_dsp_wavetable_check(dsp, + fw, reg, pos, data_len, + CL_DSP_YM_UNPACKED_TYPE); if (ret) goto err_free; - - if (reg == wt_reg) { - if (data_block.header.data_len > - dsp->wt_desc->wt_limit_ym) { - dev_err(dev, - "YM too large: %d bytes\n", - data_block.header.data_len - / 4 * 3); - - ret = -EINVAL; - goto err_free; - } else { - ret = cl_dsp_read_wt(dsp, pos, - data_block.header.data_len); - if (ret < 0) - goto err_free; - dsp->wt_desc->is_xm = false; - } - - dev_dbg(dev, - "Wavetable found: %d bytes (YM)\n", - data_block.header.data_len / 4 * 3); - } } break; case CL_DSP_YM_PACKED_TYPE: @@ -535,8 +554,7 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) } if (reg) { ret = cl_dsp_raw_write(dsp, reg, &fw->data[pos], - data_block.header.data_len, - CL_DSP_MAX_WLEN); + data_len, CL_DSP_MAX_WLEN); if (ret) { dev_err(dev, "Failed to write coefficients\n"); goto err_free; @@ -544,7 +562,7 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) } /* Blocks are word-aligned */ - pos += (data_block.header.data_len + 3) & ~CL_DSP_ALIGN; + pos += (data_len + 3) & ~CL_DSP_ALIGN; kfree(data_block.payload); } @@ -1088,9 +1106,9 @@ int cl_dsp_wavetable_create(struct cl_dsp *dsp, unsigned int id, return -ENOMEM; wt_desc->id = id; - memcpy(wt_desc->wt_name_xm, wt_name_xm, CL_DSP_WMDR_NAME_LEN); - memcpy(wt_desc->wt_name_ym, wt_name_ym, CL_DSP_WMDR_NAME_LEN); - memcpy(wt_desc->wt_file, wt_file, CL_DSP_WMDR_NAME_LEN); + strscpy(wt_desc->wt_name_xm, wt_name_xm, strlen(wt_name_xm) + 1); + strscpy(wt_desc->wt_name_ym, wt_name_ym, strlen(wt_name_ym) + 1); + strscpy(wt_desc->wt_file, wt_file, strlen(wt_file) + 1); dsp->wt_desc = wt_desc; @@ -1104,7 +1122,7 @@ struct cl_dsp *cl_dsp_create(struct device *dev, struct regmap *regmap) dsp = devm_kzalloc(dev, sizeof(struct cl_dsp), GFP_KERNEL); if (!dsp) - return NULL; + return ERR_PTR(-ENOMEM); dsp->dev = dev; dsp->regmap = regmap; diff --git a/cs40l26/cl_dsp.h b/cs40l26/cl_dsp.h index eb01682..31169f4 100644 --- a/cs40l26/cl_dsp.h +++ b/cs40l26/cl_dsp.h @@ -311,7 +311,8 @@ int cl_dsp_get_reg(struct cl_dsp *dsp, const char *coeff_name, unsigned int *reg); struct cl_dsp_memchunk cl_dsp_memchunk_create(void *data, int size); int cl_dsp_memchunk_write(struct cl_dsp_memchunk *ch, int nbits, u32 val); -int cl_dsp_memchunk_read(struct cl_dsp_memchunk *ch, int nbits); +int cl_dsp_memchunk_read(struct cl_dsp *dsp, struct cl_dsp_memchunk *ch, + int nbits, void *val); int cl_dsp_memchunk_flush(struct cl_dsp_memchunk *ch); int cl_dsp_raw_write(struct cl_dsp *dsp, unsigned int reg, const void *val, size_t val_len, size_t limit); diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c index d126149..b1c8b6e 100644 --- a/cs40l26/cs40l26-codec.c +++ b/cs40l26/cs40l26-codec.c @@ -2,7 +2,7 @@ // // cs40l26.c -- ALSA SoC Audio driver for Cirrus Logic Haptic Device: CS40L26 // -// Copyright 2021 Cirrus Logic. Inc. +// Copyright 2022 Cirrus Logic. Inc. #include "cs40l26.h" @@ -167,8 +167,15 @@ static int cs40l26_a2h_ev(struct snd_soc_dapm_widget *w, ret = cl_dsp_coeff_file_parse(cs40l26->dsp, fw); release_firmware(fw); - if (ret) + if (ret) { + dev_warn(dev, + "Failed to load %s, %d. Continuing...", + codec->bin_file, ret); return ret; + } + + dev_info(dev, "%s Loaded Successfully\n", + codec->bin_file); codec->tuning_prev = codec->tuning; @@ -276,11 +283,9 @@ static int cs40l26_i2s_vmon_get(struct snd_kcontrol *kcontrol, int ret; u32 val; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = regmap_read(cs40l26->regmap, CS40L26_SPKMON_VMON_DEC_OUT_DATA, &val); @@ -299,8 +304,7 @@ static int cs40l26_i2s_vmon_get(struct snd_kcontrol *kcontrol, CS40L26_VMON_DEC_OUT_DATA_MASK; pm_err: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -359,31 +363,32 @@ static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = regmap_read(regmap, reg, &val); if (ret) { dev_err(cs40l26->dev, "Failed to read FLAGS\n"); - return ret; + goto pm_err; } - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - if (val & CS40L26_SVC_FOR_STREAMING_MASK) ucontrol->value.enumerated.item[0] = 1; else ucontrol->value.enumerated.item[0] = 0; +pm_err: + cs40l26_pm_exit(dev); + return ret; } + static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_codec *codec = snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_private *cs40l26 = codec->core; @@ -397,11 +402,11 @@ static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } + + snd_soc_dapm_mutex_lock(dapm); ret = regmap_update_bits(regmap, reg, CS40L26_SVC_FOR_STREAMING_MASK, @@ -409,8 +414,9 @@ static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol, if (ret) dev_err(cs40l26->dev, "Failed to specify SVC for streaming\n"); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + snd_soc_dapm_mutex_unlock(dapm); + + cs40l26_pm_exit(dev); return ret; } @@ -431,32 +437,32 @@ static int cs40l26_invert_streaming_data_get(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = regmap_read(regmap, reg, &val); if (ret) { dev_err(cs40l26->dev, "Failed to read SOURCE_INVERT\n"); - return ret; + goto pm_err; } - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - if (val) ucontrol->value.enumerated.item[0] = 1; else ucontrol->value.enumerated.item[0] = 0; +pm_err: + cs40l26_pm_exit(dev); + return ret; } static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_codec *codec = snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_private *cs40l26 = codec->core; @@ -470,18 +476,19 @@ static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } + + snd_soc_dapm_mutex_lock(dapm); ret = regmap_write(regmap, reg, ucontrol->value.enumerated.item[0]); if (ret) dev_err(cs40l26->dev, "Failed to specify invert streaming data\n"); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + snd_soc_dapm_mutex_unlock(dapm); + + cs40l26_pm_exit(dev); return ret; } @@ -540,27 +547,29 @@ static int cs40l26_a2h_volume_get(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = regmap_read(regmap, reg, &val); - if (ret) + if (ret) { dev_err(dev, "Failed to get VOLUMELEVEL\n"); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + goto pm_err; + } ucontrol->value.integer.value[0] = val; +pm_err: + cs40l26_pm_exit(dev); + return ret; } static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_codec *codec = snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_private *cs40l26 = codec->core; @@ -574,6 +583,12 @@ static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol, if (ret) return ret; + ret = cs40l26_pm_enter(dev); + if (ret) + return ret; + + snd_soc_dapm_mutex_lock(dapm); + if (ucontrol->value.integer.value[0] > CS40L26_A2H_VOLUME_MAX) val = CS40L26_A2H_VOLUME_MAX; else if (ucontrol->value.integer.value[0] < 0) @@ -581,18 +596,13 @@ static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol, else val = ucontrol->value.integer.value[0]; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); - return ret; - } - ret = regmap_write(regmap, reg, val); if (ret) dev_err(dev, "Failed to set VOLUMELEVEL\n"); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + snd_soc_dapm_mutex_unlock(dapm); + + cs40l26_pm_exit(dev); return ret; } @@ -638,11 +648,9 @@ static int cs40l26_a2h_delay_get(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = regmap_read(regmap, reg, &val); if (ret) { @@ -653,8 +661,7 @@ static int cs40l26_a2h_delay_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = val; err: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -662,6 +669,8 @@ err: static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_codec *codec = snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); struct cs40l26_private *cs40l26 = codec->core; @@ -675,6 +684,12 @@ static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol, if (ret) return ret; + ret = cs40l26_pm_enter(dev); + if (ret) + return ret; + + snd_soc_dapm_mutex_lock(dapm); + if (ucontrol->value.integer.value[0] > CS40L26_A2H_DELAY_MAX) val = CS40L26_A2H_DELAY_MAX; else if (ucontrol->value.integer.value[0] < 0) @@ -682,18 +697,13 @@ static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol, else val = ucontrol->value.integer.value[0]; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); - return ret; - } - ret = regmap_write(regmap, reg, val); if (ret) dev_err(dev, "Failed to set LRADELAYSAMPS\n"); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + snd_soc_dapm_mutex_unlock(dapm); + + cs40l26_pm_exit(dev); return ret; } @@ -836,11 +846,9 @@ static int cs40l26_pcm_hw_params(struct snd_pcm_substream *substream, u8 asp_rx_wl, asp_rx_width, global_fs; int ret, lrck; - ret = pm_runtime_get_sync(codec->dev); - if (ret < 0) { - cs40l26_resume_error_handle(codec->dev, ret); + ret = cs40l26_pm_enter(codec->dev); + if (ret) return ret; - } lrck = params_rate(params); switch (lrck) { @@ -902,8 +910,7 @@ static int cs40l26_pcm_hw_params(struct snd_pcm_substream *substream, codec->tdm_slot[1]); err_pm: - pm_runtime_mark_last_busy(codec->dev); - pm_runtime_put_autosuspend(codec->dev); + cs40l26_pm_exit(codec->dev); return ret; } diff --git a/cs40l26/cs40l26-i2c.c b/cs40l26/cs40l26-i2c.c index fac18be..971a49c 100644 --- a/cs40l26/cs40l26-i2c.c +++ b/cs40l26/cs40l26-i2c.c @@ -2,7 +2,7 @@ // // cs40l26-i2c.c -- CS40L26 I2C Driver // -// Copyright 2021 Cirrus Logic, Inc. +// Copyright 2022 Cirrus Logic, Inc. // // Author: Fred Treven <fred.treven@cirrus.com> // diff --git a/cs40l26/cs40l26-spi.c b/cs40l26/cs40l26-spi.c index 695cab8..7d0f021 100644 --- a/cs40l26/cs40l26-spi.c +++ b/cs40l26/cs40l26-spi.c @@ -2,7 +2,7 @@ // // cs40l26-spi.c -- CS40L26 SPI Driver // -// Copyright 2021 Cirrus Logic, Inc. +// Copyright 2022 Cirrus Logic, Inc. // // Author: Fred Treven <fred.treven@cirrus.com> // diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c index ce4f711..1f125db 100644 --- a/cs40l26/cs40l26-sysfs.c +++ b/cs40l26/cs40l26-sysfs.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // -// cs40l26-syfs.c -- CS40L26 Boosted Haptic Driver with Integrated DSP and +// cs40l26-sysfs.c -- CS40L26 Boosted Haptic Driver with Integrated DSP and // Waveform Memory with Advanced Closed Loop Algorithms and LRA protection // -// Copyright 2021 Cirrus Logic, Inc. +// Copyright 2022 Cirrus Logic, Inc. // // Author: Fred Treven <fred.treven@cirrus.com> // @@ -20,16 +20,13 @@ static ssize_t dsp_state_show(struct device *dev, u8 dsp_state; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_dsp_state_get(cs40l26, &dsp_state); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -51,16 +48,13 @@ static ssize_t halo_heartbeat_show(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = regmap_read(cs40l26->regmap, reg, &halo_heartbeat); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -76,16 +70,13 @@ static ssize_t pm_stdby_timeout_ms_show(struct device *dev, u32 timeout_ms; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -104,16 +95,13 @@ static ssize_t pm_stdby_timeout_ms_store(struct device *dev, if (ret) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, timeout_ms); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -129,16 +117,13 @@ static ssize_t pm_active_timeout_ms_show(struct device *dev, u32 timeout_ms; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_pm_active_timeout_ms_get(cs40l26, &timeout_ms); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -157,16 +142,13 @@ static ssize_t pm_active_timeout_ms_store(struct device *dev, if (ret) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_pm_active_timeout_ms_set(cs40l26, timeout_ms); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -206,6 +188,12 @@ static ssize_t power_on_seq_show(struct device *dev, base = cs40l26->pseq_base; + if (list_empty(&cs40l26->pseq_op_head)) { + dev_err(cs40l26->dev, "Power on sequence is empty\n"); + ret = -EINVAL; + goto err_mutex; + } + list_for_each_entry_reverse(op, &cs40l26->pseq_op_head, list) { switch (op->operation) { case CS40L26_PSEQ_OP_WRITE_FULL: @@ -259,11 +247,9 @@ static ssize_t owt_free_space_show(struct device *dev, u32 reg, words; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_SIZE_XM", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -279,8 +265,7 @@ static ssize_t owt_free_space_show(struct device *dev, ret = snprintf(buf, PAGE_SIZE, "%d\n", words * CL_DSP_BYTES_PER_WORD); err_pm: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -295,11 +280,9 @@ static ssize_t die_temp_show(struct device *dev, int ret; u32 val; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = regmap_read(regmap, CS40L26_GLOBAL_ENABLES, &val); if (ret) { @@ -326,8 +309,7 @@ static ssize_t die_temp_show(struct device *dev, ret = snprintf(buf, PAGE_SIZE, "0x%03X\n", die_temp); err_pm: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -340,11 +322,9 @@ static ssize_t num_waves_show(struct device *dev, u32 nwaves; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cs40l26_get_num_waves(cs40l26, &nwaves); if (ret) @@ -353,8 +333,7 @@ static ssize_t num_waves_show(struct device *dev, ret = snprintf(buf, PAGE_SIZE, "%u\n", nwaves); err_pm: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -368,11 +347,9 @@ static ssize_t boost_disable_delay_show(struct device *dev, u32 reg, boost_disable_delay; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY", CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); @@ -386,8 +363,7 @@ static ssize_t boost_disable_delay_show(struct device *dev, ret = snprintf(buf, PAGE_SIZE, "%d\n", boost_disable_delay); err_pm: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -408,11 +384,9 @@ static ssize_t boost_disable_delay_store(struct device *dev, boost_disable_delay > CS40L26_BOOST_DISABLE_DELAY_MAX) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY", CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); @@ -422,8 +396,7 @@ static ssize_t boost_disable_delay_store(struct device *dev, ret = regmap_write(cs40l26->regmap, reg, boost_disable_delay); err_pm: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -439,11 +412,9 @@ static ssize_t f0_offset_show(struct device *dev, unsigned int reg, val; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -462,8 +433,8 @@ static ssize_t f0_offset_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -482,11 +453,9 @@ static ssize_t f0_offset_store(struct device *dev, if (val > CS40L26_F0_OFFSET_MAX && val < CS40L26_F0_OFFSET_MIN) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -504,8 +473,8 @@ static ssize_t f0_offset_store(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -587,11 +556,9 @@ static ssize_t f0_comp_enable_store(struct device *dev, if (ret) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -621,8 +588,8 @@ static ssize_t f0_comp_enable_store(struct device *dev, err_mutex: cs40l26->comp_enable_pend = false; mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -667,11 +634,10 @@ static ssize_t redc_comp_enable_store(struct device *dev, if (ret) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } + mutex_lock(&cs40l26->lock); cs40l26->comp_enable_pend = true; @@ -700,8 +666,8 @@ static ssize_t redc_comp_enable_store(struct device *dev, err_mutex: cs40l26->comp_enable_pend = false; mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -786,11 +752,10 @@ static ssize_t vpbr_thld_store(struct device *dev, sysfs_val < CS40L26_VPBR_THLD_MIN) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } + mutex_lock(&cs40l26->lock); if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { @@ -835,8 +800,8 @@ static ssize_t vpbr_thld_store(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -874,11 +839,9 @@ static ssize_t dbc_enable_show(struct device *dev, u32 val, reg; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -902,8 +865,7 @@ static ssize_t dbc_enable_show(struct device *dev, err_pm: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -922,11 +884,9 @@ static ssize_t dbc_enable_store(struct device *dev, if (val > 1) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -934,8 +894,7 @@ static ssize_t dbc_enable_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret ? ret : count; } @@ -965,11 +924,9 @@ static ssize_t dbc_env_rel_coef_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -977,8 +934,7 @@ static ssize_t dbc_env_rel_coef_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1008,11 +964,9 @@ static ssize_t dbc_rise_headroom_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1020,8 +974,7 @@ static ssize_t dbc_rise_headroom_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1051,11 +1004,9 @@ static ssize_t dbc_fall_headroom_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1063,8 +1014,7 @@ static ssize_t dbc_fall_headroom_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1094,11 +1044,9 @@ static ssize_t dbc_tx_lvl_thresh_fs_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1106,8 +1054,7 @@ static ssize_t dbc_tx_lvl_thresh_fs_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1137,11 +1084,9 @@ static ssize_t dbc_tx_lvl_hold_off_ms_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1149,8 +1094,7 @@ static ssize_t dbc_tx_lvl_hold_off_ms_store(struct device *dev, mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1180,6 +1124,11 @@ static ssize_t trigger_calibration_store(struct device *dev, dev_dbg(cs40l26->dev, "%s: %s", __func__, buf); + if (!cs40l26->calib_fw) { + dev_err(cs40l26->dev, "Must use calibration firmware\n"); + return -EPERM; + } + ret = kstrtou32(buf, 16, &calibration_request_payload); if (ret || calibration_request_payload < 1 || @@ -1192,12 +1141,10 @@ static ssize_t trigger_calibration_store(struct device *dev, (calibration_request_payload & CS40L26_DSP_MBOX_CMD_PAYLOAD_MASK); - /* pm_runtime_put occurs is irq_handler after diagnostic is finished */ - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + /* pm_exit occurs is irq_handler after diagnostic is finished */ + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1225,11 +1172,9 @@ static ssize_t f0_measured_show(struct device *dev, int ret; u32 reg, f0_measured; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1245,8 +1190,8 @@ static ssize_t f0_measured_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1262,11 +1207,9 @@ static ssize_t q_measured_show(struct device *dev, int ret; u32 reg, q_measured; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1282,8 +1225,8 @@ static ssize_t q_measured_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1299,17 +1242,15 @@ static ssize_t redc_measured_show(struct device *dev, int ret; u32 reg, redc_measured; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); ret = cl_dsp_get_reg(cs40l26->dsp, "RE_EST_STATUS", CL_DSP_YM_UNPACKED_TYPE, - CS40l26_SVC_ALGO_ID, ®); + CS40L26_SVC_ALGO_ID, ®); if (ret) goto err_mutex; @@ -1319,8 +1260,8 @@ static ssize_t redc_measured_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1336,11 +1277,9 @@ static ssize_t redc_est_show(struct device *dev, struct device_attribute *attr, int ret; u32 reg, redc_est; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1356,8 +1295,8 @@ static ssize_t redc_est_show(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1378,11 +1317,9 @@ static ssize_t redc_est_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1398,8 +1335,8 @@ static ssize_t redc_est_store(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1415,11 +1352,9 @@ static ssize_t f0_stored_show(struct device *dev, int ret; u32 reg, f0_stored; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1435,8 +1370,8 @@ static ssize_t f0_stored_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1460,11 +1395,9 @@ static ssize_t f0_stored_store(struct device *dev, f0_stored > CS40L26_F0_EST_MAX) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1480,8 +1413,8 @@ static ssize_t f0_stored_store(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1497,11 +1430,9 @@ static ssize_t q_stored_show(struct device *dev, struct device_attribute *attr, int ret; u32 reg, q_stored; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1517,8 +1448,8 @@ static ssize_t q_stored_show(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1540,11 +1471,9 @@ static ssize_t q_stored_store(struct device *dev, struct device_attribute *attr, if (ret || q_stored < CS40L26_Q_EST_MIN || q_stored > CS40L26_Q_EST_MAX) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1560,8 +1489,8 @@ static ssize_t q_stored_store(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1577,11 +1506,9 @@ static ssize_t redc_stored_show(struct device *dev, int ret; u32 reg, redc_stored; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1597,8 +1524,8 @@ static ssize_t redc_stored_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1619,11 +1546,9 @@ static ssize_t redc_stored_store(struct device *dev, if (ret) return ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1639,8 +1564,8 @@ static ssize_t redc_stored_store(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1656,11 +1581,9 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev, 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) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1719,8 +1642,8 @@ static ssize_t f0_and_q_cal_time_ms_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1737,11 +1660,9 @@ static ssize_t redc_cal_time_ms_show(struct device *dev, int ret; u32 reg, redc_playtime_ms, redc_total_cal_time_ms; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1759,8 +1680,8 @@ static ssize_t redc_cal_time_ms_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -1776,11 +1697,9 @@ static ssize_t logging_en_show(struct device *dev, u32 reg, enable; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1797,8 +1716,8 @@ static ssize_t logging_en_show(struct device *dev, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -1820,11 +1739,9 @@ static ssize_t logging_en_store(struct device *dev, if (enable != 0 && enable != 1) return -EINVAL; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) { - cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -1845,13 +1762,6 @@ static ssize_t logging_en_store(struct device *dev, goto exit_mutex; if (cs40l26->fw_id == CS40L26_FW_ID) { - if (src_count != CS40L26_LOGGER_SRC_COUNT) { - dev_err(cdev, "Unexpected source count %u\n", - src_count); - ret = -EINVAL; - goto exit_mutex; - } - ret = regmap_read(regmap, reg, &src); if (ret) { dev_err(cdev, "Failed to get Logger Source\n"); @@ -1867,13 +1777,6 @@ static ssize_t logging_en_store(struct device *dev, goto exit_mutex; } } else if (cs40l26->fw_id == CS40L26_FW_CALIB_ID) { - if (src_count != CS40L26_LOGGER_SRC_COUNT_CALIB) { - dev_err(cdev, "Unexpected source count %u\n", - src_count); - ret = -EINVAL; - goto exit_mutex; - } - for (i = 0; i < src_count; i++) { ret = regmap_read(regmap, reg + (i * CL_DSP_BYTES_PER_WORD), &src); @@ -1910,9 +1813,8 @@ static ssize_t logging_en_store(struct device *dev, exit_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + cs40l26_pm_exit(cdev); return ret ? ret : count; } @@ -1933,17 +1835,14 @@ static ssize_t logging_max_reset_store(struct device *dev, if (rst != 1) return -EINVAL; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_DSP_MBOX_CMD_LOGGER_MAX_RESET, CS40L26_DSP_MBOX_RESET); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return count; } @@ -1962,7 +1861,10 @@ static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr, return -EPERM; } - pm_runtime_get_sync(cs40l26->dev); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return ret; + mutex_lock(&cs40l26->lock); ret = cl_dsp_get_reg(cs40l26->dsp, "DATA", CL_DSP_XM_UNPACKED_TYPE, @@ -1977,8 +1879,8 @@ static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -2000,7 +1902,10 @@ static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr, return -EPERM; } - pm_runtime_get_sync(cs40l26->dev); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return ret; + mutex_lock(&cs40l26->lock); ret = cl_dsp_get_reg(cs40l26->dsp, "DATA", CL_DSP_XM_UNPACKED_TYPE, @@ -2015,8 +1920,8 @@ static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -2039,7 +1944,10 @@ static ssize_t max_vmon_show(struct device *dev, struct device_attribute *attr, else offset = CS40L26_LOGGER_DATA_1_MAX_OFFSET; - pm_runtime_get_sync(cs40l26->dev); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return ret; + mutex_lock(&cs40l26->lock); ret = cl_dsp_get_reg(cs40l26->dsp, "DATA", CL_DSP_XM_UNPACKED_TYPE, @@ -2053,8 +1961,8 @@ static ssize_t max_vmon_show(struct device *dev, struct device_attribute *attr, err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -2070,14 +1978,17 @@ static ssize_t svc_le_est_show(struct device *dev, unsigned int le; int ret; - pm_runtime_get_sync(cs40l26->dev); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return ret; + mutex_lock(&cs40l26->lock); ret = cs40l26_svc_le_estimate(cs40l26, &le); mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + + cs40l26_pm_exit(cs40l26->dev); if (ret) return ret; @@ -2086,8 +1997,45 @@ static ssize_t svc_le_est_show(struct device *dev, } static DEVICE_ATTR_RO(svc_le_est); +static ssize_t svc_le_stored_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + int ret; + + mutex_lock(&cs40l26->lock); + + ret = snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->svc_le_est_stored); + + mutex_unlock(&cs40l26->lock); + + return ret; +} + +static ssize_t svc_le_stored_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + int ret; + u32 svc_le_stored; + + ret = kstrtou32(buf, 10, &svc_le_stored); + if (ret) + return ret; + + mutex_lock(&cs40l26->lock); + + cs40l26->svc_le_est_stored = svc_le_stored; + + mutex_unlock(&cs40l26->lock); + + return count; +} +static DEVICE_ATTR_RW(svc_le_stored); + static struct attribute *cs40l26_dev_attrs_cal[] = { &dev_attr_svc_le_est.attr, + &dev_attr_svc_le_stored.attr, &dev_attr_max_vbst.attr, &dev_attr_max_bemf.attr, &dev_attr_max_vmon.attr, diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c index c1de6c4..a217f9a 100644 --- a/cs40l26/cs40l26-tables.c +++ b/cs40l26/cs40l26-tables.c @@ -3,7 +3,7 @@ // cs40l26-tables.c -- CS40L26 Boosted Haptic Driver with Integrated DSP and // Waveform Memory with Advanced Closed Loop Algorithms and LRA protection // -// Copyright 2021 Cirrus Logic, Inc. +// Copyright 2022 Cirrus Logic, Inc. // // Author: Fred Treven <fred.treven@cirrus.com> // diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index 39f5253..0cbb8e3 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -3,7 +3,7 @@ // cs40l26.c -- CS40L26 Boosted Haptic Driver with Integrated DSP and // Waveform Memory with Advanced Closed Loop Algorithms and LRA protection // -// Copyright 2021 Cirrus Logic, Inc. +// Copyright 2022 Cirrus Logic, Inc. // // Author: Fred Treven <fred.treven@cirrus.com> // @@ -212,11 +212,9 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, unsigned int reg; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -232,8 +230,7 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, err_pm: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -540,7 +537,6 @@ static int cs40l26_dsp_start(struct cs40l26_private *cs40l26) unsigned int val; int ret; - ret = regmap_read(cs40l26->regmap, CS40L26_A1_DSP_REQ_ACTIVE_REG, &val); if (ret) { @@ -756,8 +752,7 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) cs40l26->cal_requested &= ~CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q; /* for pm_runtime_get see trigger_calibration */ - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } else { dev_err(dev, "Unexpected mbox msg: %d", val); return -EINVAL; @@ -774,8 +769,7 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) cs40l26->cal_requested &= ~CS40L26_CALIBRATION_CONTROL_REQUEST_REDC; /* for pm_runtime_get see trigger_calibration */ - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } else { dev_err(dev, "Unexpected mbox msg: %d", val); return -EINVAL; @@ -802,8 +796,6 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) int cs40l26_asp_start(struct cs40l26_private *cs40l26) { - bool ack = false; - unsigned int val; int ret; if (cs40l26->pdata.asp_scale_pct < CS40L26_GAIN_FULL_SCALE) @@ -819,29 +811,8 @@ int cs40l26_asp_start(struct cs40l26_private *cs40l26) reinit_completion(&cs40l26->i2s_cont); - ret = regmap_write(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, - CS40L26_DSP_MBOX_CMD_START_I2S); - if (ret) { - dev_err(cs40l26->dev, "Failed to start I2S\n"); - return ret; - } - - while (!ack) { - usleep_range(CS40L26_DSP_TIMEOUT_US_MIN, - CS40L26_DSP_TIMEOUT_US_MAX); - - ret = regmap_read(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, - &val); - if (ret) { - dev_err(cs40l26->dev, "Failed to read from MBOX_1\n"); - return ret; - } - - if (val == CS40L26_DSP_MBOX_RESET) - ack = true; - } - - return 0; + return cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET); } EXPORT_SYMBOL(cs40l26_asp_start); @@ -854,18 +825,20 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, return; } + dev_dbg(cs40l26->dev, "effects_in_flight = %d\n", + cs40l26->effects_in_flight); + switch (event) { case CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK: case CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER: cs40l26_remove_asp_scaling(cs40l26); - cs40l26->effects_in_flight++; + cs40l26->effects_in_flight = cs40l26->effects_in_flight <= 0 ? 1 : + cs40l26->effects_in_flight + 1; break; case CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE: case CS40L26_VIBE_STATE_EVENT_GPIO_COMPLETE: - cs40l26->effects_in_flight--; - if (cs40l26->effects_in_flight < 0) - dev_err(cs40l26->dev, "effects_in_flight < 0, %d\n", - cs40l26->effects_in_flight); + cs40l26->effects_in_flight = cs40l26->effects_in_flight <= 0 ? 0 : + cs40l26->effects_in_flight - 1; if (cs40l26->effects_in_flight == 0 && cs40l26->asp_enable) if (cs40l26_asp_start(cs40l26)) return; @@ -1317,8 +1290,8 @@ static irqreturn_t cs40l26_irq(int irq, void *data) err: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); + /* if an error has occurred, all IRQs have not been successfully * processed; however, IRQ_HANDLED is still returned if at least one * interrupt request generated by CS40L26 was handled successfully. @@ -1774,18 +1747,15 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, (ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) | (owt << CS40L26_BTN_OWT_SHIFT); - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = regmap_write(cs40l26->regmap, reg, write_val); if (ret) dev_err(cs40l26->dev, "Failed to update event map\n"); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -1797,7 +1767,7 @@ static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26, if (list_empty(&cs40l26->owt_head)) { dev_err(cs40l26->dev, "OWT list is empty\n"); - return NULL; + return ERR_PTR(-EINVAL); } list_for_each_entry(owt, &cs40l26->owt_head, list) { @@ -1807,7 +1777,7 @@ static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26, if (owt->effect_id != id) { dev_err(cs40l26->dev, "OWT effect with ID %d not found\n", id); - return NULL; + return ERR_PTR(-EINVAL); } return owt; @@ -1821,9 +1791,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work) u32 reg; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -1851,8 +1821,7 @@ static void cs40l26_set_gain_worker(struct work_struct *work) err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } static void cs40l26_vibe_start_worker(struct work_struct *work) @@ -1870,9 +1839,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) dev_dbg(dev, "%s\n", __func__); - ret = pm_runtime_get_sync(dev); - if (ret < 0) - return cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -1884,8 +1853,8 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) if (is_owt(index)) { owt = cs40l26_owt_find(cs40l26, effect->id); - if (owt == NULL) { - ret = -ENOMEM; + if (IS_ERR(owt)) { + ret = PTR_ERR(owt); goto err_mutex; } } @@ -1948,8 +1917,8 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) reinit_completion(&cs40l26->erase_cont); err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + + cs40l26_pm_exit(dev); } static void cs40l26_vibe_stop_worker(struct work_struct *work) @@ -1960,9 +1929,9 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) dev_dbg(cs40l26->dev, "%s\n", __func__); - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; /* wait for SVC init phase to complete */ if (cs40l26->delay_before_stop_playback_us) @@ -1971,8 +1940,11 @@ 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) { + dev_warn(cs40l26->dev, "Attempted stop when vibe_state = %d\n", + cs40l26->vibe_state); goto mutex_exit; + } ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); @@ -1983,8 +1955,7 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) mutex_exit: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } static void cs40l26_set_gain(struct input_dev *dev, u16 gain) @@ -2059,28 +2030,28 @@ EXPORT_SYMBOL(cs40l26_get_num_waves); static struct cl_dsp_owt_header *cs40l26_header(struct cs40l26_private *cs40l26, u8 index) { - if (!cs40l26->dsp || !cs40l26->dsp->wt_desc) - return NULL; - - if (index >= cs40l26->dsp->wt_desc->owt.nwaves) - return NULL; + if (!cs40l26->dsp || !cs40l26->dsp->wt_desc || + index >= cs40l26->dsp->wt_desc->owt.nwaves) + return ERR_PTR(-EINVAL); return &cs40l26->dsp->wt_desc->owt.waves[index]; - } -static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) +static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, + u8 index, u32 *wlen_whole) { struct device *dev = cs40l26->dev; struct cl_dsp_owt_header *entry; struct cl_dsp_memchunk ch; - if (index == 0) + if (index == 0) { + *wlen_whole = 0; return 0; + } entry = cs40l26_header(cs40l26, index); - if (entry == NULL) - return -EINVAL; + if (IS_ERR(entry)) + return PTR_ERR(entry); switch (entry->type) { case WT_TYPE_V6_PCM_F0_REDC: @@ -2095,7 +2066,7 @@ static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) ch = cl_dsp_memchunk_create(entry->data, sizeof(u32)); /* First 24 bits of each waveform is the length in samples @ 8 kHz */ - return cl_dsp_memchunk_read(&ch, 24); + return cl_dsp_memchunk_read(cs40l26->dsp, &ch, 24, wlen_whole); } static void cs40l26_owt_set_section_info(struct cs40l26_private *cs40l26, @@ -2118,35 +2089,63 @@ static void cs40l26_owt_set_section_info(struct cs40l26_private *cs40l26, } } -static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, +static int cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, struct cl_dsp_memchunk *ch, struct cs40l26_owt_section *sections, u8 nsections) { - int i; + int ret = 0, i; for (i = 0; i < nsections; i++) { - sections[i].amplitude = cl_dsp_memchunk_read(ch, 8); - sections[i].index = cl_dsp_memchunk_read(ch, 8); - sections[i].repeat = cl_dsp_memchunk_read(ch, 8); - sections[i].flags = cl_dsp_memchunk_read(ch, 8); - sections[i].delay = cl_dsp_memchunk_read(ch, 16); + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].amplitude); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].index); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].repeat); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].flags); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 16, + §ions[i].delay); + if (ret) + return ret; if (sections[i].flags & CS40L26_WT_TYPE10_COMP_DURATION_FLAG) { - cl_dsp_memchunk_read(ch, 8); /* Skip padding */ - sections[i].duration = cl_dsp_memchunk_read(ch, 16); + /* Skip padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, NULL); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 16, + §ions[i].duration); + if (ret) + return ret; } } + + return ret; } static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, u8 nsections, u8 global_rep, u8 *data, u32 data_size_bytes, u32 *owt_wlen) { - struct cl_dsp_memchunk ch; - u32 total_len = 0, section_len = 0, loop_len = 0; + u32 total_len = 0, section_len = 0, loop_len = 0, wlen_whole = 0; bool in_loop = false; + int ret = 0, i; struct cs40l26_owt_section *sections; - int ret = 0, i, wlen_whole; + struct cl_dsp_memchunk ch; u32 dlen, wlen; if (nsections < 1) { @@ -2160,16 +2159,19 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, return -ENOMEM; ch = cl_dsp_memchunk_create((void *) data, data_size_bytes); - cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + goto err_free; + } for (i = 0; i < nsections; i++) { - wlen_whole = cs40l26_owt_get_wlength(cs40l26, - sections[i].index); - if (wlen_whole < 0) { + ret = cs40l26_owt_get_wlength(cs40l26, sections[i].index, + &wlen_whole); + if (ret < 0) { dev_err(cs40l26->dev, "Failed to get wlength for index %u\n", sections[i].index); - ret = wlen_whole; goto err_free; } @@ -2248,11 +2250,9 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(dsp, "OWT_NEXT_XM", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -2305,8 +2305,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, data_size_bytes, write_reg); err_pm: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -2318,21 +2317,26 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp, struct cl_dsp_memchunk in_ch, out_ch; u16 amp_product; u8 *out_data; - int i; + int i, ret; if (nsections <= 0) { dev_err(cs40l26->dev, "Too few sections for NCW\n"); - return NULL; + return ERR_PTR(-EINVAL); } sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), GFP_KERNEL); if (!sections) - return NULL; + return ERR_PTR(-ENOMEM); in_ch = cl_dsp_memchunk_create(in_data, data_bytes); - cs40l26_owt_get_section_info(cs40l26, &in_ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &in_ch, sections, + nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + return ERR_PTR(ret); + } for (i = 0; i < nsections; i++) { if (sections[i].index != 0) { @@ -2343,16 +2347,14 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp, } out_data = kcalloc(data_bytes, sizeof(u8), GFP_KERNEL); - if (!out_data) - goto sections_free; + if (!out_data) { + kfree(sections); + return ERR_PTR(-ENOMEM); + } out_ch = cl_dsp_memchunk_create((void *) out_data, data_bytes); cs40l26_owt_set_section_info(cs40l26, &out_ch, sections, nsections); - -sections_free: - kfree(sections); - return out_data; } @@ -2369,8 +2371,8 @@ static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26, } header = cs40l26_header(cs40l26, sections[i].index); - if (header == NULL) - return -ENOMEM; + if (IS_ERR(header)) + return PTR_ERR(header); if (header->type == WT_TYPE_V6_COMPOSITE) { size += (header->size - 2) * 4; @@ -2388,7 +2390,8 @@ 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, bool svc_waveform, 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; @@ -2416,12 +2419,15 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, out_data_bytes); cl_dsp_memchunk_write(&out_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS | - (svc_waveform ? CS40L26_OWT_SVC_METADATA : 0)); + (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 + - (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0)); + (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)); + (svc_waveform ? + CS40L26_WT_METADATA_OFFSET : 0)); memcpy(*out_data + out_ch.bytes, in_data, in_data_bytes); @@ -2430,21 +2436,29 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, } ch = cl_dsp_memchunk_create((void *) in_data, in_data_bytes); - cl_dsp_memchunk_read(&ch, 8); /* Skip padding */ - ret = cl_dsp_memchunk_read(&ch, 8); - if (ret < 0) + /* Skip padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, NULL); + if (ret) return ret; - nsections = ret; + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, &nsections); + if (ret) + return ret; - global_rep = cl_dsp_memchunk_read(&ch, 8); + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, &global_rep); + if (ret) + return ret; sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), GFP_KERNEL); if (!sections) return -ENOMEM; - cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + return ret; + } data_bytes = cs40l26_owt_comp_data_size(cs40l26, nsections, sections); @@ -2484,18 +2498,33 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, } header = cs40l26_header(cs40l26, sections[i].index); - if (header == NULL) { - ret = -ENOMEM; + if (IS_ERR(header)) { + ret = PTR_ERR(header); goto data_err_free; } if (header->type == WT_TYPE_V6_COMPOSITE) { ch = cl_dsp_memchunk_create(header->data, 8); - cl_dsp_memchunk_read(&ch, 24); /* Skip Wlength */ - cl_dsp_memchunk_read(&ch, 8); /* Skip Padding */ - ncw_nsections = cl_dsp_memchunk_read(&ch, 8); + /* Skip Wlength */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 24, NULL); + if (ret) + return ret; + + /* Skip Padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, NULL); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, + &ncw_nsections); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, + &ncw_global_rep); + if (ret) + return ret; - ncw_global_rep = cl_dsp_memchunk_read(&ch, 8); if (ncw_global_rep != 0) { dev_err(dev, "No NCW support for outer repeat\n"); @@ -2509,8 +2538,8 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, ncw_data = cs40l26_ncw_amp_scaling(cs40l26, sections[i].amplitude, ncw_nsections, header->data + 8, ncw_bytes); - if (ncw_data == NULL) { - ret = -ENOMEM; + if (IS_ERR(ncw_data)) { + ret = PTR_ERR(ncw_data); goto data_err_free; } @@ -2678,9 +2707,9 @@ static void cs40l26_upload_worker(struct work_struct *work) u16 index, bank; bool pwle, svc_waveform; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) - return cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -2692,6 +2721,12 @@ static void cs40l26_upload_worker(struct work_struct *work) goto out_mutex; } + if (is_owt(cs40l26->trigger_indices[effect->id])) { + dev_err(cdev, "Open Wavetable effects cannot be edited\n"); + ret = -EPERM; + goto out_mutex; + } + switch (effect->u.periodic.waveform) { case FF_CUSTOM: pwle = (cs40l26->raw_custom_data[0] == @@ -2703,8 +2738,9 @@ static void cs40l26_upload_worker(struct work_struct *work) if (len > CS40L26_CUSTOM_DATA_SIZE) { refactored_size = cs40l26_refactor_owt(cs40l26, - cs40l26->raw_custom_data, len, pwle, svc_waveform, - &refactored_data); + cs40l26->raw_custom_data, len, + pwle, svc_waveform, + &refactored_data); if (refactored_size <= 0) { dev_err(cdev, @@ -2810,8 +2846,8 @@ out_free: out_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + + cs40l26_pm_exit(cdev); cs40l26->upload_ret = ret; } @@ -2856,6 +2892,7 @@ static int cs40l26_upload_effect(struct input_dev *dev, out_free: memset(&cs40l26->upload_effect, 0, sizeof(struct ff_effect)); kfree(cs40l26->raw_custom_data); + cs40l26->raw_custom_data = NULL; return ret; } @@ -2932,8 +2969,8 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) int ret; owt = cs40l26_owt_find(cs40l26, effect_id); - if (owt == NULL) - return -ENOMEM; + if (IS_ERR(owt)) + return PTR_ERR(owt); cmd |= (owt->trigger_index & 0xFF); @@ -2951,6 +2988,8 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) } } + cs40l26->trigger_indices[effect_id] = 0; + list_del(&owt->list); kfree(owt); cs40l26->num_owt_effects--; @@ -2958,7 +2997,6 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) return 0; } - static void cs40l26_erase_worker(struct work_struct *work) { struct cs40l26_private *cs40l26 = container_of(work, @@ -2968,9 +3006,9 @@ static void cs40l26_erase_worker(struct work_struct *work) u16 duration; u32 index; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -3018,8 +3056,7 @@ static void cs40l26_erase_worker(struct work_struct *work) out_mutex: mutex_unlock(&cs40l26->lock); pm_err: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); cs40l26->erase_ret = ret; } @@ -3793,13 +3830,6 @@ static int cs40l26_handle_errata(struct cs40l26_private *cs40l26) num_writes = CS40L26_ERRATA_A1_EXPL_EN_NUM_WRITES; } - ret = regmap_register_patch(cs40l26->regmap, cs40l26_a1_errata, - num_writes); - if (ret) { - dev_err(cs40l26->dev, "Failed to patch A1 errata\n"); - return ret; - } - return cs40l26_pseq_multi_write(cs40l26, cs40l26_a1_errata, num_writes, false, CS40L26_PSEQ_OP_WRITE_FULL); } @@ -3963,11 +3993,9 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -4011,8 +4039,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) } pm_err: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -4051,7 +4078,8 @@ int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le) return ret; ret = cl_dsp_get_reg(cs40l26->dsp, "LE_EST_STATUS", - CL_DSP_YM_UNPACKED_TYPE, CS40l26_SVC_ALGO_ID, ®); + CL_DSP_YM_UNPACKED_TYPE, CS40L26_SVC_ALGO_ID, + ®); if (ret) return ret; @@ -4077,20 +4105,10 @@ int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le) EXPORT_SYMBOL(cs40l26_svc_le_estimate); static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, - u32 *tuning_num) + unsigned int le, u32 *tuning_num) { - unsigned int le; - int ret, i; - - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); - return ret; - } - - ret = cs40l26_svc_le_estimate(cs40l26, &le); - if (ret) - goto pm_err; + int ret = 0; + int i; if (le) { for (i = 0; i < cs40l26->num_svc_le_vals; i++) { @@ -4108,10 +4126,6 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, if (!le || i == cs40l26->num_svc_le_vals) dev_warn(cs40l26->dev, "Using default tunings\n"); -pm_err: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); - return ret; } @@ -4125,7 +4139,7 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, coeff_files = kcalloc(n, sizeof(char *), GFP_KERNEL); if (!coeff_files) - return NULL; + return ERR_PTR(-ENOMEM); for (i = 0; i < n; i++) { coeff_files[i] = @@ -4157,16 +4171,15 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, CS40L26_TUNING_FILE_NAME_MAX_LEN); strscpy(coeff_files[0], wt_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN); + strscpy(coeff_files[1], svc_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, + strscpy(coeff_files[2], CS40L26_A2H_TUNING_FILE_NAME, 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, + strscpy(coeff_files[2], CS40L26_CALIB_BIN_FILE_NAME, CS40L26_TUNING_FILE_NAME_MAX_LEN); } @@ -4174,7 +4187,7 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, err_free: kfree(coeff_files); - return NULL; + return ERR_PTR(-ENOMEM); } static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning) @@ -4188,8 +4201,8 @@ static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning) CS40L26_TUNING_FILES_RUNTIME : CS40L26_TUNING_FILES_CAL; coeff_files = cs40l26_get_tuning_names(cs40l26, num_files, tuning); - if (!coeff_files) - return -ENOMEM; + if (IS_ERR(coeff_files)) + return PTR_ERR(coeff_files); for (i = 0; i < num_files; i++) { ret = request_firmware(&coeff, coeff_files[i], dev); @@ -4306,8 +4319,8 @@ static int cs40l26_cl_dsp_reinit(struct cs40l26_private *cs40l26) } cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap); - if (!cs40l26->dsp) - return -ENOMEM; + if (IS_ERR(cs40l26->dsp)) + return PTR_ERR(cs40l26->dsp); return cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); @@ -4317,9 +4330,10 @@ 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; + u32 rev, branch, tuning_num = 0; const struct firmware *fw; int ret; + unsigned int le = 0; cs40l26->fw_loaded = false; @@ -4355,17 +4369,47 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) return ret; if (svc_le_required) { - ret = cs40l26_dsp_config(cs40l26); + ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev); if (ret) return ret; - ret = cs40l26_tuning_select_from_svc_le(cs40l26, &tuning_num); - if (ret) - return ret; + branch = CL_DSP_GET_MAJOR(rev); - cs40l26_pm_runtime_teardown(cs40l26); + switch (branch) { + case CS40L26_FW_MAINT_BRANCH: + ret = cs40l26_dsp_config(cs40l26); + if (ret) + return ret; + + ret = cs40l26_pm_enter(dev); + if (ret) + return ret; + + ret = cs40l26_svc_le_estimate(cs40l26, &le); + if (ret) + dev_warn(dev, "svc_le est failed, %d", ret); + + cs40l26_pm_exit(dev); + + cs40l26_pm_runtime_teardown(cs40l26); + + ret = cs40l26_dsp_pre_config(cs40l26); + if (ret) + return ret; + + break; + + case CS40L26_FW_BRANCH: + le = cs40l26->svc_le_est_stored; + break; + + default: + dev_err(dev, "Invalid firmware branch, %d", branch); + return -EINVAL; + } - ret = cs40l26_dsp_pre_config(cs40l26); + ret = cs40l26_tuning_select_from_svc_le(cs40l26, + le, &tuning_num); if (ret) return ret; } @@ -4377,35 +4421,30 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) return cs40l26_dsp_config(cs40l26); } -int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) +int cs40l26_fw_swap(struct cs40l26_private *cs40l26, const u32 id) { struct device *dev = cs40l26->dev; - bool deferred = cs40l26->fw_defer; - u32 pseq_rom_end_of_script_loc; - int ret; - - if (cs40l26->fw_defer) { - cs40l26->fw_defer = false; - } else { - disable_irq(cs40l26->irq); - cs40l26_pm_runtime_teardown(cs40l26); - } + bool re_enable = false; + int ret = 0; if (cs40l26->revid != CS40L26_REVID_A1 && cs40l26->revid != CS40L26_REVID_B0) { dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid); - ret = -EINVAL; - goto defer_err; + return -EINVAL; } - /* reset pseq END_OF_SCRIPT to location from ROM */ - pseq_rom_end_of_script_loc = CS40L26_PSEQ_ROM_END_OF_SCRIPT; + if (cs40l26->fw_loaded) { + disable_irq(cs40l26->irq); + cs40l26_pm_runtime_teardown(cs40l26); + re_enable = true; + } - ret = cs40l26_dsp_write(cs40l26, pseq_rom_end_of_script_loc, + /* reset pseq END_OF_SCRIPT to location from ROM */ + ret = cs40l26_dsp_write(cs40l26, CS40L26_PSEQ_ROM_END_OF_SCRIPT, CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT); if (ret) { dev_err(dev, "Failed to reset pseq END_OF_SCRIPT %d\n", ret); - goto defer_err; + return ret; } if (id == CS40L26_FW_CALIB_ID) @@ -4415,42 +4454,28 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) ret = cs40l26_fw_upload(cs40l26); if (ret) - goto defer_err; + return ret; - if (deferred) { + if (cs40l26->fw_defer && cs40l26->fw_loaded) { 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); - goto defer_err; + return ret; } - } else { - enable_irq(cs40l26->irq); - } - return 0; + cs40l26->fw_defer = false; + } -defer_err: - if (deferred) - cs40l26->fw_defer = true; + if (re_enable) + enable_irq(cs40l26->irq); return ret; } EXPORT_SYMBOL(cs40l26_fw_swap); -static int cs40l26_update_reg_defaults(struct cs40l26_private *cs40l26) -{ - int ret; - - ret = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG, - CS40L26_SPK_DEFAULT_HIZ_MASK, 1 << - CS40L26_SPK_DEFAULT_HIZ_SHIFT); - - return ret; -} - static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) { struct device *dev = cs40l26->dev; @@ -4669,7 +4694,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) } if (!of_property_read_u32(np, "cirrus,boost-ctl-microvolt", &val)) - cs40l26->pdata.boost_ctl = val | CS40L26_PDATA_PRESENT; + cs40l26->pdata.boost_ctl = val; else cs40l26->pdata.boost_ctl = CS40L26_BST_CTL_DEFAULT; @@ -4815,9 +4840,12 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, if (ret) goto err; - ret = cs40l26_update_reg_defaults(cs40l26); + /* Set LRA to high-z to avoid fault conditions */ + ret = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG, + CS40L26_SPK_DEFAULT_HIZ_MASK, 1 << + CS40L26_SPK_DEFAULT_HIZ_SHIFT); if (ret) { - dev_err(dev, "Failed to update reg defaults\n"); + dev_err(dev, "Failed to set LRA to HI-Z\n"); goto err; } @@ -4911,6 +4939,27 @@ int cs40l26_remove(struct cs40l26_private *cs40l26) } EXPORT_SYMBOL(cs40l26_remove); +int cs40l26_pm_enter(struct device *dev) +{ + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(cs40l26_pm_enter); + +void cs40l26_pm_exit(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL(cs40l26_pm_exit); + int cs40l26_suspend(struct device *dev) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -4958,8 +5007,7 @@ void cs40l26_resume_error_handle(struct device *dev, int ret) pm_runtime_set_active(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); } EXPORT_SYMBOL(cs40l26_resume_error_handle); diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index c61e4de..17e675f 100644 --- a/cs40l26/cs40l26.h +++ b/cs40l26/cs40l26.h @@ -3,7 +3,7 @@ * cs40l26.h -- CS40L26 Boosted Haptic Driver with Integrated DSP and * Waveform Memory with Advanced Closed Loop Algorithms and LRA protection * - * Copyright 2021 Cirrus Logic, Inc. + * Copyright 2022 Cirrus Logic, Inc. * * Author: Fred Treven <fred.treven@cirrus.com> */ @@ -703,7 +703,7 @@ #define CS40L26_MAILBOX_ALGO_ID 0x0001F203 #define CS40L26_MDSYNC_ALGO_ID 0x0001F20F #define CS40L26_PM_ALGO_ID 0x0001F206 -#define CS40l26_SVC_ALGO_ID 0x0001F207 +#define CS40L26_SVC_ALGO_ID 0x0001F207 #define CS40L26_VIBEGEN_ALGO_ID 0x000100BD #define CS40L26_LOGGER_ALGO_ID 0x0004013D #define CS40L26_EXT_ALGO_ID 0x0004013C @@ -837,7 +837,7 @@ #define CS40L26_FW_CALIB_NAME "cs40l26-calib.wmfw" #define CS40L26_TUNING_FILES_RUNTIME 4 -#define CS40L26_TUNING_FILES_CAL 2 +#define CS40L26_TUNING_FILES_CAL 3 #define CS40L26_WT_FILE_NAME "cs40l26.bin" #define CS40L26_WT_FILE_PREFIX "cs40l26-wt" @@ -1171,9 +1171,6 @@ #define CS40L26_A2H_DELAY_MAX 0x190 -#define CS40L26_PDATA_PRESENT 0x80000000 -#define CS40L26_PDATA_MASK ~CS40L26_PDATA_PRESENT - #define CS40L26_VMON_DEC_OUT_DATA_MASK GENMASK(23, 0) #define CS40L26_VMON_OVFL_FLAG_MASK BIT(31) #define CS40L26_VMON_DEC_OUT_DATA_MAX CS40L26_VMON_DEC_OUT_DATA_MASK @@ -1522,6 +1519,7 @@ struct cs40l26_private { struct completion i2s_cont; struct completion erase_cont; u8 vpbr_thld; + unsigned int svc_le_est_stored; }; struct cs40l26_codec { @@ -1555,7 +1553,7 @@ int cs40l26_dbc_set(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, 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); +int cs40l26_fw_swap(struct cs40l26_private *cs40l26, const u32 id); void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, enum cs40l26_vibe_state_event event); int cs40l26_pm_stdby_timeout_ms_get(struct cs40l26_private *cs40l26, @@ -1570,6 +1568,8 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, enum cs40l26_pm_state state); int cs40l26_ack_write(struct cs40l26_private *cs40l26, u32 reg, u32 write_val, u32 reset_val); +int cs40l26_pm_enter(struct device *dev); +void cs40l26_pm_exit(struct device *dev); void cs40l26_resume_error_handle(struct device *dev, int ret); int cs40l26_resume(struct device *dev); int cs40l26_sys_resume(struct device *dev); |