summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/cs35l56.c
diff options
context:
space:
mode:
authorRichard Fitzgerald <rf@opensource.cirrus.com>2023-04-11 18:25:28 +0300
committerMark Brown <broonie@kernel.org>2023-04-12 19:34:36 +0300
commit59322d35179987e85b593e504fd334de8683c835 (patch)
tree1f5b4c06b109bcda54e14bc3538077be11b7468d /sound/soc/codecs/cs35l56.c
parent39a594dc0b4ac949edf221db33c7061c45e2c90b (diff)
downloadlinux-59322d35179987e85b593e504fd334de8683c835.tar.xz
ASoC: cs35l56: Re-patch firmware after system suspend
Check during cs35l56_system_resume() whether the firmware patch must be applied again. The FIRMWARE_MISSING flag in the PROTECTION_STATUS register indicates whether the firmware has been patched. In non-secure mode the FIRMWARE_MISSING flag is cleared at the end of dsp_work(). If it is set after system-resume we know that dsp_work() must be run again. In secure mode the pre-OS loader will have done the secure patching and cleared the FIRMWARE_MISSING flag. So this flag does not tell us whether firmware memory was lost. But the driver could only be downloading non-secure tunings, which is always safe to do. If the driver has control of RESET we will have asserted it during suspend so the firmware patch will have been lost. The driver would only have control of RESET in non-secure mode. Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Link: https://lore.kernel.org/r/168122674550.26.8545058503709956172@mailman-core.alsa-project.org Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/codecs/cs35l56.c')
-rw-r--r--sound/soc/codecs/cs35l56.c67
1 files changed, 66 insertions, 1 deletions
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index eb85c27ab087..18e341744839 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -946,6 +946,7 @@ static void cs35l56_dsp_work(struct work_struct *work)
goto err_unlock;
}
+ regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING);
cs35l56->fw_patched = true;
err_unlock:
@@ -1026,6 +1027,8 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
.num_controls = ARRAY_SIZE(cs35l56_controls),
.set_bias_level = cs35l56_set_bias_level,
+
+ .suspend_bias_off = 1, /* see cs35l56_system_resume() */
};
static const struct reg_sequence cs35l56_hibernate_seq[] = {
@@ -1156,6 +1159,47 @@ err:
}
EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE);
+static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ /* Nothing to re-patch if we haven't done any patching yet. */
+ if (!cs35l56->fw_patched)
+ return false;
+
+ /*
+ * If we have control of RESET we will have asserted it so the firmware
+ * will need re-patching.
+ */
+ if (cs35l56->reset_gpio)
+ return true;
+
+ /*
+ * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+ * can't be used here to test for memory retention.
+ * Assume that tuning must be re-loaded.
+ */
+ if (cs35l56->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56->dev);
+ if (ret) {
+ dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val);
+ if (ret)
+ dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56->dev);
+
+ return ret;
+}
+
int cs35l56_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
@@ -1273,7 +1317,28 @@ int cs35l56_system_resume(struct device *dev)
if (cs35l56->irq)
enable_irq(cs35l56->irq);
- return ret;
+ if (ret)
+ return ret;
+
+ /* Firmware won't have been loaded if the component hasn't probed */
+ if (!cs35l56->component)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(cs35l56);
+ dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret);
+ if (ret < 1)
+ return ret;
+
+ cs35l56->fw_patched = false;
+ init_completion(&cs35l56->dsp_ready_completion);
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ /*
+ * suspend_bias_off ensures we are now in BIAS_OFF so there will be
+ * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching.
+ */
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cs35l56_system_resume);