ASoC: Intel: Skylake: Fix DSP pipe underrun/overrun issue
authorJeeja KP <jeeja.kp@intel.com>
Tue, 27 Oct 2015 00:22:53 +0000 (09:22 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 16 Nov 2015 10:08:09 +0000 (10:08 +0000)
While rigourous testing of SKL drivers, we noticed underuns and
overuns and on debug realized that we need to change driver
handling of FE pipe startup and shutdown

We need to start DMA and then run pipe together and not split
these up. Similarly while stopping we should stop pipe and then
DMA in a sequence.

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c

index 1a9cd00c0b0a1cd855c66d0ee57f2abfd620164d..2517ec576ffc9f91797673f8253074c659df2371 100644 (file)
@@ -295,29 +295,101 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
        return skl_tplg_be_update_params(dai, &p_params);
 }
 
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
+               int cmd)
+{
+       struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct hdac_ext_stream *stream;
+       int start;
+       unsigned long cookie;
+       struct hdac_stream *hstr;
+
+       stream = get_hdac_ext_stream(substream);
+       hstr = hdac_stream(stream);
+
+       if (!hstr->prepared)
+               return -EPIPE;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               start = 1;
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+               start = 0;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&bus->reg_lock, cookie);
+
+       if (start) {
+               snd_hdac_stream_start(hdac_stream(stream), true);
+               snd_hdac_stream_timecounter_init(hstr, 0);
+       } else {
+               snd_hdac_stream_stop(hdac_stream(stream));
+       }
+
+       spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+       return 0;
+}
+
 static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
                struct snd_soc_dai *dai)
 {
        struct skl *skl = get_skl_ctx(dai->dev);
        struct skl_sst *ctx = skl->skl_sst;
        struct skl_module_cfg *mconfig;
+       int ret;
 
        mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
        if (!mconfig)
                return -EIO;
 
        switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        case SNDRV_PCM_TRIGGER_RESUME:
+               /*
+                * Start HOST DMA and Start FE Pipe.This is to make sure that
+                * there are no underrun/overrun in the case when the FE
+                * pipeline is started but there is a delay in starting the
+                * DMA channel on the host.
+                */
+               ret = skl_decoupled_trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
                return skl_run_pipe(ctx, mconfig->pipe);
+               break;
 
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        case SNDRV_PCM_TRIGGER_SUSPEND:
-               return skl_stop_pipe(ctx, mconfig->pipe);
+       case SNDRV_PCM_TRIGGER_STOP:
+               /*
+                * Stop FE Pipe first and stop DMA. This is to make sure that
+                * there are no underrun/overrun in the case if there is a delay
+                * between the two operations.
+                */
+               ret = skl_stop_pipe(ctx, mconfig->pipe);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_decoupled_trigger(substream, cmd);
+               break;
 
        default:
-               return 0;
+               return -EINVAL;
        }
+
+       return 0;
 }
 
 static int skl_link_hw_params(struct snd_pcm_substream *substream,
@@ -685,66 +757,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
-               int cmd)
-{
-       struct hdac_ext_bus *ebus = get_bus_ctx(substream);
-       struct hdac_bus *bus = ebus_to_hbus(ebus);
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct hdac_ext_stream *stream;
-       int start;
-       unsigned long cookie;
-       struct hdac_stream *hstr;
-
-       dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name);
-
-       stream = get_hdac_ext_stream(substream);
-       hstr = hdac_stream(stream);
-
-       if (!hstr->prepared)
-               return -EPIPE;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-       case SNDRV_PCM_TRIGGER_RESUME:
-               start = 1;
-               break;
-
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-               start = 0;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       spin_lock_irqsave(&bus->reg_lock, cookie);
-
-       if (start)
-               snd_hdac_stream_start(hdac_stream(stream), true);
-       else
-               snd_hdac_stream_stop(hdac_stream(stream));
-
-       if (start)
-               snd_hdac_stream_timecounter_init(hstr, 0);
-
-       spin_unlock_irqrestore(&bus->reg_lock, cookie);
-
-       return 0;
-}
 static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
 
-       if (ebus->ppcap)
-               return skl_decoupled_trigger(substream, cmd);
-       else
+       if (!ebus->ppcap)
                return skl_coupled_trigger(substream, cmd);
+
+       return 0;
 }
 
 /* calculate runtime delay from LPIB */
index 0c6e7833e6523c4be580beecdd4fd25141d8ee80..2f263ddd696deb3ac560b5da0c612554c32cdb93 100644 (file)
@@ -433,7 +433,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
 
                        /* Start sinks pipe first */
                        if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
-                               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+                               if (sink_mconfig->pipe->conn_type !=
+                                                       SKL_PIPE_CONN_TYPE_FE)
+                                       ret = skl_run_pipe(ctx,
+                                                       sink_mconfig->pipe);
                                if (ret)
                                        return ret;
                        }
@@ -475,9 +478,8 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
                return ret;
 
        /* Start source pipe last after starting all sinks */
-       ret = skl_run_pipe(ctx, src_mconfig->pipe);
-       if (ret)
-               return ret;
+       if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
+               return skl_run_pipe(ctx, src_mconfig->pipe);
 
        return 0;
 }
@@ -559,7 +561,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
                if (ret)
                        return ret;
 
-               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+               if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
+                       ret = skl_run_pipe(ctx, sink_mconfig->pipe);
        }
 
        return ret;