ASoC: SOF: Intel: add .ack support for HDaudio platforms
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Fri, 19 Nov 2021 23:08:52 +0000 (17:08 -0600)
committerMark Brown <broonie@kernel.org>
Wed, 24 Nov 2021 12:57:21 +0000 (12:57 +0000)
When we disable rewinds, then the .ack can be used to program SPIB
with the application pointer, which allows the HDaudio DMA to save
power by opportunistically bursting data transfers when the path to
memory is enabled (and conversely to shut it down when there are no
transfer requests).

The SPIB register can only be programmed with incremental values with
wrap-around after the DMA RUN bits are set. For simplicity, we set the
INFO_NO_REWINDS flag in the .open callback when we already need to
program the SNDRV_PCM_INFO_SYNC_APPLPTR flag.

Rewinds are not used by many applications. One notable application
using rewinds is PulseAudio. Practical experiments with
Ubuntu/PulseAudio default settings did not show any audible issues,
but the user may hear volume changes and notification with a delay,
depending on the size of the ring buffer and latency constraints.

The choice of disabling rewinds is exposed as a kernel parameter and
not a Kconfig option to avoid any undesirable side-effects.

Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Co-developed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20211119230852.206310-5-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/intel/apl.c
sound/soc/sof/intel/cnl.c
sound/soc/sof/intel/hda-pcm.c
sound/soc/sof/intel/hda-stream.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/icl.c
sound/soc/sof/intel/tgl.c

index 1baf0fddeb3d514bec7909086a8d12a0fac068a8..8778f46f1d37f153a67d951e398cd444670fd0fa 100644 (file)
@@ -78,6 +78,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
        .pcm_hw_free    = hda_dsp_stream_hw_free,
        .pcm_trigger    = hda_dsp_pcm_trigger,
        .pcm_pointer    = hda_dsp_pcm_pointer,
+       .pcm_ack        = hda_dsp_pcm_ack,
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
        /* probe callbacks */
index d455272bfc8ebd35d94dacdebe6ea79db4010ba2..04daaa6100f12450a138bfe525a639e9300f04f7 100644 (file)
@@ -283,6 +283,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
        .pcm_hw_free    = hda_dsp_stream_hw_free,
        .pcm_trigger    = hda_dsp_pcm_trigger,
        .pcm_pointer    = hda_dsp_pcm_pointer,
+       .pcm_ack        = hda_dsp_pcm_ack,
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
        /* probe callbacks */
index cc8ddef37f37b51c81d231d96432e3090eeffd0f..974383cd0440740c4b904b7994bf3976f6df1df4 100644 (file)
@@ -32,6 +32,10 @@ static bool hda_always_enable_dmi_l1;
 module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
 MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
 
+static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS);
+module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
+MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
+
 u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate)
 {
        switch (rate) {
@@ -120,8 +124,11 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
                return ret;
        }
 
-       /* disable SPIB, to enable buffer wrap for stream */
-       hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
+       /* enable SPIB when rewinds are disabled */
+       if (hda_disable_rewinds)
+               hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0);
+       else
+               hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
 
        /* update no_stream_position flag for ipc params */
        if (hda && hda->no_ipc_position) {
@@ -140,6 +147,29 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
        return 0;
 }
 
+/* update SPIB register with appl position */
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *hstream = substream->runtime->private_data;
+       struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       ssize_t appl_pos, buf_size;
+       u32 spib;
+
+       appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr);
+       buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+
+       spib = appl_pos % buf_size;
+
+       /* Allowable value for SPIB is 1 byte to max buffer size */
+       if (!spib)
+               spib = buf_size;
+
+       sof_io_write(sdev, hext_stream->spib_addr, spib);
+
+       return 0;
+}
+
 int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
                        struct snd_pcm_substream *substream, int cmd)
 {
@@ -234,6 +264,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
                return -EINVAL;
        }
 
+       /*
+        * if we want the .ack to work, we need to prevent the control from being mapped.
+        * The status can still be mapped.
+        */
+       if (hda_disable_rewinds)
+               runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR;
+
        /*
         * All playback streams are DMI L1 capable, capture streams need
         * pause push/release to be disabled
index 1d845c2cbc33d45ec3a4559aab5ee493d2a7c4dd..b6f0378153441c3a0ba1658d9ccc37c391abd33d 100644 (file)
@@ -655,6 +655,8 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
                                        SOF_HDA_REG_PP_PPCTL, mask, 0);
        spin_unlock_irq(&bus->reg_lock);
 
+       hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0);
+
        stream->substream = NULL;
 
        return 0;
index 646f5d4dc8824fbd9a5600a19b00cd28de97e02a..8ed4031ca0078a9dd8dd8c90a948d3ab3a03c811 100644 (file)
@@ -534,6 +534,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
                        struct snd_pcm_substream *substream, int cmd);
 snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
                                      struct snd_pcm_substream *substream);
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
 
 /*
  * DSP Stream Operations.
index 6c5422157ec89b42134edf1c75d07351031bf846..343c1af7c4532fc1edc563151ef28921e3eff5dc 100644 (file)
@@ -77,6 +77,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
        .pcm_hw_free    = hda_dsp_stream_hw_free,
        .pcm_trigger    = hda_dsp_pcm_trigger,
        .pcm_pointer    = hda_dsp_pcm_pointer,
+       .pcm_ack        = hda_dsp_pcm_ack,
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
        /* probe callbacks */
index 237e92e790b7244f3953c4d8db600a5771ef6902..7f7929c5cb88fd2638f61010429fbc606ac1b553 100644 (file)
@@ -113,6 +113,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
        .pcm_hw_free    = hda_dsp_stream_hw_free,
        .pcm_trigger    = hda_dsp_pcm_trigger,
        .pcm_pointer    = hda_dsp_pcm_pointer,
+       .pcm_ack        = hda_dsp_pcm_ack,
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
        /* probe callbacks */