From: Ramesh Babu ramesh.babu@intel.com
Skylake audio controller supports SPIB (Software Position in buffer) capability, which can be used to inform position of application pointer to host DMA controller. When SPIB mode is enabled, driver could write the application pointer position in SPIB register. Host DMA will make sure it won't read/write beyond bytes specified in SPIB register.
SPIB mode will be useful in low power use cases, where DSP could pre-fetch large buffers to avoid frequent wakes caused due to interrupts.
Skylake driver makes use of no_rewind flag and appl_ptr_update callback to enable and update SPIB register respectively.
Signed-off-by: Ramesh Babu ramesh.babu@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Sanyog Kale sanyog.r.kale@intel.com Signed-off-by: Sriram Periyasamy sriramx.periyasamy@intel.com --- sound/soc/intel/skylake/skl-pcm.c | 43 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index df824224261e..346f9ac8053b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -43,7 +43,8 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ SNDRV_PCM_INFO_HAS_LINK_ATIME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE, @@ -145,6 +146,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; + struct snd_pcm_runtime *runtime; int err;
hstream = snd_hdac_get_stream(bus, params->stream, @@ -170,6 +172,11 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) if (err < 0) return err;
+ /* Enable SPIB if no_rewinds flag is set */ + runtime = hdac_stream(stream)->substream->runtime; + if (runtime->no_rewinds) + snd_hdac_ext_stream_spbcap_enable(ebus, true, hstream->index); + hdac_stream(stream)->prepared = 1;
return 0; @@ -366,9 +373,14 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_stream *hstream = hdac_stream(stream);
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ if (runtime->no_rewinds) + snd_hdac_ext_stream_spbcap_enable(ebus, false, hstream->index); + snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0;
@@ -444,6 +456,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl_module_cfg *mconfig; struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dapm_widget *w; int ret;
@@ -469,6 +482,10 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + + if (runtime->no_rewinds) + snd_hdac_ext_stream_set_spib(ebus, + stream, stream->spib); }
case SNDRV_PCM_TRIGGER_START: @@ -1095,6 +1112,29 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, return 0; }
+/* Update SPIB register with application position */ +static int skl_platform_ack(struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *estream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + ssize_t appl_pos, buf_size; + u32 spib; + + /* Use spib mode only if no_rewind mode is set */ + if (!runtime->no_rewinds) + return 0; + + 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 */ + spib = (spib == 0) ? buf_size : spib; + return snd_hdac_ext_stream_set_spib(ebus, estream, spib); +} + static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { @@ -1202,6 +1242,7 @@ static const struct snd_pcm_ops skl_platform_ops = { .get_time_info = skl_get_time_info, .mmap = snd_pcm_lib_default_mmap, .page = snd_pcm_sgbuf_ops_page, + .ack = skl_platform_ack, };
static void skl_pcm_free(struct snd_pcm *pcm)