[PATCH v1 07/11] ALSA: hda: cs35l41: Use pre and post playback hooks

Stefan Binding
2023-07-20T13:34:18Z

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 <sbinding@opensource.cirrus.com>
---
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 f77583b46b6b0..a482d4752b3f8 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.34.1