diff options
author | PixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com> | 2022-10-30 18:59:59 -0700 |
---|---|---|
committer | SecurityBot <android-nexus-securitybot@system.gserviceaccount.com> | 2022-10-30 18:59:59 -0700 |
commit | 0dd8c419639d9b1c02a65507fcb992435606ee14 (patch) | |
tree | 711b841c749dc2ba106ff0fd6c1443ef3ca8348a | |
parent | ad41b7b82d645108e3082a087fd412a289497852 (diff) | |
parent | 5f2484108386af73566a0efc2d7ef720ab392975 (diff) | |
download | amplifiers-0dd8c419639d9b1c02a65507fcb992435606ee14.tar.gz |
Merge android13-gs-pixel-5.10-tm-qpr2 into android13-gs-pixel-5.10-udc
SBMerger: 478053055
Change-Id: I2ae72a1dacfbc4061304f626ef5cc3d071f2f928
Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-rw-r--r-- | cs40l26/Makefile | 1 | ||||
-rw-r--r-- | cs40l26/cl_dsp.c | 213 | ||||
-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 | 677 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 52 |
10 files changed, 817 insertions, 770 deletions
diff --git a/cs40l26/Makefile b/cs40l26/Makefile index 5642c07..3c4ceaf 100644 --- a/cs40l26/Makefile +++ b/cs40l26/Makefile @@ -21,6 +21,7 @@ KBUILD_OPTIONS += CONFIG_INPUT_CS40L26_I2C=m \ EXTRA_CFLAGS += -DDYNAMIC_DEBUG_MODULE EXTRA_CFLAGS += -DCONFIG_INPUT_CS40L26_ATTR_UNDER_BUS +EXTRA_CFLAGS += -DCONFIG_GOOG_CUST modules modules_install clean: $(MAKE) -C $(KERNEL_SRC) M=$(M) \ diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c index bcbee33..ee6a525 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; @@ -1135,3 +1153,4 @@ EXPORT_SYMBOL(cl_dsp_destroy); MODULE_DESCRIPTION("Cirrus Logic DSP Firmware Driver"); MODULE_AUTHOR("Fred Treven, Cirrus Logic Inc, <fred.treven@cirrus.com>"); MODULE_LICENSE("GPL"); +MODULE_VERSION("3.1.10"); 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..d4d4b3f 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> // @@ -25,18 +25,6 @@ static inline bool is_buzz(unsigned int index) index <= CS40L26_BUZZGEN_INDEX_END); } -static inline bool is_ram(unsigned int index) -{ - return (index >= CS40L26_RAM_INDEX_START && - index <= CS40L26_RAM_INDEX_END); -} - -static inline bool is_rom(unsigned int index) -{ - return (index >= CS40L26_ROM_INDEX_START && - index <= CS40L26_ROM_INDEX_END); -} - static inline bool section_complete(struct cs40l26_owt_section *s) { return s->delay ? true : false; @@ -212,11 +200,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 +218,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 +525,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 +740,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 +757,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 +784,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 +799,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 +813,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 +1278,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. @@ -1736,11 +1697,12 @@ static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq, static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, u32 index, u8 bank, struct ff_effect *effect) { - int ret; - bool edge, ev_handler_bank_ram, owt; - u32 reg, write_val; u16 button = effect->trigger.button; u8 gpio = (button & CS40L26_BTN_NUM_MASK) >> CS40L26_BTN_NUM_SHIFT; + bool edge, ev_handler_bank_ram, owt, use_timeout; + unsigned int fw_rev; + u32 reg, write_val; + int ret; edge = (button & CS40L26_BTN_EDGE_MASK) >> CS40L26_BTN_EDGE_SHIFT; @@ -1774,18 +1736,44 @@ 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) + if (ret) { dev_err(cs40l26->dev, "Failed to update event map\n"); + goto pm_exit; + } - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + ret = cl_dsp_fw_rev_get(cs40l26->dsp, &fw_rev); + if (ret) + goto pm_exit; + + use_timeout = (!cs40l26->calib_fw && + fw_rev >= CS40L26_FW_GPI_TIMEOUT_MIN_REV) || + (cs40l26->calib_fw && fw_rev >= + CS40L26_FW_GPI_TIMEOUT_CALIB_MIN_REV); + + if (use_timeout) { + ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_GPI_MS", + CL_DSP_XM_UNPACKED_TYPE, + CS40L26_VIBEGEN_ALGO_ID, ®); + if (ret) + goto pm_exit; + + ret = regmap_write(cs40l26->regmap, reg, effect->replay.length); + if (ret) + dev_err(cs40l26->dev, "Failed to set GPI timeout\n"); + } + + if (edge) + cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_PRESS] = effect->id; + else + cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_RELEASE] = effect->id; + +pm_exit: + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -1797,7 +1785,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 +1795,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 +1809,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 +1839,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 +1857,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 +1871,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 +1935,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 +1947,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 +1958,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 +1973,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 +2048,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 +2084,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 +2107,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 +2177,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 +2268,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 +2323,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 +2335,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 +2365,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 +2389,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 +2408,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 +2437,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 +2454,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 +2516,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 +2556,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 +2725,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 +2739,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 +2756,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 +2864,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 +2910,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; } @@ -2869,51 +2924,44 @@ const struct attribute_group *cs40l26_dev_attr_groups[] = { }; #endif -static int cs40l26_clear_gpi_event_reg(struct cs40l26_private *cs40l26, u32 reg) +static enum cs40l26_gpio_map cs40l26_map_get(struct cs40l26_private *cs40l26, + int effect_id) { - struct regmap *regmap = cs40l26->regmap; - int ret; + if (cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_PRESS] == effect_id) + return CS40L26_GPIO_MAP_A_PRESS; + else if (cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_RELEASE] == effect_id) + return CS40L26_GPIO_MAP_A_RELEASE; - ret = regmap_write(regmap, reg, CS40L26_EVENT_MAP_GPI_EVENT_DISABLE); - if (ret) - dev_err(cs40l26->dev, "Failed to clear gpi reg: %08X", reg); - - return ret; + return CS40L26_GPIO_MAP_INVALID; } static int cs40l26_erase_gpi_mapping(struct cs40l26_private *cs40l26, - int effect_id) + enum cs40l26_gpio_map mapping) { - struct device *dev = cs40l26->dev; - u32 index = cs40l26->trigger_indices[effect_id]; - u8 trigger_index, gpi_index; - u32 reg, val; - int i, ret; + int ret = 0; + u32 reg; - trigger_index = index & 0xFF; + if (mapping == CS40L26_GPIO_MAP_A_PRESS) + reg = CS40L26_A1_EVENT_MAP_1; + else if (mapping == CS40L26_GPIO_MAP_A_RELEASE) + reg = CS40L26_A1_EVENT_MAP_2; + else + ret = -EINVAL; - for (i = 0; i < CS40L26_EVENT_MAP_NUM_GPI_REGS; i++) { - reg = cs40l26->event_map_base + (i * 4); - ret = regmap_read(cs40l26->regmap, reg, &val); - if (ret) { - dev_err(dev, "Failed to read gpi event reg: 0x%08X", - reg); - return ret; - } - gpi_index = val & 0xFF; - - if (is_buzz(index) || - (is_owt(index) && val & CS40L26_BTN_OWT_MASK) || - (is_ram(index) && val & CS40L26_BTN_BANK_MASK) || - (is_rom(index) && ~val & CS40L26_BTN_BANK_MASK)) { - if (trigger_index == gpi_index) { - ret = cs40l26_clear_gpi_event_reg(cs40l26, reg); - if (ret) - return ret; - } - } + if (ret) { + dev_err(cs40l26->dev, "Invalid GPI mapping %u\n", mapping); + return ret; + } + + ret = regmap_write(cs40l26->regmap, reg, CS40L26_EVENT_MAP_GPI_DISABLE); + if (ret) { + dev_err(cs40l26->dev, "Failed to clear GPI mapping %u\n", + mapping); + return ret; } + cs40l26->gpi_ids[mapping] = -1; + return 0; } @@ -2932,8 +2980,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 +2999,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,19 +3008,19 @@ 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, struct cs40l26_private, erase_work); int ret = 0; + enum cs40l26_gpio_map mapping; int effect_id; 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); @@ -2987,7 +3037,8 @@ static void cs40l26_erase_worker(struct work_struct *work) if (!wait_for_completion_timeout(&cs40l26->erase_cont, msecs_to_jiffies(duration))) { ret = -ETIME; - dev_err(cs40l26->dev, "Failed to erase effect: %d", ret); + dev_err(cs40l26->dev, "Failed to erase effect: %d", + ret); goto pm_err; } mutex_lock(&cs40l26->lock); @@ -2995,31 +3046,25 @@ static void cs40l26_erase_worker(struct work_struct *work) dev_dbg(cs40l26->dev, "%s: effect ID = %d\n", __func__, effect_id); - if (is_owt(index)) { - ret = cs40l26_erase_owt(cs40l26, effect_id); + mapping = cs40l26_map_get(cs40l26, effect_id); + if (mapping != CS40L26_GPIO_MAP_INVALID) { + ret = cs40l26_erase_gpi_mapping(cs40l26, mapping); if (ret) - dev_err(cs40l26->dev, "Failed to erase OWT effect: %d", - ret); - goto out_mutex; + goto out_mutex; } - if (is_buzz(index)) { + if (is_owt(index)) + ret = cs40l26_erase_owt(cs40l26, effect_id); + else if (is_buzz(index)) ret = cs40l26_erase_buzz(cs40l26, effect_id); - if (ret) - dev_err(cs40l26->dev, "Failed to erase buzz effect: %d", - ret); - goto out_mutex; - } - ret = cs40l26_erase_gpi_mapping(cs40l26, effect_id); if (ret) - dev_err(cs40l26->dev, "Failed to erase gpi mapping: %d", ret); + dev_err(cs40l26->dev, "Failed to erase effect: %d", ret); 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 +3838,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 +4001,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 +4047,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 +4086,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 +4113,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 +4134,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 +4147,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 +4179,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 +4195,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 +4209,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 +4327,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 +4338,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 +4377,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); + 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_tuning_select_from_svc_le(cs40l26, + le, &tuning_num); if (ret) return ret; } @@ -4377,35 +4429,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 +4462,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 +4702,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; @@ -4801,23 +4834,26 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, ret = cs40l26_set_pll_loop(cs40l26, CS40L26_PLL_REFCLK_SET_OPEN_LOOP); if (ret) - return ret; + goto err; - ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_1); + ret = cs40l26_erase_gpi_mapping(cs40l26, CS40L26_GPIO_MAP_A_PRESS); if (ret) - return ret; + goto err; - ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_2); + ret = cs40l26_erase_gpi_mapping(cs40l26, CS40L26_GPIO_MAP_A_RELEASE); if (ret) - return ret; + goto err; ret = cs40l26_part_num_resolve(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 +4947,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 +5015,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); @@ -5008,3 +5064,4 @@ EXPORT_SYMBOL(cs40l26_sys_resume_noirq); MODULE_DESCRIPTION("CS40L26 Boosted Mono Class D Amplifier for Haptics"); MODULE_AUTHOR("Fred Treven, Cirrus Logic Inc. <fred.treven@cirrus.com>"); MODULE_LICENSE("GPL"); +MODULE_VERSION("5.6.3"); diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index d7be94f..359abe1 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 @@ -780,7 +780,11 @@ /* DSP mailbox controls */ #define CS40L26_DSP_TIMEOUT_US_MIN 1000 #define CS40L26_DSP_TIMEOUT_US_MAX 1100 +#if IS_ENABLED(CONFIG_GOOG_CUST) +#define CS40L26_DSP_TIMEOUT_COUNT 3 +#else #define CS40L26_DSP_TIMEOUT_COUNT 100 +#endif #define CS40L26_DSP_MBOX_RESET 0x0 @@ -833,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" @@ -852,17 +856,19 @@ #define CS40L26_SVC_LE_MAX_ATTEMPTS 2 #define CS40L26_SVC_DT_PREFIX "svc-le" -#define CS40L26_FW_ID 0x1800D4 -#define CS40L26_FW_MIN_REV 0x07021C -#define CS40L26_FW_BRANCH 0x07 -#define CS40L26_FW_CALIB_ID 0x1800DA -#define CS40L26_FW_CALIB_MIN_REV 0x010014 -#define CS40L26_FW_CALIB_BRANCH 0x01 -#define CS40L26_FW_MAINT_MIN_REV 0x270216 -#define CS40L26_FW_MAINT_BRANCH 0x27 -#define CS40L26_FW_MAINT_CALIB_MIN_REV 0x21010D -#define CS40L26_FW_MAINT_CALIB_BRANCH 0x21 -#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21) +#define CS40L26_FW_ID 0x1800D4 +#define CS40L26_FW_MIN_REV 0x07021C +#define CS40L26_FW_BRANCH 0x07 +#define CS40L26_FW_CALIB_ID 0x1800DA +#define CS40L26_FW_CALIB_MIN_REV 0x010014 +#define CS40L26_FW_CALIB_BRANCH 0x01 +#define CS40L26_FW_MAINT_MIN_REV 0x270216 +#define CS40L26_FW_MAINT_BRANCH 0x27 +#define CS40L26_FW_MAINT_CALIB_MIN_REV 0x21010D +#define CS40L26_FW_MAINT_CALIB_BRANCH 0x21 +#define CS40L26_FW_GPI_TIMEOUT_MIN_REV 0x07022A +#define CS40L26_FW_GPI_TIMEOUT_CALIB_MIN_REV 0x010122 +#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21) #define CS40L26_CCM_CORE_RESET 0x00000200 #define CS40L26_CCM_CORE_ENABLE 0x00000281 @@ -934,7 +940,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_EVENT_MAP_GPI_DISABLE 0x1FF #define CS40L26_BTN_INDEX_MASK GENMASK(7, 0) #define CS40L26_BTN_BUZZ_MASK BIT(7) @@ -1167,9 +1173,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 @@ -1289,6 +1292,13 @@ #define CS40L26_SAMPS_TO_MS(n) ((n) / 8) /* enums */ +enum cs40l26_gpio_map { + CS40L26_GPIO_MAP_A_PRESS, + CS40L26_GPIO_MAP_A_RELEASE, + CS40L26_GPIO_MAP_NUM_AVAILABLE, + CS40L26_GPIO_MAP_INVALID, +}; + enum cs40l26_dbc { CS40L26_DBC_ENV_REL_COEF, /* 0 */ CS40L26_DBC_RISE_HEADROOM, @@ -1472,6 +1482,7 @@ struct cs40l26_private { struct input_dev *input; struct cl_dsp *dsp; unsigned int trigger_indices[FF_MAX_EFFECTS]; + int gpi_ids[CS40L26_GPIO_MAP_NUM_AVAILABLE]; struct ff_effect *trigger_effect; struct ff_effect upload_effect; struct ff_effect *erase_effect; @@ -1518,6 +1529,7 @@ struct cs40l26_private { struct completion i2s_cont; struct completion erase_cont; u8 vpbr_thld; + unsigned int svc_le_est_stored; }; struct cs40l26_codec { @@ -1551,7 +1563,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, @@ -1566,6 +1578,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); |