summaryrefslogtreecommitdiff
path: root/cs40l26/cs40l26.c
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2022-10-04 17:43:27 +0800
committerTai Kuo <taikuo@google.com>2022-10-24 08:21:47 +0000
commita446aef40c377974eb834f781e1b82562d673a4f (patch)
treeef6a010378a5f1f22b4663f85f097f632d18b285 /cs40l26/cs40l26.c
parent4fbab05a66f7e9ed54ef9afac64cdc4a0588a7de (diff)
downloadamplifiers-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
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r--cs40l26/cs40l26.c528
1 files changed, 288 insertions, 240 deletions
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,
+ &sections[i].amplitude);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8,
+ &sections[i].index);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8,
+ &sections[i].repeat);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8,
+ &sections[i].flags);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 16,
+ &sections[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,
+ &sections[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, &reg);
@@ -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, &reg);
@@ -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, &reg);
+ CL_DSP_YM_UNPACKED_TYPE, CS40L26_SVC_ALGO_ID,
+ &reg);
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);