From 4f3b42e2f126f96b1e512871d7073fb10d9a7283 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jul 2023 16:18:13 +0100 Subject: [PATCH 08/11] ALSA: hda: cs35l41: Use pre and post playback hooks Use new hooks to ensure separation between play/pause actions, as required by external boost. External Boost on CS35L41 requires the amp to go through a particular sequence of steps. One of these steps involes the setting of a GPIO. This GPIO is connected to one or more of the amps, and it may control the boost for all of the amps. To ensure that the GPIO is set when it is safe to do so, and to ensure that boost is ready for the rest of the sequence to be able to continue, we must ensure that the each part of the sequence is executed for each amp before moving on to the next part of the sequence. Some of the Play and Pause actions have moved from Open to Prepare. This is because Open is not guaranteed to be called again on system resume, whereas Prepare should. Signed-off-by: Stefan Binding --- sound/pci/hda/cs35l41_hda.c | 53 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index f77583b46b6b..a482d4752b3f 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -556,37 +556,68 @@ static void cs35l41_hda_pause_done(struct device *dev) cs35l41->playback_started = false; } +static void cs35l41_hda_pre_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + switch (action) { + case HDA_GEN_PCM_ACT_CLEANUP: + mutex_lock(&cs35l41->fw_mutex); + cs35l41_hda_pause_start(dev); + mutex_unlock(&cs35l41->fw_mutex); + break; + default: + break; + } +} static void cs35l41_hda_playback_hook(struct device *dev, int action) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); switch (action) { case HDA_GEN_PCM_ACT_OPEN: + /* + * All amps must be resumed before we can start playing back. + * This ensures, for external boost, that all amps are in AMP_SAFE mode. + * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the + * other actions. + */ pm_runtime_get_sync(dev); - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_play_start(dev); - mutex_unlock(&cs35l41->fw_mutex); break; case HDA_GEN_PCM_ACT_PREPARE: mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_play_done(dev); + cs35l41_hda_play_start(dev); mutex_unlock(&cs35l41->fw_mutex); break; case HDA_GEN_PCM_ACT_CLEANUP: mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_pause_start(dev); + cs35l41_hda_pause_done(dev); mutex_unlock(&cs35l41->fw_mutex); break; case HDA_GEN_PCM_ACT_CLOSE: - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_pause_done(dev); - mutex_unlock(&cs35l41->fw_mutex); - + /* + * 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. + */ pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: - dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action); + break; + } +} + +static void cs35l41_hda_post_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + mutex_lock(&cs35l41->fw_mutex); + cs35l41_hda_play_done(dev); + mutex_unlock(&cs35l41->fw_mutex); + break; + default: break; } } @@ -1037,6 +1068,8 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas ret = cs35l41_create_controls(cs35l41); comps->playback_hook = cs35l41_hda_playback_hook; + comps->pre_playback_hook = cs35l41_hda_pre_playback_hook; + comps->post_playback_hook = cs35l41_hda_post_playback_hook; mutex_unlock(&cs35l41->fw_mutex); -- 2.41.0