Commit c4d0510b authored by Stefan Binding's avatar Stefan Binding Committed by Takashi Iwai

ALSA: hda: cs35l41: Rework System Suspend to ensure correct call separation

In order to correctly pause audio on suspend, amps using external boost
require parts of the pause sequence to be called for all amps before moving
on to the next steps.
For example, as part of pausing the audio, the VSPK GPIO must be disabled,
but since this GPIO is controlled by one amp, but controls the boost for
all amps, it is required to separate the calls.
During playback this is achieved by using the pre and post playback hooks,
however during system suspend, this is not possible, so to separate the
calls, we use both the .prepare and .suspend calls to pause the audio.

Currently, for this reason, we do not restart audio on system resume.
However, we can support this by relying on the playback hook to resume
playback after system suspend.
Signed-off-by: default avatarStefan Binding <sbinding@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20230721151816.2080453-10-sbinding@opensource.cirrus.comSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 01ecc562
...@@ -595,6 +595,15 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action) ...@@ -595,6 +595,15 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
mutex_unlock(&cs35l41->fw_mutex); mutex_unlock(&cs35l41->fw_mutex);
break; break;
case HDA_GEN_PCM_ACT_CLOSE: case HDA_GEN_PCM_ACT_CLOSE:
mutex_lock(&cs35l41->fw_mutex);
if (!cs35l41->firmware_running && cs35l41->request_fw_load &&
!cs35l41->fw_request_ongoing) {
dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n");
cs35l41->fw_request_ongoing = true;
schedule_work(&cs35l41->fw_load_work);
}
mutex_unlock(&cs35l41->fw_mutex);
/* /*
* Playback must be finished for all amps before we start runtime suspend. * Playback must be finished for all amps before we start runtime suspend.
* This ensures no amps are playing back when we start putting them to sleep. * This ensures no amps are playing back when we start putting them to sleep.
...@@ -681,6 +690,25 @@ static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41) ...@@ -681,6 +690,25 @@ static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
return ret; return ret;
} }
static int cs35l41_system_suspend_prep(struct device *dev)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
dev_dbg(cs35l41->dev, "System Suspend Prepare\n");
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
dev_err_once(cs35l41->dev, "System Suspend not supported\n");
return 0; /* don't block the whole system suspend */
}
mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->playback_started)
cs35l41_hda_pause_start(dev);
mutex_unlock(&cs35l41->fw_mutex);
return 0;
}
static int cs35l41_system_suspend(struct device *dev) static int cs35l41_system_suspend(struct device *dev)
{ {
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
...@@ -693,6 +721,11 @@ static int cs35l41_system_suspend(struct device *dev) ...@@ -693,6 +721,11 @@ static int cs35l41_system_suspend(struct device *dev)
return 0; /* don't block the whole system suspend */ return 0; /* don't block the whole system suspend */
} }
mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->playback_started)
cs35l41_hda_pause_done(dev);
mutex_unlock(&cs35l41->fw_mutex);
ret = pm_runtime_force_suspend(dev); ret = pm_runtime_force_suspend(dev);
if (ret) { if (ret) {
dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret); dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret);
...@@ -738,6 +771,7 @@ static int cs35l41_system_resume(struct device *dev) ...@@ -738,6 +771,7 @@ static int cs35l41_system_resume(struct device *dev)
} }
mutex_lock(&cs35l41->fw_mutex); mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) { if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
cs35l41->fw_request_ongoing = true; cs35l41->fw_request_ongoing = true;
schedule_work(&cs35l41->fw_load_work); schedule_work(&cs35l41->fw_load_work);
...@@ -770,11 +804,6 @@ static int cs35l41_runtime_suspend(struct device *dev) ...@@ -770,11 +804,6 @@ static int cs35l41_runtime_suspend(struct device *dev)
mutex_lock(&cs35l41->fw_mutex); mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->playback_started) {
cs35l41_hda_pause_start(dev);
cs35l41_hda_pause_done(dev);
}
if (cs35l41->firmware_running) { if (cs35l41->firmware_running) {
ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap, ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
cs35l41->hw_cfg.bst_type); cs35l41->hw_cfg.bst_type);
...@@ -1641,6 +1670,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41); ...@@ -1641,6 +1670,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);
const struct dev_pm_ops cs35l41_hda_pm_ops = { const struct dev_pm_ops cs35l41_hda_pm_ops = {
RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
cs35l41_runtime_idle) cs35l41_runtime_idle)
.prepare = cs35l41_system_suspend_prep,
SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume) SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
}; };
EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41); EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment