Commit 89a400bd authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown

ASoC: SOF: Intel: hda: enable DMI L1 for D0i3-compatible streams

DMI L1 entry is currently disabled whenever any capture stream is
opened to prevent xruns during pause/release. But, in
order to maximise power savings for the wake-on-voice usecase,
DMI L1 entry should be enabled for D0i3-compatible capture streams.
Introduce a new field, flags in struct sof_intel_hda_stream
that stores whether a stream is dmi_l1_compatible. All playback streams,
and D0i3-compatible capture streams are DMI L1 compatible.
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20210127020737.1088960-2-ranjani.sridharan@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent ed9ce1ed
...@@ -25,7 +25,7 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev, ...@@ -25,7 +25,7 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev,
{ {
struct hdac_ext_stream *stream; struct hdac_ext_stream *stream;
stream = hda_dsp_stream_get(sdev, cstream->direction); stream = hda_dsp_stream_get(sdev, cstream->direction, 0);
if (!stream) if (!stream)
return -EBUSY; return -EBUSY;
......
...@@ -35,7 +35,7 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig ...@@ -35,7 +35,7 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig
struct pci_dev *pci = to_pci_dev(sdev->dev); struct pci_dev *pci = to_pci_dev(sdev->dev);
int ret; int ret;
dsp_stream = hda_dsp_stream_get(sdev, direction); dsp_stream = hda_dsp_stream_get(sdev, direction, 0);
if (!dsp_stream) { if (!dsp_stream) {
dev_err(sdev->dev, "error: no stream available\n"); dev_err(sdev->dev, "error: no stream available\n");
......
...@@ -215,11 +215,25 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, ...@@ -215,11 +215,25 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
int hda_dsp_pcm_open(struct snd_sof_dev *sdev, int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *scomp = sdev->component;
struct hdac_ext_stream *dsp_stream; struct hdac_ext_stream *dsp_stream;
struct snd_sof_pcm *spcm;
int direction = substream->stream; int direction = substream->stream;
u32 flags = 0;
spcm = snd_sof_find_spcm_dai(scomp, rtd);
if (!spcm) {
dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id);
return -EINVAL;
}
dsp_stream = hda_dsp_stream_get(sdev, direction); /* All playback and D0i3 compatible streams are DMI L1 capable */
if (direction == SNDRV_PCM_STREAM_PLAYBACK ||
spcm->stream[substream->stream].d0i3_compatible)
flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE;
dsp_stream = hda_dsp_stream_get(sdev, direction, flags);
if (!dsp_stream) { if (!dsp_stream) {
dev_err(sdev->dev, "error: no stream available\n"); dev_err(sdev->dev, "error: no stream available\n");
return -ENODEV; return -ENODEV;
......
...@@ -155,7 +155,7 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, ...@@ -155,7 +155,7 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
/* get next unused stream */ /* get next unused stream */
struct hdac_ext_stream * struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
{ {
struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream; struct sof_intel_hda_stream *hda_stream;
...@@ -183,18 +183,22 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) ...@@ -183,18 +183,22 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);
/* stream found ? */ /* stream found ? */
if (!stream) if (!stream) {
dev_err(sdev->dev, "error: no free %s streams\n", dev_err(sdev->dev, "error: no free %s streams\n",
direction == SNDRV_PCM_STREAM_PLAYBACK ? direction == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture"); "playback" : "capture");
return stream;
}
hda_stream->flags = flags;
/* /*
* Disable DMI Link L1 entry when capture stream is opened. * Prevent DMI Link L1 entry for streams that don't support it.
* Workaround to address a known issue with host DMA that results * Workaround to address a known issue with host DMA that results
* in xruns during pause/release in capture scenarios. * in xruns during pause/release in capture scenarios.
*/ */
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1))
if (stream && direction == SNDRV_PCM_STREAM_CAPTURE) if (stream && !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE))
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2, HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0); HDA_VS_INTEL_EM2_L1SEN, 0);
...@@ -206,37 +210,39 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) ...@@ -206,37 +210,39 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{ {
struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *stream;
struct hdac_stream *s; struct hdac_stream *s;
bool active_capture_stream = false; bool dmi_l1_enable = true;
bool found = false; bool found = false;
spin_lock_irq(&bus->reg_lock); spin_lock_irq(&bus->reg_lock);
/* /*
* close stream matching the stream tag * close stream matching the stream tag and check if there are any open streams
* and check if there are any open capture streams. * that are DMI L1 incompatible.
*/ */
list_for_each_entry(s, &bus->stream_list, list) { list_for_each_entry(s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
hda_stream = container_of(stream, struct sof_intel_hda_stream, hda_stream);
if (!s->opened) if (!s->opened)
continue; continue;
if (s->direction == direction && s->stream_tag == stream_tag) { if (s->direction == direction && s->stream_tag == stream_tag) {
s->opened = false; s->opened = false;
found = true; found = true;
} else if (s->direction == SNDRV_PCM_STREAM_CAPTURE) { } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
active_capture_stream = true; dmi_l1_enable = false;
} }
} }
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);
/* Enable DMI L1 entry if there are no capture streams open */ /* Enable DMI L1 if permitted */
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1) && dmi_l1_enable)
if (!active_capture_stream) snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN,
HDA_VS_INTEL_EM2_L1SEN);
if (!found) { if (!found) {
dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag);
......
...@@ -42,8 +42,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) ...@@ -42,8 +42,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
int ret; int ret;
hda->dtrace_stream = hda_dsp_stream_get(sdev, hda->dtrace_stream = hda_dsp_stream_get(sdev, SNDRV_PCM_STREAM_CAPTURE, 0);
SNDRV_PCM_STREAM_CAPTURE);
if (!hda->dtrace_stream) { if (!hda->dtrace_stream) {
dev_err(sdev->dev, dev_err(sdev->dev,
......
...@@ -402,6 +402,9 @@ struct sof_intel_dsp_bdl { ...@@ -402,6 +402,9 @@ struct sof_intel_dsp_bdl {
#define SOF_HDA_PLAYBACK 0 #define SOF_HDA_PLAYBACK 0
#define SOF_HDA_CAPTURE 1 #define SOF_HDA_CAPTURE 1
/* stream flags */
#define SOF_HDA_STREAM_DMI_L1_COMPATIBLE 1
/* /*
* Time in ms for opportunistic D0I3 entry delay. * Time in ms for opportunistic D0I3 entry delay.
* This has been deliberately chosen to be long to avoid race conditions. * This has been deliberately chosen to be long to avoid race conditions.
...@@ -471,6 +474,7 @@ struct sof_intel_hda_stream { ...@@ -471,6 +474,7 @@ struct sof_intel_hda_stream {
struct hdac_ext_stream hda_stream; struct hdac_ext_stream hda_stream;
struct sof_intel_stream stream; struct sof_intel_stream stream;
int host_reserved; /* reserve host DMA channel */ int host_reserved; /* reserve host DMA channel */
u32 flags;
}; };
#define hstream_to_sof_hda_stream(hstream) \ #define hstream_to_sof_hda_stream(hstream) \
...@@ -562,7 +566,7 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev); ...@@ -562,7 +566,7 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev);
struct hdac_ext_stream * struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream, struct hdac_ext_stream *stream,
......
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