Commit d1730c3d authored by Jeeja KP's avatar Jeeja KP Committed by Mark Brown

ASoC: Intel: Skylake: Fix DSP pipe underrun/overrun issue

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: default avatarJeeja KP <jeeja.kp@intel.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 8724ff17
...@@ -295,29 +295,101 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, ...@@ -295,29 +295,101 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
return skl_tplg_be_update_params(dai, &p_params); 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, static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct skl *skl = get_skl_ctx(dai->dev); struct skl *skl = get_skl_ctx(dai->dev);
struct skl_sst *ctx = skl->skl_sst; struct skl_sst *ctx = skl->skl_sst;
struct skl_module_cfg *mconfig; struct skl_module_cfg *mconfig;
int ret;
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
if (!mconfig) if (!mconfig)
return -EIO; return -EIO;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME: 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); return skl_run_pipe(ctx, mconfig->pipe);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND: 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: default:
return 0; return -EINVAL;
} }
return 0;
} }
static int skl_link_hw_params(struct snd_pcm_substream *substream, 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, ...@@ -685,66 +757,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
return 0; 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, static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
int cmd) int cmd)
{ {
struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_ext_bus *ebus = get_bus_ctx(substream);
if (ebus->ppcap) if (!ebus->ppcap)
return skl_decoupled_trigger(substream, cmd);
else
return skl_coupled_trigger(substream, cmd); return skl_coupled_trigger(substream, cmd);
return 0;
} }
/* calculate runtime delay from LPIB */ /* calculate runtime delay from LPIB */
......
...@@ -433,7 +433,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, ...@@ -433,7 +433,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
/* Start sinks pipe first */ /* Start sinks pipe first */
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { 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) if (ret)
return ret; return ret;
} }
...@@ -475,9 +478,8 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, ...@@ -475,9 +478,8 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
return ret; return ret;
/* Start source pipe last after starting all sinks */ /* Start source pipe last after starting all sinks */
ret = skl_run_pipe(ctx, src_mconfig->pipe); if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
if (ret) return skl_run_pipe(ctx, src_mconfig->pipe);
return ret;
return 0; return 0;
} }
...@@ -559,6 +561,7 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, ...@@ -559,6 +561,7 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
if (ret) if (ret)
return ret; return ret;
if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
ret = skl_run_pipe(ctx, sink_mconfig->pipe); ret = skl_run_pipe(ctx, sink_mconfig->pipe);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment