diff options
author | Tai Kuo <taikuo@google.com> | 2022-02-08 13:00:32 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2022-02-08 13:27:47 +0800 |
commit | bdf53aab1b4368d3178ee07f7c3234001d839d58 (patch) | |
tree | 5be0390552954a6201383f4dfdec6a2623cb2d8b /cs40l26/cs40l26.c | |
parent | f4c7647aefec271be24e987328fa3e38fef4b874 (diff) | |
download | amplifiers-bdf53aab1b4368d3178ee07f7c3234001d839d58.tar.gz |
cs40l26: merge CirrusLogic cs40l26 v4.0.0
Branch: v5.10-cs40l26
Tag: CS40L26-v4.0.0_5.10
Files:
drivers/input/misc/cs40l26-i2c.c (No changes)
drivers/input/misc/cs40l26-spi.c (No changes)
drivers/input/misc/cs40l26-sysfs.c
drivers/input/misc/cs40l26-tables.c
drivers/input/misc/cs40l26.c
include/linux/mfd/cs40l26.h
sound/soc/codecs/cs40l26.c -> cs40l26-codec.c
Features:
- Devicetree Property "cirrus,gain-adjust"
- For LRA-specific gain changes
- Controls for f0 and ReDC Compensation
- Sysfs control vmon_max for VMON logging maximum reporting
- Allow multiple writes to one register in power on sequencer
- Devicetree Property "cirrus,bst-expl-mode-disbale"
- If present, will disable boost exploratory mode at the
cost of the ability to reliably detect BST_SHORT_ERR instances
- Imrpoved error handling for PM Runtime Resume errors
Bug fixes:
- Update error handling to avoid deadlock
- Avoid hibernation when intializing power on sequencer
- Updated handling of vibe_state to avoid race condition with firmware
- Code Improvements
Commits:
e4b5d23 ASoC: cs40l26: Ensure recovery from PM Runtime Resume error
2d5cb03 input: cs40l26: Ensure recovery from PM Runtime Resume error
(SKIP) 098d137 Documentation: cs40l26: Add DT boolean "cirrus,bst-expl-mode-disable"
30c30e7 input: cs40l26: Add DT boolean "cirrus,bst-expl-mode-disable"
(SKIP) 0707fb3 Documentation: cs40l26: Add DT boolean "cirrus,vibe-state"
38592da input: cs40l26: Add DT boolean "cirrus,vibe-state"
7e0c02c input: cs40l26: Read IRQ1_STATUS after PM Runtime resume
c903a82 input: cs40l26: Load calibration tuning file
26e93ab input: cs40l26: Print measured LE estimation value
b8ef3b0 input: cs40l26: Disable Boost Exploratory Mode
2d8ef2c input: cs40l26: Allow multiple writes to an address in PSEQ
9f166cd input: cs40l26: Update vibe_state in mailbox handler
53f24ab input: cs40l26: Add vmon_max logging sysfs control
c7708f5 input: cs40l26: Avoid hibernation when initializing PSEQ
337536d input: cs40l26: Add f0 and redc comp controls
(SKIP) 6e1642c Documentation: cs40l26: Add DT property "gain-adjust"
eb1fed7 input: cs40l26: Add DT property "gain-adjust"
bc34f05 input: cs40l26: Move BST_IPK_FLAG unmask to proper function
0570df1 input: cs40l26: Update error handling procedure
Bug: 205688153
Test: idlcli commands.
Signed-off-by: Tai Kuo <taikuo@google.com>
Change-Id: Ic4b7cc5664c32132125178f7a41ca7c1a916123e
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r-- | cs40l26/cs40l26.c | 475 |
1 files changed, 320 insertions, 155 deletions
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index 19b1b39..5f8f6da 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -421,7 +421,6 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, break; case CS40L26_PM_STATE_ALLOW_HIBERNATE: - case CS40L26_PM_STATE_SHUTDOWN: cs40l26->wksrc_sts = 0x00; ret = cs40l26_dsp_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd); @@ -429,6 +428,12 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, return ret; break; + case CS40L26_PM_STATE_SHUTDOWN: + cs40l26->wksrc_sts = 0x00; + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + cmd, CS40L26_DSP_MBOX_RESET); + + break; default: dev_err(dev, "Invalid PM state: %u\n", state); return -EINVAL; @@ -476,52 +481,6 @@ static int cs40l26_dsp_start(struct cs40l26_private *cs40l26) return 0; } -static int cs40l26_dsp_wake(struct cs40l26_private *cs40l26) -{ - u8 dsp_state; - int ret; - - ret = cs40l26_pm_state_transition(cs40l26, CS40L26_PM_STATE_WAKEUP); - if (ret) - return ret; - - ret = cs40l26_pm_state_transition(cs40l26, - CS40L26_PM_STATE_PREVENT_HIBERNATE); - if (ret) - return ret; - - ret = cs40l26_dsp_state_get(cs40l26, &dsp_state); - if (ret) - return ret; - - if (dsp_state != CS40L26_DSP_STATE_STANDBY && - dsp_state != CS40L26_DSP_STATE_ACTIVE) { - dev_err(cs40l26->dev, "Failed to wake DSP\n"); - return -EINVAL; - } - - return 0; -} - -static int cs40l26_dsp_shutdown(struct cs40l26_private *cs40l26) -{ - u32 timeout_ms; - int ret; - - ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms); - if (ret) - return ret; - - ret = cs40l26_pm_state_transition(cs40l26, CS40L26_PM_STATE_SHUTDOWN); - if (ret) - return ret; - - usleep_range(CS40L26_MS_TO_US(timeout_ms), - CS40L26_MS_TO_US(timeout_ms) + 100); - - return 0; -} - static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26) { u32 halo_state, timeout_ms; @@ -570,14 +529,6 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26) return -EINVAL; } - /* errata write fixing indeterminent PLL lock time */ - ret = regmap_update_bits(cs40l26->regmap, CS40L26_PLL_REFCLK_DETECT_0, - CS40L26_PLL_REFCLK_DET_EN_MASK, 0); - if (ret) { - dev_err(cs40l26->dev, "Failed to disable PLL refclk detect\n"); - return ret; - } - ret = regmap_write(cs40l26->regmap, CS40L26_DSP1_CCM_CORE_CONTROL, CS40L26_DSP_CCM_CORE_KILL); if (ret) @@ -678,6 +629,16 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) case CS40L26_DSP_MBOX_COMPLETE_I2S: dev_dbg(dev, "Mailbox: COMPLETE_I2S\n"); break; + case CS40L26_DSP_MBOX_TRIGGER_CP: + if (!cs40l26->pdata.vibe_state_reporting) { + dev_err(dev, "cirrus,vibe-state not in DT\n"); + return -EPERM; + } + + dev_dbg(dev, "Mailbox: TRIGGER_CP\n"); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK); + break; case CS40L26_DSP_MBOX_TRIGGER_GPIO: dev_dbg(dev, "Mailbox: TRIGGER_GPIO\n"); cs40l26_vibe_state_update(cs40l26, @@ -837,20 +798,13 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, EXPORT_SYMBOL(cs40l26_vibe_state_update); static int cs40l26_error_release(struct cs40l26_private *cs40l26, - unsigned int err_rls, bool bst_err) + unsigned int err_rls) { struct regmap *regmap = cs40l26->regmap; struct device *dev = cs40l26->dev; u32 err_sts, err_cfg; int ret; - /* Boost related errors must be handled with DSP turned off */ - if (bst_err) { - ret = cs40l26_dsp_shutdown(cs40l26); - if (ret) - return ret; - } - ret = regmap_read(regmap, CS40L26_ERROR_RELEASE, &err_sts); if (ret) { dev_err(cs40l26->dev, "Failed to get error status\n"); @@ -876,16 +830,8 @@ static int cs40l26_error_release(struct cs40l26_private *cs40l26, err_cfg &= ~BIT(err_rls); ret = regmap_write(cs40l26->regmap, CS40L26_ERROR_RELEASE, err_cfg); - if (ret) { + if (ret) dev_err(dev, "Actuator Safe Mode release sequence failed\n"); - return ret; - } - - if (bst_err) { - ret = cs40l26_dsp_wake(cs40l26); - if (ret) - return ret; - } return ret; } @@ -895,9 +841,8 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, { struct device *dev = cs40l26->dev; u32 err_rls = 0; - unsigned int reg, val; - bool bst_err = false; int ret = 0; + unsigned int reg, val; switch (irq1) { case CS40L26_IRQ1_GPIO1_RISE: @@ -992,23 +937,19 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, "BST voltage returned below warning threshold\n"); break; case CS40L26_IRQ1_BST_OVP_ERR: - dev_err(dev, "BST overvolt. error, CS40L26 shutting down\n"); + dev_err(dev, "BST overvolt. error\n"); err_rls = CS40L26_BST_OVP_ERR_RLS; - bst_err = true; break; case CS40L26_IRQ1_BST_DCM_UVP_ERR: - dev_err(dev, - "BST undervolt. error, CS40L26 shutting down\n"); + dev_err(dev, "BST undervolt. error\n"); err_rls = CS40L26_BST_UVP_ERR_RLS; - bst_err = true; break; case CS40L26_IRQ1_BST_SHORT_ERR: - dev_err(dev, "LBST short detected, CS40L26 shutting down\n"); + dev_err(dev, "LBST short detected\n"); err_rls = CS40L26_BST_SHORT_ERR_RLS; - bst_err = true; break; case CS40L26_IRQ1_BST_IPK_FLAG: - dev_warn(dev, "Current is being limited by LBST inductor\n"); + dev_dbg(dev, "Current is being limited by LBST inductor\n"); break; case CS40L26_IRQ1_TEMP_WARN_RISE: dev_err(dev, "Die overtemperature warning\n"); @@ -1019,11 +960,11 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, break; case CS40L26_IRQ1_TEMP_ERR: dev_err(dev, - "Die overtemperature error, CS40L26 shutting down\n"); + "Die overtemperature error\n"); err_rls = CS40L26_TEMP_ERR_RLS; break; case CS40L26_IRQ1_AMP_ERR: - dev_err(dev, "AMP short detected, CS40L26 shutting down\n"); + dev_err(dev, "AMP short detected\n"); err_rls = CS40L26_AMP_SHORT_ERR_RLS; break; case CS40L26_IRQ1_DC_WATCHDOG_RISE: @@ -1046,7 +987,7 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, } if (err_rls) - ret = cs40l26_error_release(cs40l26, err_rls, bst_err); + ret = cs40l26_error_release(cs40l26, err_rls); err: regmap_write(cs40l26->regmap, CS40L26_IRQ1_EINT_1, BIT(irq1)); @@ -1185,30 +1126,30 @@ static irqreturn_t cs40l26_irq(int irq, void *data) unsigned long num_irq; int ret; - if (cs40l26_dsp_read(cs40l26, CS40L26_IRQ1_STATUS, &sts)) { - dev_err(dev, "Failed to read IRQ1 Status\n"); - return IRQ_NONE; - } - - if (sts != CS40L26_IRQ_STATUS_ASSERT) { - dev_err(dev, "IRQ1 asserted with no pending interrupts\n"); - return IRQ_NONE; - } - - ret = pm_runtime_get_sync(dev); - - mutex_lock(&cs40l26->lock); - - if (ret < 0) { - pm_runtime_set_active(dev); + if (pm_runtime_get_sync(dev) < 0) { + cs40l26_resume_error_handle(dev); - dev_err(dev, "PM Runtime Resume failed, interrupts missed\n"); + dev_err(dev, "Interrupts missed\n"); cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_1, CS40L26_IRQ_EINT1_ALL_MASK); cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_2, CS40L26_IRQ_EINT2_ALL_MASK); + return IRQ_NONE; + } + + mutex_lock(&cs40l26->lock); + + if (regmap_read(regmap, CS40L26_IRQ1_STATUS, &sts)) { + dev_err(dev, "Failed to read IRQ1 Status\n"); + ret = IRQ_NONE; + goto err; + } + + if (sts != CS40L26_IRQ_STATUS_ASSERT) { + dev_err(dev, "IRQ1 asserted with no pending interrupts\n"); + ret = IRQ_NONE; goto err; } @@ -1305,7 +1246,7 @@ static int cs40l26_pseq_find_end(struct cs40l26_private *cs40l26, } static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, - u32 data, u8 op_code) + u32 data, bool update, u8 op_code) { struct device *dev = cs40l26->dev; bool is_new = true; @@ -1360,7 +1301,7 @@ static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, list_for_each_entry(op, &cs40l26->pseq_op_head, list) { if (op->words[0] == op_words[0] && (op->words[1] & op_mask) == - (op_words[1] & op_mask)) { + (op_words[1] & op_mask) && update) { if (op->size != num_op_words) { dev_err(dev, "Failed to replace PSEQ op.\n"); ret = -EINVAL; @@ -1432,7 +1373,7 @@ static int cs40l26_pseq_multi_write(struct cs40l26_private *cs40l26, for (i = 0; i < num_regs; i++) { ret = cs40l26_pseq_write(cs40l26, reg_seq[i].reg, - reg_seq[i].def, op_code); + reg_seq[i].def, update, op_code); if (ret) return ret; } @@ -1539,12 +1480,13 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26) int ret; ret = cs40l26_pseq_write(cs40l26, CS40L26_NGATE1_INPUT, - CS40L26_DATA_SRC_DSP1TX4, CS40L26_PSEQ_OP_WRITE_L16); + CS40L26_DATA_SRC_DSP1TX4, true, + CS40L26_PSEQ_OP_WRITE_L16); if (ret) return ret; ret = cs40l26_pseq_write(cs40l26, CS40L26_MIXER_NGATE_CH1_CFG, - CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW, + CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW, true, CS40L26_PSEQ_OP_WRITE_FULL); if (ret) { dev_err(dev, "Failed to sequence Mixer Noise Gate\n"); @@ -1554,7 +1496,7 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26) /* set SPK_DEFAULT_HIZ to 1 */ ret = cs40l26_pseq_write(cs40l26, CS40L26_TST_DAC_MSM_CONFIG, CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_H16, - CS40L26_PSEQ_OP_WRITE_H16); + true, CS40L26_PSEQ_OP_WRITE_H16); if (ret) dev_err(dev, "Failed to sequence register default updates\n"); @@ -1598,7 +1540,7 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg, } return cs40l26_pseq_write(cs40l26, reg, - new_mask, CS40L26_PSEQ_OP_WRITE_FULL); + new_mask, true, CS40L26_PSEQ_OP_WRITE_FULL); } static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq, @@ -1685,7 +1627,11 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, (ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) | (owt << CS40L26_BTN_OWT_SHIFT); - pm_runtime_get_sync(cs40l26->dev); + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) { + cs40l26_resume_error_handle(cs40l26->dev); + return ret; + } ret = regmap_write(cs40l26->regmap, reg, write_val); if (ret) @@ -1728,7 +1674,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work) u32 reg; int ret; - pm_runtime_get_sync(cs40l26->dev); + if (pm_runtime_get_sync(cs40l26->dev) < 0) + return cs40l26_resume_error_handle(cs40l26->dev); + mutex_lock(&cs40l26->lock); if (cs40l26->vibe_state == CS40L26_VIBE_STATE_ASP) { @@ -1774,7 +1722,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) dev_dbg(dev, "%s\n", __func__); - pm_runtime_get_sync(dev); + if (pm_runtime_get_sync(dev) < 0) + return cs40l26_resume_error_handle(dev); + mutex_lock(&cs40l26->lock); effect = cs40l26->trigger_effect; @@ -1834,8 +1784,6 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) index, CS40L26_DSP_MBOX_RESET); if (ret) goto err_mutex; - cs40l26_vibe_state_update(cs40l26, - CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK); break; default: dev_err(dev, "Invalid waveform type: 0x%X\n", @@ -1844,6 +1792,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) goto err_mutex; } + if (!cs40l26->pdata.vibe_state_reporting) + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK); err_mutex: mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(dev); @@ -1858,7 +1809,9 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) dev_dbg(cs40l26->dev, "%s\n", __func__); - pm_runtime_get_sync(cs40l26->dev); + if (pm_runtime_get_sync(cs40l26->dev) < 0) + return cs40l26_resume_error_handle(cs40l26->dev); + mutex_lock(&cs40l26->lock); if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) @@ -2143,7 +2096,11 @@ 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; - pm_runtime_get_sync(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev); + return ret; + } ret = cl_dsp_get_reg(dsp, "OWT_NEXT_XM", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -2560,7 +2517,9 @@ static void cs40l26_upload_worker(struct work_struct *work) u16 index, bank; bool pwle; - pm_runtime_get_sync(cdev); + if (pm_runtime_get_sync(cdev) < 0) + return cs40l26_resume_error_handle(cdev); + mutex_lock(&cs40l26->lock); effect = &cs40l26->upload_effect; @@ -2841,8 +2800,10 @@ static void cs40l26_erase_worker(struct work_struct *work) int effect_id; u32 index; + if (pm_runtime_get_sync(cs40l26->dev) < 0) + return cs40l26_resume_error_handle(cs40l26->dev); + mutex_lock(&cs40l26->lock); - pm_runtime_get_sync(cs40l26->dev); effect_id = cs40l26->erase_effect->id; index = cs40l26->trigger_indices[effect_id]; @@ -3037,19 +2998,24 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id) } else { cs40l26->fw.id = id; - if (id == CS40L26_FW_ID) + if (id == CS40L26_FW_ID) { cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV; - if (id == CS40L26_FW_CALIB_ID) + cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_RT; + } else if (id == CS40L26_FW_CALIB_ID) { cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV; - - cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_MAX; + cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_CAL; + } else { + dev_err(cs40l26->dev, "Invalid firmware ID 0x%06X\n", + id); + return -EINVAL; + } if (!cs40l26->fw.coeff_files) cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev, - CS40L26_TUNING_FILES_MAX, sizeof(char *), + cs40l26->fw.num_coeff_files, sizeof(char *), GFP_KERNEL); - for (i = 0; i < CS40L26_TUNING_FILES_MAX; i++) { + for (i = 0; i < cs40l26->fw.num_coeff_files; i++) { if (!cs40l26->fw.coeff_files[i]) { cs40l26->fw.coeff_files[i] = devm_kzalloc(cs40l26->dev, @@ -3063,15 +3029,23 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id) strncpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME, CS40L26_WT_FILE_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[1], - CS40L26_A2H_TUNING_FILE_NAME, - CS40L26_A2H_TUNING_FILE_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[2], - CS40L26_SVC_TUNING_FILE_NAME, - CS40L26_SVC_TUNING_FILE_NAME_LEN); - strncpy(cs40l26->fw.coeff_files[3], - CS40L26_DVL_FILE_NAME, - CS40L26_DVL_FILE_NAME_LEN); + + if (id == CS40L26_FW_ID) { + strncpy(cs40l26->fw.coeff_files[1], + CS40L26_A2H_TUNING_FILE_NAME, + CS40L26_A2H_TUNING_FILE_NAME_LEN); + strncpy(cs40l26->fw.coeff_files[2], + CS40L26_SVC_TUNING_FILE_NAME, + CS40L26_SVC_TUNING_FILE_NAME_LEN); + strncpy(cs40l26->fw.coeff_files[3], + CS40L26_DVL_FILE_NAME, + CS40L26_DVL_FILE_NAME_LEN); + } else { + strncpy(cs40l26->fw.coeff_files[1], + CS40L26_CALIB_BIN_FILE_NAME, + CS40L26_CALIB_BIN_FILE_NAME_LEN); + } + ret = cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); @@ -3155,7 +3129,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_BLOCK_ENABLES2, - val, CS40L26_PSEQ_OP_WRITE_FULL); + val, true, CS40L26_PSEQ_OP_WRITE_FULL); if (ret) { dev_err(dev, "Failed to sequence brownout prevention\n"); return ret; @@ -3256,7 +3230,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, val, - CS40L26_PSEQ_OP_WRITE_FULL); + true, CS40L26_PSEQ_OP_WRITE_FULL); if (ret) return ret; } @@ -3359,7 +3333,7 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, val, - CS40L26_PSEQ_OP_WRITE_FULL); + true, CS40L26_PSEQ_OP_WRITE_FULL); if (ret) return ret; } @@ -3456,7 +3430,7 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_BST_DCM_CTL, - val, CS40L26_PSEQ_OP_WRITE_FULL); + val, true, CS40L26_PSEQ_OP_WRITE_FULL); } return ret; @@ -3528,7 +3502,7 @@ static int calib_device_tree_config(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_1, bst_ctl, - CS40L26_PSEQ_OP_WRITE_L16); + true, CS40L26_PSEQ_OP_WRITE_L16); if (ret) return ret; @@ -3549,11 +3523,10 @@ static int calib_device_tree_config(struct cs40l26_private *cs40l26) } ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_2, - bst_ctl_cfg, CS40L26_PSEQ_OP_WRITE_FULL); + bst_ctl_cfg, true, CS40L26_PSEQ_OP_WRITE_FULL); } - return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0, - BIT(CS40L26_IRQ1_BST_IPK_FLAG)); + return ret; } static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26) @@ -3576,8 +3549,13 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26) return ret; } - return cs40l26_pseq_write(cs40l26, CS40L26_BST_IPK_CTL, val, - CS40L26_PSEQ_OP_WRITE_L16); + ret = cs40l26_pseq_write(cs40l26, CS40L26_BST_IPK_CTL, val, + true, CS40L26_PSEQ_OP_WRITE_L16); + if (ret) + return ret; + + return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0, + BIT(CS40L26_IRQ1_BST_IPK_FLAG)); } static int cs40l26_owt_setup(struct cs40l26_private *cs40l26) @@ -3611,12 +3589,103 @@ static int cs40l26_owt_setup(struct cs40l26_private *cs40l26) return ret; } +static int cs40l26_lbst_short_test(struct cs40l26_private *cs40l26) +{ + struct regmap *regmap = cs40l26->regmap; + struct device *dev = cs40l26->dev; + unsigned int err; + int ret; + + ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2, + CS40L26_BST_CTL_SEL_MASK, CS40L26_BST_CTL_SEL_FIXED); + if (ret) { + dev_err(dev, "Failed to set VBST_CTL_2\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_1, + CS40L26_BST_CTL_MASK, CS40L26_BST_CTL_VP); + if (ret) { + dev_err(dev, "Failed to set VBST_CTL_1\n"); + return ret; + } + + /* Set GLOBAL_EN; safe because DSP is guaranteed to be off here */ + ret = regmap_update_bits(regmap, CS40L26_GLOBAL_ENABLES, + CS40L26_GLOBAL_EN_MASK, 1); + if (ret) { + dev_err(dev, "Failed to set GLOBAL_EN\n"); + return ret; + } + + /* Wait until boost converter is guaranteed to be powered up */ + usleep_range(CS40L26_BST_TIME_MIN_US, CS40L26_BST_TIME_MAX_US); + + ret = regmap_read(regmap, CS40L26_ERROR_RELEASE, &err); + if (ret) { + dev_err(dev, "Failed to get ERROR_RELEASE contents\n"); + return ret; + } + + if (err & BIT(CS40L26_BST_SHORT_ERR_RLS)) { + dev_err(dev, "FATAL: Boost shorted at startup\n"); + return ret; + } + + /* Clear GLOBAL_EN; safe because DSP is guaranteed to be off here */ + ret = regmap_update_bits(regmap, CS40L26_GLOBAL_ENABLES, + CS40L26_GLOBAL_EN_MASK, 0); + if (ret) { + dev_err(dev, "Failed to clear GLOBAL_EN\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2, + CS40L26_BST_CTL_SEL_MASK, CS40L26_BST_CTL_SEL_CLASS_H); + if (ret) { + dev_err(dev, "Failed to set VBST_CTL_2\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_1, + CS40L26_BST_CTL_MASK, CS40L26_BST_CTL_VP); + if (ret) + dev_err(dev, "Failed to set VBST_CTL_1\n"); + + return ret; +} + +static int cs40l26_handle_errata(struct cs40l26_private *cs40l26) +{ + int ret, num_writes; + + if (!cs40l26->pdata.expl_mode_enabled) { + ret = cs40l26_lbst_short_test(cs40l26); + if (ret) + return ret; + + num_writes = CS40L26_ERRATA_A1_NUM_WRITES; + } else { + 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); +} + static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) { struct regmap *regmap = cs40l26->regmap; struct device *dev = cs40l26->dev; unsigned int val; - u32 reg, nwaves; + u32 reg, nwaves, value; int ret; ret = cs40l26_verify_fw(cs40l26); @@ -3651,12 +3720,17 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; + + ret = cs40l26_handle_errata(cs40l26); + if (ret) + return ret; + ret = cs40l26_dsp_start(cs40l26); if (ret) return ret; - ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0, - BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR)); + ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, + CS40L26_PM_TIMEOUT_MS_MAX); if (ret) return ret; @@ -3664,7 +3738,8 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) BIT(CS40L26_IRQ1_AMP_ERR) | BIT(CS40L26_IRQ1_TEMP_ERR) | BIT(CS40L26_IRQ1_BST_SHORT_ERR) | BIT(CS40L26_IRQ1_BST_DCM_UVP_ERR) | - BIT(CS40L26_IRQ1_BST_OVP_ERR)); + BIT(CS40L26_IRQ1_BST_OVP_ERR) | + BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR)); if (ret) return ret; @@ -3694,6 +3769,11 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; + ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, + cs40l26->pdata.pm_stdby_timeout_ms); + if (ret) + return ret; + /* ensure firmware running */ ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE", CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); @@ -3725,7 +3805,11 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) cs40l26_pm_runtime_setup(cs40l26); - pm_runtime_get_sync(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev); + return ret; + } ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -3753,6 +3837,21 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) goto pm_err; + value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) | + (cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT); + + if (cs40l26->fw.id != CS40L26_FW_CALIB_ID) { + ret = cl_dsp_get_reg(cs40l26->dsp, "COMPENSATION_ENABLE", + CL_DSP_XM_UNPACKED_TYPE, + CS40L26_VIBEGEN_ALGO_ID, ®); + if (ret) + goto pm_err; + + ret = regmap_write(cs40l26->regmap, reg, value); + if (ret) + dev_err(dev, "Failed to configure compensation\n"); + } + pm_err: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -3760,6 +3859,28 @@ pm_err: return ret; } +static void cs40l26_gain_adjust(struct cs40l26_private *cs40l26, s32 adjust) +{ + u16 total, asp, change; + + asp = cs40l26->pdata.asp_scale_pct; + + if (adjust < 0) { + change = (u16) ((adjust * -1) & 0xFFFF); + if (asp < change) + total = 0; + else + total = asp - change; + } else { + change = (u16) (adjust & 0xFFFF); + total = asp + change; + if (total > CS40L26_GAIN_FULL_SCALE) + total = CS40L26_GAIN_FULL_SCALE; + } + + cs40l26->pdata.asp_scale_pct = total; +} + static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) { unsigned int reg, le = 0; @@ -3768,7 +3889,11 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) char n_str[2]; int ret, i, j; - pm_runtime_get_sync(cs40l26->dev); + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) { + cs40l26_resume_error_handle(cs40l26->dev); + return ret; + } ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_DSP_MBOX_CMD_LE_EST, CS40L26_DSP_MBOX_RESET); @@ -3788,6 +3913,8 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) goto pm_err; } + dev_dbg(cs40l26->dev, "Measured LE estimate = 0x%08X\n", le); + for (j = 0; j < cs40l26->num_svc_le_vals; j++) { if (le >= cs40l26->svc_le_vals[j]->min && le <= cs40l26->svc_le_vals[j]->max) { @@ -3804,6 +3931,9 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) strncat(svc_bin_file, n_str, 2); strncat(wt_bin_file, n_str, 2); + cs40l26_gain_adjust(cs40l26, + cs40l26->svc_le_vals[j]->gain_adjust); + break; } } @@ -3839,11 +3969,6 @@ static void cs40l26_coeff_load(struct cs40l26_private *cs40l26) int i, ret; for (i = 0; i < cs40l26->fw.num_coeff_files; i++) { - if (strncmp(cs40l26->fw.coeff_files[i], CS40L26_WT_FILE_NAME, - CS40L26_WT_FILE_NAME_LEN) - && cs40l26->fw.id == CS40L26_FW_CALIB_ID) - continue; - ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev); if (ret) { dev_warn(dev, "Continuing..."); @@ -3949,7 +4074,12 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) } if (cs40l26->fw_loaded) { - pm_runtime_get_sync(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev); + return ret; + } + ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, CS40L26_PM_TIMEOUT_MS_MAX); if (ret) { @@ -4008,7 +4138,9 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) int i, ret = 0, init_count, node_count = 0; struct fwnode_handle *child; unsigned int min, max, index; + const char *gain_adjust_str; const char *node_name; + s32 gain_adjust; init_count = device_get_child_node_count(dev); if (!init_count) @@ -4041,6 +4173,17 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) continue; } + if (fwnode_property_read_string(child, "cirrus,gain-adjust", + &gain_adjust_str)) { + gain_adjust = 0; + } else { + ret = kstrtos32(gain_adjust_str, 10, &gain_adjust); + if (ret) { + dev_warn(dev, "Failed to get gain adjust\n"); + gain_adjust = 0; + } + } + if (fwnode_property_read_u32(child, "cirrus,index", &index)) { dev_err(dev, "No index specified for SVC LE node\n"); continue; @@ -4067,6 +4210,7 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) cs40l26->svc_le_vals[node_count]->min = min; cs40l26->svc_le_vals[node_count]->max = max; + cs40l26->svc_le_vals[node_count]->gain_adjust = gain_adjust; cs40l26->svc_le_vals[node_count]->n = index; node_count++; } @@ -4108,6 +4252,16 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) if (of_property_read_bool(np, "cirrus,fw-defer")) cs40l26->fw_mode = CS40L26_FW_MODE_NONE; + if (of_property_read_bool(np, "cirrus,vibe-state")) + cs40l26->pdata.vibe_state_reporting = true; + else + cs40l26->pdata.vibe_state_reporting = false; + + if (of_property_read_bool(np, "cirrus,bst-expl-mode-disable")) + cs40l26->pdata.expl_mode_enabled = false; + else + cs40l26->pdata.expl_mode_enabled = true; + cs40l26->pdata.vbbr_en = of_property_read_bool(np, "cirrus,vbbr-enable"); @@ -4421,6 +4575,17 @@ int cs40l26_sys_suspend_noirq(struct device *dev) } EXPORT_SYMBOL(cs40l26_sys_suspend_noirq); +void cs40l26_resume_error_handle(struct device *dev) +{ + dev_err(dev, "PM Runtime Resume failed\n"); + + pm_runtime_set_active(dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL(cs40l26_resume_error_handle); + int cs40l26_resume(struct device *dev) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); |