From a446aef40c377974eb834f781e1b82562d673a4f Mon Sep 17 00:00:00 2001 From: Tai Kuo Date: Tue, 4 Oct 2022 17:43:27 +0800 Subject: 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 Change-Id: I262c3d4dd3d5205cc0cd1228f9f67607a86b2cb1 --- cs40l26/cs40l26.c | 528 +++++++++++++++++++++++++++++------------------------- 1 file changed, 288 insertions(+), 240 deletions(-) (limited to 'cs40l26/cs40l26.c') 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 // @@ -212,11 +212,9 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, unsigned int reg; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } mutex_lock(&cs40l26->lock); @@ -232,8 +230,7 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, err_pm: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -540,7 +537,6 @@ static int cs40l26_dsp_start(struct cs40l26_private *cs40l26) unsigned int val; int ret; - ret = regmap_read(cs40l26->regmap, CS40L26_A1_DSP_REQ_ACTIVE_REG, &val); if (ret) { @@ -756,8 +752,7 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) cs40l26->cal_requested &= ~CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q; /* for pm_runtime_get see trigger_calibration */ - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } else { dev_err(dev, "Unexpected mbox msg: %d", val); return -EINVAL; @@ -774,8 +769,7 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) cs40l26->cal_requested &= ~CS40L26_CALIBRATION_CONTROL_REQUEST_REDC; /* for pm_runtime_get see trigger_calibration */ - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } else { dev_err(dev, "Unexpected mbox msg: %d", val); return -EINVAL; @@ -802,8 +796,6 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) int cs40l26_asp_start(struct cs40l26_private *cs40l26) { - bool ack = false; - unsigned int val; int ret; if (cs40l26->pdata.asp_scale_pct < CS40L26_GAIN_FULL_SCALE) @@ -819,29 +811,8 @@ int cs40l26_asp_start(struct cs40l26_private *cs40l26) reinit_completion(&cs40l26->i2s_cont); - ret = regmap_write(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, - CS40L26_DSP_MBOX_CMD_START_I2S); - if (ret) { - dev_err(cs40l26->dev, "Failed to start I2S\n"); - return ret; - } - - while (!ack) { - usleep_range(CS40L26_DSP_TIMEOUT_US_MIN, - CS40L26_DSP_TIMEOUT_US_MAX); - - ret = regmap_read(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, - &val); - if (ret) { - dev_err(cs40l26->dev, "Failed to read from MBOX_1\n"); - return ret; - } - - if (val == CS40L26_DSP_MBOX_RESET) - ack = true; - } - - return 0; + return cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET); } EXPORT_SYMBOL(cs40l26_asp_start); @@ -854,18 +825,20 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, return; } + dev_dbg(cs40l26->dev, "effects_in_flight = %d\n", + cs40l26->effects_in_flight); + switch (event) { case CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK: case CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER: cs40l26_remove_asp_scaling(cs40l26); - cs40l26->effects_in_flight++; + cs40l26->effects_in_flight = cs40l26->effects_in_flight <= 0 ? 1 : + cs40l26->effects_in_flight + 1; break; case CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE: case CS40L26_VIBE_STATE_EVENT_GPIO_COMPLETE: - cs40l26->effects_in_flight--; - if (cs40l26->effects_in_flight < 0) - dev_err(cs40l26->dev, "effects_in_flight < 0, %d\n", - cs40l26->effects_in_flight); + cs40l26->effects_in_flight = cs40l26->effects_in_flight <= 0 ? 0 : + cs40l26->effects_in_flight - 1; if (cs40l26->effects_in_flight == 0 && cs40l26->asp_enable) if (cs40l26_asp_start(cs40l26)) return; @@ -1317,8 +1290,8 @@ static irqreturn_t cs40l26_irq(int irq, void *data) err: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); + /* if an error has occurred, all IRQs have not been successfully * processed; however, IRQ_HANDLED is still returned if at least one * interrupt request generated by CS40L26 was handled successfully. @@ -1774,18 +1747,15 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, (ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) | (owt << CS40L26_BTN_OWT_SHIFT); - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) return ret; - } ret = regmap_write(cs40l26->regmap, reg, write_val); if (ret) dev_err(cs40l26->dev, "Failed to update event map\n"); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); return ret; } @@ -1797,7 +1767,7 @@ static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26, if (list_empty(&cs40l26->owt_head)) { dev_err(cs40l26->dev, "OWT list is empty\n"); - return NULL; + return ERR_PTR(-EINVAL); } list_for_each_entry(owt, &cs40l26->owt_head, list) { @@ -1807,7 +1777,7 @@ static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26, if (owt->effect_id != id) { dev_err(cs40l26->dev, "OWT effect with ID %d not found\n", id); - return NULL; + return ERR_PTR(-EINVAL); } return owt; @@ -1821,9 +1791,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work) u32 reg; int ret; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -1851,8 +1821,7 @@ static void cs40l26_set_gain_worker(struct work_struct *work) err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } static void cs40l26_vibe_start_worker(struct work_struct *work) @@ -1870,9 +1839,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) dev_dbg(dev, "%s\n", __func__); - ret = pm_runtime_get_sync(dev); - if (ret < 0) - return cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -1884,8 +1853,8 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) if (is_owt(index)) { owt = cs40l26_owt_find(cs40l26, effect->id); - if (owt == NULL) { - ret = -ENOMEM; + if (IS_ERR(owt)) { + ret = PTR_ERR(owt); goto err_mutex; } } @@ -1948,8 +1917,8 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) reinit_completion(&cs40l26->erase_cont); err_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + + cs40l26_pm_exit(dev); } static void cs40l26_vibe_stop_worker(struct work_struct *work) @@ -1960,9 +1929,9 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) dev_dbg(cs40l26->dev, "%s\n", __func__); - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; /* wait for SVC init phase to complete */ if (cs40l26->delay_before_stop_playback_us) @@ -1971,8 +1940,11 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) mutex_lock(&cs40l26->lock); - if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) + if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) { + dev_warn(cs40l26->dev, "Attempted stop when vibe_state = %d\n", + cs40l26->vibe_state); goto mutex_exit; + } ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); @@ -1983,8 +1955,7 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) mutex_exit: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); } static void cs40l26_set_gain(struct input_dev *dev, u16 gain) @@ -2059,28 +2030,28 @@ EXPORT_SYMBOL(cs40l26_get_num_waves); static struct cl_dsp_owt_header *cs40l26_header(struct cs40l26_private *cs40l26, u8 index) { - if (!cs40l26->dsp || !cs40l26->dsp->wt_desc) - return NULL; - - if (index >= cs40l26->dsp->wt_desc->owt.nwaves) - return NULL; + if (!cs40l26->dsp || !cs40l26->dsp->wt_desc || + index >= cs40l26->dsp->wt_desc->owt.nwaves) + return ERR_PTR(-EINVAL); return &cs40l26->dsp->wt_desc->owt.waves[index]; - } -static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) +static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, + u8 index, u32 *wlen_whole) { struct device *dev = cs40l26->dev; struct cl_dsp_owt_header *entry; struct cl_dsp_memchunk ch; - if (index == 0) + if (index == 0) { + *wlen_whole = 0; return 0; + } entry = cs40l26_header(cs40l26, index); - if (entry == NULL) - return -EINVAL; + if (IS_ERR(entry)) + return PTR_ERR(entry); switch (entry->type) { case WT_TYPE_V6_PCM_F0_REDC: @@ -2095,7 +2066,7 @@ static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) ch = cl_dsp_memchunk_create(entry->data, sizeof(u32)); /* First 24 bits of each waveform is the length in samples @ 8 kHz */ - return cl_dsp_memchunk_read(&ch, 24); + return cl_dsp_memchunk_read(cs40l26->dsp, &ch, 24, wlen_whole); } static void cs40l26_owt_set_section_info(struct cs40l26_private *cs40l26, @@ -2118,35 +2089,63 @@ static void cs40l26_owt_set_section_info(struct cs40l26_private *cs40l26, } } -static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, +static int cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, struct cl_dsp_memchunk *ch, struct cs40l26_owt_section *sections, u8 nsections) { - int i; + int ret = 0, i; for (i = 0; i < nsections; i++) { - sections[i].amplitude = cl_dsp_memchunk_read(ch, 8); - sections[i].index = cl_dsp_memchunk_read(ch, 8); - sections[i].repeat = cl_dsp_memchunk_read(ch, 8); - sections[i].flags = cl_dsp_memchunk_read(ch, 8); - sections[i].delay = cl_dsp_memchunk_read(ch, 16); + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].amplitude); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].index); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].repeat); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, + §ions[i].flags); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 16, + §ions[i].delay); + if (ret) + return ret; if (sections[i].flags & CS40L26_WT_TYPE10_COMP_DURATION_FLAG) { - cl_dsp_memchunk_read(ch, 8); /* Skip padding */ - sections[i].duration = cl_dsp_memchunk_read(ch, 16); + /* Skip padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 8, NULL); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, ch, 16, + §ions[i].duration); + if (ret) + return ret; } } + + return ret; } static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, u8 nsections, u8 global_rep, u8 *data, u32 data_size_bytes, u32 *owt_wlen) { - struct cl_dsp_memchunk ch; - u32 total_len = 0, section_len = 0, loop_len = 0; + u32 total_len = 0, section_len = 0, loop_len = 0, wlen_whole = 0; bool in_loop = false; + int ret = 0, i; struct cs40l26_owt_section *sections; - int ret = 0, i, wlen_whole; + struct cl_dsp_memchunk ch; u32 dlen, wlen; if (nsections < 1) { @@ -2160,16 +2159,19 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, return -ENOMEM; ch = cl_dsp_memchunk_create((void *) data, data_size_bytes); - cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + goto err_free; + } for (i = 0; i < nsections; i++) { - wlen_whole = cs40l26_owt_get_wlength(cs40l26, - sections[i].index); - if (wlen_whole < 0) { + ret = cs40l26_owt_get_wlength(cs40l26, sections[i].index, + &wlen_whole); + if (ret < 0) { dev_err(cs40l26->dev, "Failed to get wlength for index %u\n", sections[i].index); - ret = wlen_whole; goto err_free; } @@ -2248,11 +2250,9 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(dsp, "OWT_NEXT_XM", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -2305,8 +2305,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, data_size_bytes, write_reg); err_pm: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -2318,21 +2317,26 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp, struct cl_dsp_memchunk in_ch, out_ch; u16 amp_product; u8 *out_data; - int i; + int i, ret; if (nsections <= 0) { dev_err(cs40l26->dev, "Too few sections for NCW\n"); - return NULL; + return ERR_PTR(-EINVAL); } sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), GFP_KERNEL); if (!sections) - return NULL; + return ERR_PTR(-ENOMEM); in_ch = cl_dsp_memchunk_create(in_data, data_bytes); - cs40l26_owt_get_section_info(cs40l26, &in_ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &in_ch, sections, + nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + return ERR_PTR(ret); + } for (i = 0; i < nsections; i++) { if (sections[i].index != 0) { @@ -2343,16 +2347,14 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp, } out_data = kcalloc(data_bytes, sizeof(u8), GFP_KERNEL); - if (!out_data) - goto sections_free; + if (!out_data) { + kfree(sections); + return ERR_PTR(-ENOMEM); + } out_ch = cl_dsp_memchunk_create((void *) out_data, data_bytes); cs40l26_owt_set_section_info(cs40l26, &out_ch, sections, nsections); - -sections_free: - kfree(sections); - return out_data; } @@ -2369,8 +2371,8 @@ static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26, } header = cs40l26_header(cs40l26, sections[i].index); - if (header == NULL) - return -ENOMEM; + if (IS_ERR(header)) + return PTR_ERR(header); if (header->type == WT_TYPE_V6_COMPOSITE) { size += (header->size - 2) * 4; @@ -2388,7 +2390,8 @@ static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26, } static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, - u32 in_data_nibbles, bool pwle, bool svc_waveform, u8 **out_data) + u32 in_data_nibbles, bool pwle, bool svc_waveform, + u8 **out_data) { u8 nsections, global_rep, out_nsections = 0; int ret = 0, pos_byte = 0, in_pos_nib = 2; @@ -2416,12 +2419,15 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, out_data_bytes); cl_dsp_memchunk_write(&out_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS | - (svc_waveform ? CS40L26_OWT_SVC_METADATA : 0)); + (svc_waveform ? + CS40L26_OWT_SVC_METADATA : 0)); cl_dsp_memchunk_write(&out_ch, 8, WT_TYPE_V6_PWLE); cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET + - (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0)); + (svc_waveform ? + CS40L26_WT_METADATA_OFFSET : 0)); cl_dsp_memchunk_write(&out_ch, 24, (in_data_bytes / 4) - - (svc_waveform ? CS40L26_WT_METADATA_OFFSET : 0)); + (svc_waveform ? + CS40L26_WT_METADATA_OFFSET : 0)); memcpy(*out_data + out_ch.bytes, in_data, in_data_bytes); @@ -2430,21 +2436,29 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, } ch = cl_dsp_memchunk_create((void *) in_data, in_data_bytes); - cl_dsp_memchunk_read(&ch, 8); /* Skip padding */ - ret = cl_dsp_memchunk_read(&ch, 8); - if (ret < 0) + /* Skip padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, NULL); + if (ret) return ret; - nsections = ret; + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, &nsections); + if (ret) + return ret; - global_rep = cl_dsp_memchunk_read(&ch, 8); + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, &global_rep); + if (ret) + return ret; sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), GFP_KERNEL); if (!sections) return -ENOMEM; - cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + ret = cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + if (ret) { + dev_err(cs40l26->dev, "Failed to get section info\n"); + return ret; + } data_bytes = cs40l26_owt_comp_data_size(cs40l26, nsections, sections); @@ -2484,18 +2498,33 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, } header = cs40l26_header(cs40l26, sections[i].index); - if (header == NULL) { - ret = -ENOMEM; + if (IS_ERR(header)) { + ret = PTR_ERR(header); goto data_err_free; } if (header->type == WT_TYPE_V6_COMPOSITE) { ch = cl_dsp_memchunk_create(header->data, 8); - cl_dsp_memchunk_read(&ch, 24); /* Skip Wlength */ - cl_dsp_memchunk_read(&ch, 8); /* Skip Padding */ - ncw_nsections = cl_dsp_memchunk_read(&ch, 8); + /* Skip Wlength */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 24, NULL); + if (ret) + return ret; + + /* Skip Padding */ + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, NULL); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, + &ncw_nsections); + if (ret) + return ret; + + ret = cl_dsp_memchunk_read(cs40l26->dsp, &ch, 8, + &ncw_global_rep); + if (ret) + return ret; - ncw_global_rep = cl_dsp_memchunk_read(&ch, 8); if (ncw_global_rep != 0) { dev_err(dev, "No NCW support for outer repeat\n"); @@ -2509,8 +2538,8 @@ static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, ncw_data = cs40l26_ncw_amp_scaling(cs40l26, sections[i].amplitude, ncw_nsections, header->data + 8, ncw_bytes); - if (ncw_data == NULL) { - ret = -ENOMEM; + if (IS_ERR(ncw_data)) { + ret = PTR_ERR(ncw_data); goto data_err_free; } @@ -2678,9 +2707,9 @@ static void cs40l26_upload_worker(struct work_struct *work) u16 index, bank; bool pwle, svc_waveform; - ret = pm_runtime_get_sync(cdev); - if (ret < 0) - return cs40l26_resume_error_handle(cdev, ret); + ret = cs40l26_pm_enter(cdev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -2692,6 +2721,12 @@ static void cs40l26_upload_worker(struct work_struct *work) goto out_mutex; } + if (is_owt(cs40l26->trigger_indices[effect->id])) { + dev_err(cdev, "Open Wavetable effects cannot be edited\n"); + ret = -EPERM; + goto out_mutex; + } + switch (effect->u.periodic.waveform) { case FF_CUSTOM: pwle = (cs40l26->raw_custom_data[0] == @@ -2703,8 +2738,9 @@ static void cs40l26_upload_worker(struct work_struct *work) if (len > CS40L26_CUSTOM_DATA_SIZE) { refactored_size = cs40l26_refactor_owt(cs40l26, - cs40l26->raw_custom_data, len, pwle, svc_waveform, - &refactored_data); + cs40l26->raw_custom_data, len, + pwle, svc_waveform, + &refactored_data); if (refactored_size <= 0) { dev_err(cdev, @@ -2810,8 +2846,8 @@ out_free: out_mutex: mutex_unlock(&cs40l26->lock); - pm_runtime_mark_last_busy(cdev); - pm_runtime_put_autosuspend(cdev); + + cs40l26_pm_exit(cdev); cs40l26->upload_ret = ret; } @@ -2856,6 +2892,7 @@ static int cs40l26_upload_effect(struct input_dev *dev, out_free: memset(&cs40l26->upload_effect, 0, sizeof(struct ff_effect)); kfree(cs40l26->raw_custom_data); + cs40l26->raw_custom_data = NULL; return ret; } @@ -2932,8 +2969,8 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) int ret; owt = cs40l26_owt_find(cs40l26, effect_id); - if (owt == NULL) - return -ENOMEM; + if (IS_ERR(owt)) + return PTR_ERR(owt); cmd |= (owt->trigger_index & 0xFF); @@ -2951,6 +2988,8 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) } } + cs40l26->trigger_indices[effect_id] = 0; + list_del(&owt->list); kfree(owt); cs40l26->num_owt_effects--; @@ -2958,7 +2997,6 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id) return 0; } - static void cs40l26_erase_worker(struct work_struct *work) { struct cs40l26_private *cs40l26 = container_of(work, @@ -2968,9 +3006,9 @@ static void cs40l26_erase_worker(struct work_struct *work) u16 duration; u32 index; - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) - return cs40l26_resume_error_handle(cs40l26->dev, ret); + ret = cs40l26_pm_enter(cs40l26->dev); + if (ret) + return; mutex_lock(&cs40l26->lock); @@ -3018,8 +3056,7 @@ static void cs40l26_erase_worker(struct work_struct *work) out_mutex: mutex_unlock(&cs40l26->lock); pm_err: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + cs40l26_pm_exit(cs40l26->dev); cs40l26->erase_ret = ret; } @@ -3793,13 +3830,6 @@ static int cs40l26_handle_errata(struct cs40l26_private *cs40l26) num_writes = CS40L26_ERRATA_A1_EXPL_EN_NUM_WRITES; } - ret = regmap_register_patch(cs40l26->regmap, cs40l26_a1_errata, - num_writes); - if (ret) { - dev_err(cs40l26->dev, "Failed to patch A1 errata\n"); - return ret; - } - return cs40l26_pseq_multi_write(cs40l26, cs40l26_a1_errata, num_writes, false, CS40L26_PSEQ_OP_WRITE_FULL); } @@ -3963,11 +3993,9 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - cs40l26_resume_error_handle(dev, ret); + ret = cs40l26_pm_enter(dev); + if (ret) return ret; - } ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -4011,8 +4039,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) } pm_err: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); return ret; } @@ -4051,7 +4078,8 @@ int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le) return ret; ret = cl_dsp_get_reg(cs40l26->dsp, "LE_EST_STATUS", - CL_DSP_YM_UNPACKED_TYPE, CS40l26_SVC_ALGO_ID, ®); + CL_DSP_YM_UNPACKED_TYPE, CS40L26_SVC_ALGO_ID, + ®); if (ret) return ret; @@ -4077,20 +4105,10 @@ int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le) EXPORT_SYMBOL(cs40l26_svc_le_estimate); static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, - u32 *tuning_num) + unsigned int le, u32 *tuning_num) { - unsigned int le; - int ret, i; - - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev, ret); - return ret; - } - - ret = cs40l26_svc_le_estimate(cs40l26, &le); - if (ret) - goto pm_err; + int ret = 0; + int i; if (le) { for (i = 0; i < cs40l26->num_svc_le_vals; i++) { @@ -4108,10 +4126,6 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, if (!le || i == cs40l26->num_svc_le_vals) dev_warn(cs40l26->dev, "Using default tunings\n"); -pm_err: - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); - return ret; } @@ -4125,7 +4139,7 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, coeff_files = kcalloc(n, sizeof(char *), GFP_KERNEL); if (!coeff_files) - return NULL; + return ERR_PTR(-ENOMEM); for (i = 0; i < n; i++) { coeff_files[i] = @@ -4157,16 +4171,15 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, CS40L26_TUNING_FILE_NAME_MAX_LEN); strscpy(coeff_files[0], wt_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN); + strscpy(coeff_files[1], svc_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN); if (cs40l26->fw_id == CS40L26_FW_ID) { - strscpy(coeff_files[1], CS40L26_A2H_TUNING_FILE_NAME, - CS40L26_TUNING_FILE_NAME_MAX_LEN); - strscpy(coeff_files[2], svc_tuning, + strscpy(coeff_files[2], CS40L26_A2H_TUNING_FILE_NAME, CS40L26_TUNING_FILE_NAME_MAX_LEN); strscpy(coeff_files[3], CS40L26_DVL_FILE_NAME, CS40L26_TUNING_FILE_NAME_MAX_LEN); } else { - strscpy(coeff_files[1], CS40L26_CALIB_BIN_FILE_NAME, + strscpy(coeff_files[2], CS40L26_CALIB_BIN_FILE_NAME, CS40L26_TUNING_FILE_NAME_MAX_LEN); } @@ -4174,7 +4187,7 @@ static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n, err_free: kfree(coeff_files); - return NULL; + return ERR_PTR(-ENOMEM); } static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning) @@ -4188,8 +4201,8 @@ static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning) CS40L26_TUNING_FILES_RUNTIME : CS40L26_TUNING_FILES_CAL; coeff_files = cs40l26_get_tuning_names(cs40l26, num_files, tuning); - if (!coeff_files) - return -ENOMEM; + if (IS_ERR(coeff_files)) + return PTR_ERR(coeff_files); for (i = 0; i < num_files; i++) { ret = request_firmware(&coeff, coeff_files[i], dev); @@ -4306,8 +4319,8 @@ static int cs40l26_cl_dsp_reinit(struct cs40l26_private *cs40l26) } cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap); - if (!cs40l26->dsp) - return -ENOMEM; + if (IS_ERR(cs40l26->dsp)) + return PTR_ERR(cs40l26->dsp); return cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); @@ -4317,9 +4330,10 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) { bool svc_le_required = cs40l26->num_svc_le_vals && !cs40l26->calib_fw; struct device *dev = cs40l26->dev; - u32 tuning_num = 0; + u32 rev, branch, tuning_num = 0; const struct firmware *fw; int ret; + unsigned int le = 0; cs40l26->fw_loaded = false; @@ -4355,17 +4369,47 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) return ret; if (svc_le_required) { - ret = cs40l26_dsp_config(cs40l26); + ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev); if (ret) return ret; - ret = cs40l26_tuning_select_from_svc_le(cs40l26, &tuning_num); - if (ret) - return ret; + branch = CL_DSP_GET_MAJOR(rev); - cs40l26_pm_runtime_teardown(cs40l26); + switch (branch) { + case CS40L26_FW_MAINT_BRANCH: + ret = cs40l26_dsp_config(cs40l26); + if (ret) + return ret; + + ret = cs40l26_pm_enter(dev); + if (ret) + return ret; + + ret = cs40l26_svc_le_estimate(cs40l26, &le); + if (ret) + dev_warn(dev, "svc_le est failed, %d", ret); + + cs40l26_pm_exit(dev); + + cs40l26_pm_runtime_teardown(cs40l26); + + ret = cs40l26_dsp_pre_config(cs40l26); + if (ret) + return ret; + + break; + + case CS40L26_FW_BRANCH: + le = cs40l26->svc_le_est_stored; + break; + + default: + dev_err(dev, "Invalid firmware branch, %d", branch); + return -EINVAL; + } - ret = cs40l26_dsp_pre_config(cs40l26); + ret = cs40l26_tuning_select_from_svc_le(cs40l26, + le, &tuning_num); if (ret) return ret; } @@ -4377,35 +4421,30 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) return cs40l26_dsp_config(cs40l26); } -int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) +int cs40l26_fw_swap(struct cs40l26_private *cs40l26, const u32 id) { struct device *dev = cs40l26->dev; - bool deferred = cs40l26->fw_defer; - u32 pseq_rom_end_of_script_loc; - int ret; - - if (cs40l26->fw_defer) { - cs40l26->fw_defer = false; - } else { - disable_irq(cs40l26->irq); - cs40l26_pm_runtime_teardown(cs40l26); - } + bool re_enable = false; + int ret = 0; if (cs40l26->revid != CS40L26_REVID_A1 && cs40l26->revid != CS40L26_REVID_B0) { dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid); - ret = -EINVAL; - goto defer_err; + return -EINVAL; } - /* reset pseq END_OF_SCRIPT to location from ROM */ - pseq_rom_end_of_script_loc = CS40L26_PSEQ_ROM_END_OF_SCRIPT; + if (cs40l26->fw_loaded) { + disable_irq(cs40l26->irq); + cs40l26_pm_runtime_teardown(cs40l26); + re_enable = true; + } - ret = cs40l26_dsp_write(cs40l26, pseq_rom_end_of_script_loc, + /* reset pseq END_OF_SCRIPT to location from ROM */ + ret = cs40l26_dsp_write(cs40l26, CS40L26_PSEQ_ROM_END_OF_SCRIPT, CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT); if (ret) { dev_err(dev, "Failed to reset pseq END_OF_SCRIPT %d\n", ret); - goto defer_err; + return ret; } if (id == CS40L26_FW_CALIB_ID) @@ -4415,42 +4454,28 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) ret = cs40l26_fw_upload(cs40l26); if (ret) - goto defer_err; + return ret; - if (deferred) { + if (cs40l26->fw_defer && cs40l26->fw_loaded) { ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, "cs40l26", cs40l26); if (ret) { dev_err(dev, "Failed to request threaded IRQ: %d\n", ret); - goto defer_err; + return ret; } - } else { - enable_irq(cs40l26->irq); - } - return 0; + cs40l26->fw_defer = false; + } -defer_err: - if (deferred) - cs40l26->fw_defer = true; + if (re_enable) + enable_irq(cs40l26->irq); return ret; } EXPORT_SYMBOL(cs40l26_fw_swap); -static int cs40l26_update_reg_defaults(struct cs40l26_private *cs40l26) -{ - int ret; - - ret = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG, - CS40L26_SPK_DEFAULT_HIZ_MASK, 1 << - CS40L26_SPK_DEFAULT_HIZ_SHIFT); - - return ret; -} - static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) { struct device *dev = cs40l26->dev; @@ -4669,7 +4694,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) } if (!of_property_read_u32(np, "cirrus,boost-ctl-microvolt", &val)) - cs40l26->pdata.boost_ctl = val | CS40L26_PDATA_PRESENT; + cs40l26->pdata.boost_ctl = val; else cs40l26->pdata.boost_ctl = CS40L26_BST_CTL_DEFAULT; @@ -4815,9 +4840,12 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, if (ret) goto err; - ret = cs40l26_update_reg_defaults(cs40l26); + /* Set LRA to high-z to avoid fault conditions */ + ret = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG, + CS40L26_SPK_DEFAULT_HIZ_MASK, 1 << + CS40L26_SPK_DEFAULT_HIZ_SHIFT); if (ret) { - dev_err(dev, "Failed to update reg defaults\n"); + dev_err(dev, "Failed to set LRA to HI-Z\n"); goto err; } @@ -4911,6 +4939,27 @@ int cs40l26_remove(struct cs40l26_private *cs40l26) } EXPORT_SYMBOL(cs40l26_remove); +int cs40l26_pm_enter(struct device *dev) +{ + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(cs40l26_pm_enter); + +void cs40l26_pm_exit(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL(cs40l26_pm_exit); + int cs40l26_suspend(struct device *dev) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -4958,8 +5007,7 @@ void cs40l26_resume_error_handle(struct device *dev, int ret) pm_runtime_set_active(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + cs40l26_pm_exit(dev); } EXPORT_SYMBOL(cs40l26_resume_error_handle); -- cgit v1.2.3