[x86] ASoC: SOF sound driver: back port fixes to prevent crash

Prevents kernel crash on firmware reload

  Closes: #940726

Signed-off-by: Héctor Orón Martínez <zumbi@debian.org>
Tested-by: Mark Pearson <mpearson@lenovo.com>
This commit is contained in:
Héctor Orón Martínez
2019-10-08 11:36:00 +02:00
parent a99c0c8c28
commit 96a39e6c5e
10 changed files with 1450 additions and 0 deletions

5
debian/changelog vendored
View File

@@ -8,6 +8,11 @@ linux (5.2.17-2) UNRELEASED; urgency=medium
* KEYS: Re-enable SECONDARY_TRUSTED_KEYRING, dropped in 5.2.6-1 by
mis-merge (Closes: #935945)
[ Héctor Orón Martínez ]
* [x86] ASoC: SOF sound driver: back port fixes to prevent kernel crash on
firmware reload (Closes: #940726)
- Based on patchset by Mark Pearson.
-- Romain Perier <romain.perier@gmail.com> Mon, 30 Sep 2019 16:09:14 +0200
linux (5.2.17-1) unstable; urgency=medium

View File

@@ -247,3 +247,68 @@ CONFIG_ZONE_DEVICE=y
##
CONFIG_LSM_MMAP_MIN_ADDR=65536
##
## file: sound/soc/sof/Kconfig
##
CONFIG_SND_SOC_SOF_TOPLEVEL=y
CONFIG_SND_SOC_SOF_PCI=m
CONFIG_SND_SOC_SOF_ACPI=m
##
## file: sound/soc/sof/intel/Kconfig
##
CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL=y
CONFIG_SND_SOC_SOF_BAYTRAIL_SUPPORT=y
CONFIG_SND_SOC_SOF_BROADWELL_SUPPORT=y
CONFIG_SND_SOC_SOF_MERRIFIELD_SUPPORT=y
CONFIG_SND_SOC_SOF_APOLLOLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_GEMINILAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_CANNONLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_COFFEELAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_ICELAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_HDA_LINK=y
CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC=y
##
## file: sound/soc/intel/Kconfig
##
CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC=y
##
## file: sound/soc/intel/boards/Kconfig
##
CONFIG_SND_SOC_INTEL_HASWELL_MACH=m
CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH=m
CONFIG_SND_SOC_INTEL_BROADWELL_MACH=m
CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH=m
CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH=m
CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH=m
CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH=m
CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH=m
CONFIG_SND_SOC_INTEL_BXT_RT298_MACH=m
CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH=m
CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH=m
CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH=m
##
## file: sound/pci/hda/Kconfig
##
CONFIG_SND_HDA_INTEL=m
CONFIG_SND_HDA_INPUT_BEEP=y
CONFIG_SND_HDA_CODEC_REALTEK=m
CONFIG_SND_HDA_CODEC_ANALOG=m
CONFIG_SND_HDA_CODEC_SIGMATEL=m
CONFIG_SND_HDA_CODEC_VIA=m
CONFIG_SND_HDA_CODEC_HDMI=m
CONFIG_SND_HDA_CODEC_CIRRUS=m
CONFIG_SND_HDA_CODEC_CONEXANT=m
CONFIG_SND_HDA_CODEC_CA0110=m
CONFIG_SND_HDA_CODEC_CA0132=m
CONFIG_SND_HDA_CODEC_CMEDIA=m
CONFIG_SND_HDA_CODEC_SI3054=m
CONFIG_SND_HDA_GENERIC=m

View File

@@ -0,0 +1,25 @@
commit f5dbba9fee801f4678a50d92c785f7f24d4ee2c6
Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Date: Wed Jun 12 12:23:34 2019 -0500
ASoC: SOF: Intel: hda: add new macro hstream_to_sof_hda_stream()
Add a new macro to get sof_intel_hda_stream from hdac_ext_stream.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 502b0a3c2e3c..376b1ca51e2b 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -415,6 +415,9 @@ struct sof_intel_hda_stream {
int hw_params_upon_resume; /* set up hw_params upon resume */
};
+#define hstream_to_sof_hda_stream(hstream) \
+ container_of(hstream, struct sof_intel_hda_stream, hda_stream)
+
#define bus_to_sof_hda(bus) \
container_of(bus, struct sof_intel_hda_dev, hbus.core)

View File

@@ -0,0 +1,168 @@
commit 7077a07a72d38a78040873bbc13a77d1e45f8aa0
Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Date: Wed Jun 12 12:23:38 2019 -0500
ASoC: SOF: Intel: hda: release link DMA for paused streams during suspend
Paused streams do not get suspended when the system enters S3.
So, clear and release link DMA channel for such streams in the
hda_dsp_set_hw_params_upon_resume() callback. Also, invalidate
the link DMA channel in the DAI config before restoring the
dai config upon resume. Also, modify the signature for the
set_hw_params_upon_resume() op to return an int.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 5b73115a0b78..c6eea3079ab7 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -454,18 +454,45 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state)
return 0;
}
-void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *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;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_link *link;
+ const char *name;
+ int stream_tag;
+#endif
+
/* set internal flag for BE */
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);
hda_stream->hw_params_upon_resume = 1;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /*
+ * clear and release stream. This should already be taken care
+ * for running streams when the SUSPEND trigger is called.
+ * But paused streams do not get suspended, so this needs to be
+ * done explicitly during suspend.
+ */
+ if (stream->link_substream) {
+ rtd = snd_pcm_substream_chip(stream->link_substream);
+ name = rtd->codec_dai->component->name;
+ link = snd_hdac_ext_bus_get_link(bus, name);
+ if (!link)
+ return -EINVAL;
+ stream_tag = hdac_stream(stream)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(stream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ }
+#endif
}
+ return 0;
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 2862b4b3b07c..327621ef5cf3 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -451,7 +451,7 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state);
int hda_dsp_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state);
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev);
-void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
+int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags);
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
void hda_ipc_dump(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 80fc3b374c2b..a23297353750 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -134,10 +134,11 @@ static inline int snd_sof_dsp_runtime_suspend(struct snd_sof_dev *sdev,
return 0;
}
-static inline void snd_sof_dsp_hw_params_upon_resume(struct snd_sof_dev *sdev)
+static inline int snd_sof_dsp_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
if (sof_ops(sdev)->set_hw_params_upon_resume)
- sof_ops(sdev)->set_hw_params_upon_resume(sdev);
+ return sof_ops(sdev)->set_hw_params_upon_resume(sdev);
+ return 0;
}
static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq)
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index b7843f02ef67..8eeb3a1029f2 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -153,6 +153,15 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev)
continue;
}
+ /*
+ * The link DMA channel would be invalidated for running
+ * streams but not for streams that were in the PAUSED
+ * state during suspend. So invalidate it here before setting
+ * the dai config in the DSP.
+ */
+ if (config->type == SOF_DAI_INTEL_HDA)
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
ret = sof_ipc_tx_message(sdev->ipc,
config->hdr.cmd, config,
config->hdr.size,
@@ -204,7 +213,7 @@ static int sof_send_pm_ipc(struct snd_sof_dev *sdev, int cmd)
sizeof(pm_ctx), &reply, sizeof(reply));
}
-static void sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
struct snd_pcm_substream *substream;
struct snd_sof_pcm *spcm;
@@ -229,7 +238,7 @@ static void sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
}
/* set internal flag for BE */
- snd_sof_dsp_hw_params_upon_resume(sdev);
+ return snd_sof_dsp_hw_params_upon_resume(sdev);
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
@@ -333,8 +342,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
snd_sof_release_trace(sdev);
/* set restore_stream for all streams during system suspend */
- if (!runtime_suspend)
- sof_set_hw_params_upon_resume(sdev);
+ if (!runtime_suspend) {
+ ret = sof_set_hw_params_upon_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: setting hw_params flag during suspend %d\n",
+ ret);
+ return ret;
+ }
+ }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
/* cache debugfs contents during runtime suspend */
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index b80d93e5df2f..cf1b047f8cb6 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -172,7 +172,7 @@ struct snd_sof_dsp_ops {
int (*runtime_suspend)(struct snd_sof_dev *sof_dev,
int state); /* optional */
int (*runtime_resume)(struct snd_sof_dev *sof_dev); /* optional */
- void (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */
+ int (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */
/* DSP clocking */
int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); /* optional */

View File

@@ -0,0 +1,47 @@
commit 7623ae793c28cc0928c5d1292542dbb92fc2e9e2
Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Date: Wed Jun 12 12:23:33 2019 -0500
ASoC: SOF: Intel: hda: save handle to sdev in sof_intel_hda_stream
Add a snd_sof_dev member to sof_intel_hda_stream. This will be
used to access the snd_sof_dev during link hw_params callback.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index c92006f89499..1cd94e7631a8 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -564,6 +564,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
@@ -617,6 +619,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
/* we always have DSP support */
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 6c7dee2627d0..502b0a3c2e3c 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -409,6 +409,7 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s)
}
struct sof_intel_hda_stream {
+ struct snd_sof_dev *sdev;
struct hdac_ext_stream hda_stream;
struct sof_intel_stream stream;
int hw_params_upon_resume; /* set up hw_params upon resume */

View File

@@ -0,0 +1,516 @@
commit bdf4ad3fd01f5dc53c5d6d3b17afc98cd76d8988
Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Date: Wed Jun 12 12:23:36 2019 -0500
ASoC: SOF: Intel: hda: assign link DMA channel at run-time
The recommended HDA HW programming sequence for setting
the DMA format requires that the link DMA and host DMA
channels be coupled before setting the format. This
change means that host DMA or link DMA channels be
reserved even if only one is used.
Statically assigned link DMA channels would mean that
all the corresponding host DMA channels will need to be
reserved, leaving only a few channels available at run-time.
So, the suggestion here is to switch to dynamically assigning
both host DMA channels and link DMA channels are run-time.
The host DMA channel is assigned when the pcm
is opened as before. While choosing the link DMA channel,
if the host DMA channel corresponding to the link DMA channel
is already taken, the proposed method checks to make
sure that the BE is connected to the FE that has been assigned
this host DMA channel. Once the link DMA channel is assigned,
an IPC is sent to the DSP to set the link DMA channel.
The link DMA channel is freed during hw_free() and also in the
SUSPEND trigger callback. It will be re-assigned when hw_params
are set upon resume.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index e1decf25aeac..c270fd7a0878 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -30,62 +30,84 @@ struct hda_pipe_params {
};
/*
- * Unlike GP dma, there is a set of stream registers in hda controller
- * to control the link dma channels. Each register controls one link
- * dma channel and the relation is fixed. To make sure FW uses correct
- * link dma channels, host allocates stream registers and sends the
- * corresponding link dma channels to FW to allocate link dma channel
- *
- * FIXME: this API is abused in the sense that tx_num and rx_num are
- * passed as arguments, not returned. We need to find a better way to
- * retrieve the stream tag allocated for the link DMA
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
*/
-static int hda_link_dma_get_channels(struct snd_soc_dai *dai,
- unsigned int *tx_num,
- unsigned int *tx_slot,
- unsigned int *rx_num,
- unsigned int *rx_slot)
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+ int dir, int stream_tag)
{
- struct hdac_bus *bus;
- struct hdac_ext_stream *stream;
- struct snd_pcm_substream substream;
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
-
- bus = sof_to_bus(sdev);
-
- memset(&substream, 0, sizeof(substream));
- if (*tx_num == 1) {
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
- stream = snd_hdac_ext_stream_assign(bus, &substream,
- HDAC_EXT_STREAM_TYPE_LINK);
- if (!stream) {
- dev_err(bus->dev, "error: failed to find a free hda ext stream for playback");
- return -EBUSY;
- }
+ struct snd_pcm_substream *fe_substream;
+ struct hdac_stream *fe_hstream;
+ struct snd_soc_dpcm *dpcm;
+
+ for_each_dpcm_fe(rtd, dir, dpcm) {
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+ fe_hstream = fe_substream->runtime->private_data;
+ if (fe_hstream->stream_tag == stream_tag)
+ return true;
+ }
- snd_soc_dai_set_dma_data(dai, &substream, stream);
- *tx_slot = hdac_stream(stream)->stream_tag - 1;
+ return false;
+}
+
+static struct hdac_ext_stream *
+ hda_link_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *stream = NULL;
- dev_dbg(bus->dev, "link dma channel %d for playback", *tx_slot);
+ int stream_dir = substream->stream;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
}
- if (*rx_num == 1) {
- substream.stream = SNDRV_PCM_STREAM_CAPTURE;
- stream = snd_hdac_ext_stream_assign(bus, &substream,
- HDAC_EXT_STREAM_TYPE_LINK);
- if (!stream) {
- dev_err(bus->dev, "error: failed to find a free hda ext stream for capture");
- return -EBUSY;
+ list_for_each_entry(stream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hstream =
+ stream_to_hdac_ext_stream(stream);
+ if (stream->direction != substream->stream)
+ continue;
+
+ hda_stream = hstream_to_sof_hda_stream(hstream);
+
+ /* check if available */
+ if (!hstream->link_locked) {
+ if (stream->opened) {
+ /*
+ * check if the stream tag matches the stream
+ * tag of one of the connected FEs
+ */
+ if (hda_check_fes(rtd, stream_dir,
+ stream->stream_tag)) {
+ res = hstream;
+ break;
+ }
+ } else {
+ res = hstream;
+ break;
+ }
}
+ }
- snd_soc_dai_set_dma_data(dai, &substream, stream);
- *rx_slot = hdac_stream(stream)->stream_tag - 1;
-
- dev_dbg(bus->dev, "link dma channel %d for capture", *rx_slot);
+ if (res) {
+ /*
+ * Decouple host and link DMA. The decoupled flag
+ * is updated in snd_hdac_ext_stream_decouple().
+ */
+ if (!res->decoupled)
+ snd_hdac_ext_stream_decouple(bus, res, true);
+ spin_lock_irq(&bus->reg_lock);
+ res->link_locked = 1;
+ res->link_substream = substream;
+ spin_unlock_irq(&bus->reg_lock);
}
- return 0;
+ return res;
}
static int hda_link_dma_params(struct hdac_ext_stream *stream,
@@ -122,6 +144,51 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
return 0;
}
+/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
+static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
+ const char *dai_name, int channel, int dir)
+{
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
+ if (!sof_dai->cpu_dai_name)
+ continue;
+
+ if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
+ dir == sof_dai->comp_dai.direction) {
+ config = sof_dai->dai_config;
+
+ if (!config) {
+ dev_err(hda_stream->sdev->dev,
+ "error: no config for DAI %s\n",
+ sof_dai->name);
+ return -EINVAL;
+ }
+
+ /* update config with stream tag */
+ config->hda.link_dma_ch = channel;
+
+ /* send IPC */
+ ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
+ config->hdr.cmd,
+ config,
+ config->hdr.size,
+ &reply, sizeof(reply));
+
+ if (ret < 0)
+ dev_err(hda_stream->sdev->dev,
+ "error: failed to set dai config for %s\n",
+ sof_dai->name);
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -135,20 +202,31 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct hda_pipe_params p_params = {0};
struct hdac_ext_link *link;
int stream_tag;
+ int ret;
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ link_dev = hda_link_stream_assign(bus, substream);
+ if (!link_dev)
+ return -EBUSY;
+
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
+
+ /* update the DSP with the new tag */
+ ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
+ substream->stream);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
- hda_stream = container_of(link_dev, struct sof_intel_hda_stream,
- hda_stream);
hda_stream->hw_params_upon_resume = 0;
link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
if (!link)
return -EINVAL;
- stream_tag = hdac_stream(link_dev)->stream_tag;
-
- /* set the stream tag in the codec dai dma params */
+ /* set the stream tag in the codec dai dma params */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
else
@@ -181,8 +259,7 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
int stream = substream->stream;
- hda_stream = container_of(link_dev, struct sof_intel_hda_stream,
- hda_stream);
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
/* setup hw_params again only if resuming from system suspend */
if (!hda_stream->hw_params_upon_resume)
@@ -199,8 +276,24 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
{
struct hdac_ext_stream *link_dev =
snd_soc_dai_get_dma_data(dai, substream);
+ struct sof_intel_hda_stream *hda_stream;
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_link *link;
+ struct hdac_stream *hstream;
+ struct hdac_bus *bus;
+ int stream_tag;
int ret;
+ hstream = substream->runtime->private_data;
+ bus = hstream->bus;
+ rtd = snd_pcm_substream_chip(substream);
+
+ link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
+ if (!link)
+ return -EINVAL;
+
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
+
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
@@ -217,8 +310,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_link_stream_start(link_dev);
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ /*
+ * clear and release link DMA channel. It will be assigned when
+ * hw_params is set up again after resume.
+ */
+ ret = hda_link_config_ipc(hda_stream, dai->name,
+ DMA_CHAN_INVALID, substream->stream);
+ if (ret < 0)
+ return ret;
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(link_dev,
+ HDAC_EXT_STREAM_TYPE_LINK);
+
+ /* fallthrough */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
snd_hdac_ext_link_stream_clear(link_dev);
break;
@@ -228,62 +335,38 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-/*
- * FIXME: This API is also abused since it's used for two purposes.
- * when the substream argument is NULL this function is used for cleanups
- * that aren't necessarily required, and called explicitly by handling
- * ASoC core structures, which is not recommended.
- * This part will be reworked in follow-up patches.
- */
static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- const char *name;
unsigned int stream_tag;
+ struct sof_intel_hda_stream *hda_stream;
struct hdac_bus *bus;
struct hdac_ext_link *link;
struct hdac_stream *hstream;
- struct hdac_ext_stream *stream;
struct snd_soc_pcm_runtime *rtd;
struct hdac_ext_stream *link_dev;
- struct snd_pcm_substream pcm_substream;
-
- memset(&pcm_substream, 0, sizeof(pcm_substream));
- if (substream) {
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = snd_pcm_substream_chip(substream);
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
- snd_hdac_ext_stream_decouple(bus, link_dev, false);
- name = rtd->codec_dai->component->name;
- link = snd_hdac_ext_bus_get_link(bus, name);
- if (!link)
- return -EINVAL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
+ int ret;
- link_dev->link_prepared = 0;
- } else {
- /* release all hda streams when dai link is unloaded */
- pcm_substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
- stream = snd_soc_dai_get_dma_data(dai, &pcm_substream);
- if (stream) {
- snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL);
- snd_hdac_ext_stream_release(stream,
- HDAC_EXT_STREAM_TYPE_LINK);
- }
+ hstream = substream->runtime->private_data;
+ bus = hstream->bus;
+ rtd = snd_pcm_substream_chip(substream);
+ link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
- pcm_substream.stream = SNDRV_PCM_STREAM_CAPTURE;
- stream = snd_soc_dai_get_dma_data(dai, &pcm_substream);
- if (stream) {
- snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL);
- snd_hdac_ext_stream_release(stream,
- HDAC_EXT_STREAM_TYPE_LINK);
- }
- }
+ /* free the link DMA channel in the FW */
+ ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
+ substream->stream);
+ if (ret < 0)
+ return ret;
+
+ link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
+ if (!link)
+ return -EINVAL;
+
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
+ link_dev->link_prepared = 0;
return 0;
}
@@ -293,7 +376,6 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
.hw_free = hda_link_hw_free,
.trigger = hda_link_pcm_trigger,
.prepare = hda_link_pcm_prepare,
- .get_channel_map = hda_link_dma_get_channels,
};
#endif
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 8c3ac149bbb9..b80d93e5df2f 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -56,6 +56,8 @@
#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT)
+#define DMA_CHAN_INVALID 0xFFFFFFFF
+
struct snd_sof_dev;
struct snd_sof_ipc_msg;
struct snd_sof_ipc;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 178256e338b1..432ae343f960 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -2571,9 +2571,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
*/
static int sof_link_hda_process(struct snd_sof_dev *sdev,
struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config,
- int tx_slot,
- int rx_slot)
+ struct sof_ipc_dai_config *config)
{
struct sof_ipc_reply reply;
u32 size = sizeof(*config);
@@ -2586,22 +2584,11 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev,
continue;
if (strcmp(link->name, sof_dai->name) == 0) {
- if (sof_dai->comp_dai.direction ==
- SNDRV_PCM_STREAM_PLAYBACK) {
- if (!link->dpcm_playback)
- return -EINVAL;
-
- config->hda.link_dma_ch = tx_slot;
- } else {
- if (!link->dpcm_capture)
- return -EINVAL;
-
- config->hda.link_dma_ch = rx_slot;
- }
-
config->dai_index = sof_dai->comp_dai.dai_index;
found = 1;
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
/* save config in dai component */
sof_dai->dai_config = kmemdup(config, size, GFP_KERNEL);
if (!sof_dai->dai_config)
@@ -2648,10 +2635,6 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_private *private = &cfg->priv;
struct snd_soc_dai *dai;
u32 size = sizeof(*config);
- u32 tx_num = 0;
- u32 tx_slot = 0;
- u32 rx_num = 0;
- u32 rx_slot = 0;
int ret;
/* init IPC */
@@ -2677,22 +2660,7 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- if (link->dpcm_playback)
- tx_num = 1;
-
- if (link->dpcm_capture)
- rx_num = 1;
-
- ret = snd_soc_dai_get_channel_map(dai, &tx_num, &tx_slot,
- &rx_num, &rx_slot);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to get dma channel for HDA%d\n",
- config->dai_index);
-
- return ret;
- }
-
- ret = sof_link_hda_process(sdev, link, config, tx_slot, rx_slot);
+ ret = sof_link_hda_process(sdev, link, config);
if (ret < 0)
dev_err(sdev->dev, "error: failed to process hda dai link %s",
link->name);
@@ -2819,17 +2787,6 @@ static int sof_link_hda_unload(struct snd_sof_dev *sdev,
return -EINVAL;
}
- /*
- * FIXME: this call to hw_free is mainly to release the link DMA ID.
- * This is abusing the API and handling SOC internals is not
- * recommended. This part will be reworked.
- */
- if (dai->driver->ops->hw_free)
- ret = dai->driver->ops->hw_free(NULL, dai);
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to free hda resource for %s\n",
- link->name);
-
return ret;
}

View File

@@ -0,0 +1,516 @@
commit 0c888baba8e041c92c5c1882f25b8df5c29bff9f
Author: Bard Liao <yung-chuan.liao@linux.intel.com>
Date: Wed Jun 12 12:01:48 2019 -0500
ASoC: SOF: dont wake dsp up in kcontrol IO
Always get kcontrol value from cache, set kcontrol value to DSP
when DSP is active. Kcontrol values will be restored when DSP boot up.
We will set the default value of kcontrol in sof_complete to make sure
the value is align with firmware.
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index 84e2cbfbbcbb..a4983f90ff5b 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -39,26 +39,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *sm =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: volume get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the mixer data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_VOLUME,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
@@ -66,12 +48,6 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
ipc_to_mixer(cdata->chanv[i].value,
scontrol->volume_table, sm->max + 1);
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: volume get failed to idle %d\n",
- err);
return 0;
}
@@ -84,16 +60,6 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: volume put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -104,18 +70,13 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of mixer updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_VOLUME,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: volume put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_VOLUME,
+ true);
+
return 0;
}
@@ -125,37 +86,13 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *sm =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: switch get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the mixer data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_SWITCH,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.integer.value[i] = cdata->chanv[i].value;
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: switch get failed to idle %d\n",
- err);
return 0;
}
@@ -168,16 +105,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: switch put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -186,18 +113,13 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of mixer updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_SWITCH,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: switch put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_SWITCH,
+ true);
+
return 0;
}
@@ -207,37 +129,13 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct soc_enum *se =
(struct soc_enum *)kcontrol->private_value;
struct snd_sof_control *scontrol = se->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: enum get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the enum data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_ENUM,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: enum get failed to idle %d\n",
- err);
return 0;
}
@@ -250,16 +148,6 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: enum put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -268,18 +156,13 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of enum updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_ENUM,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: enum put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_ENUM,
+ true);
+
return 0;
}
@@ -293,7 +176,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->data;
size_t size;
- int ret, err;
+ int ret = 0;
if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(sdev->dev,
@@ -302,22 +185,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the binary data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_DATA,
- SOF_CTRL_TYPE_DATA_GET,
- scontrol->cmd,
- false);
-
size = data->size + sizeof(*data);
if (size > be->max) {
dev_err_ratelimited(sdev->dev,
@@ -331,12 +198,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
memcpy(ucontrol->value.bytes.data, data, size);
out:
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes get failed to idle %d\n",
- err);
return ret;
}
@@ -350,7 +211,6 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->data;
size_t size = data->size + sizeof(*data);
- int ret, err;
if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(sdev->dev,
@@ -366,32 +226,18 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
/* notify DSP of byte control updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes put failed to idle %d\n",
- err);
- return ret;
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_DATA,
+ SOF_CTRL_TYPE_DATA_SET,
+ scontrol->cmd,
+ true);
+
+ return 0;
}
int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
@@ -406,8 +252,6 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_tlv header;
const struct snd_ctl_tlv __user *tlvd =
(const struct snd_ctl_tlv __user *)binary_data;
- int ret;
- int err;
/*
* The beginning of bytes data contains a header from where
@@ -453,30 +297,15 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
/* notify DSP of byte control updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_DATA,
+ SOF_CTRL_TYPE_DATA_SET,
+ scontrol->cmd,
+ true);
- return ret;
+ return 0;
}
int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
@@ -492,17 +321,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_tlv __user *tlvd =
(struct snd_ctl_tlv __user *)binary_data;
int data_size;
- int err;
- int ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
+ int ret = 0;
/*
* Decrement the limit by ext bytes header size to
@@ -514,13 +333,6 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
cdata->data->magic = SOF_ABI_MAGIC;
cdata->data->abi = SOF_ABI_VERSION;
- /* get all the component data from DSP */
- ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_DATA,
- SOF_CTRL_TYPE_DATA_GET,
- scontrol->cmd,
- false);
-
/* Prevent read of other kernel data or possibly corrupt response */
data_size = cdata->data->size + sizeof(const struct sof_abi_hdr);
@@ -543,11 +355,5 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
ret = -EFAULT;
out:
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext get failed to idle %d\n",
- err);
return ret;
}
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 8e00f829bfdb..aaf459af89d0 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -3016,6 +3016,49 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
return ret;
}
+/* Function to set the initial value of SOF kcontrols.
+ * The value will be stored in scontrol->control_data
+ */
+static int snd_sof_cache_kcontrol_val(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_control *scontrol = NULL;
+ int ipc_cmd, ctrl_type;
+ int ret = 0;
+
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+
+ /* notify DSP of kcontrol values */
+ switch (scontrol->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_ENUM:
+ case SOF_CTRL_CMD_SWITCH:
+ ipc_cmd = SOF_IPC_COMP_GET_VALUE;
+ ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ ipc_cmd = SOF_IPC_COMP_GET_DATA;
+ ctrl_type = SOF_CTRL_TYPE_DATA_GET;
+ break;
+ default:
+ dev_err(sdev->dev,
+ "error: Invalid scontrol->cmd: %d\n",
+ scontrol->cmd);
+ return -EINVAL;
+ }
+ ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ ipc_cmd, ctrl_type,
+ scontrol->cmd,
+ false);
+ if (ret < 0) {
+ dev_warn(sdev->dev,
+ "error: kcontrol value get for widget: %d\n",
+ scontrol->comp_id);
+ }
+ }
+
+ return ret;
+}
+
int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget)
{
@@ -3059,6 +3102,11 @@ static void sof_complete(struct snd_soc_component *scomp)
break;
}
}
+ /*
+ * cache initial values of SOF kcontrols by reading DSP value over
+ * IPC. It may be overwritten by alsa-mixer after booting up
+ */
+ snd_sof_cache_kcontrol_val(sdev);
}
/* manifest - optional to inform component of manifest */

View File

@@ -0,0 +1,50 @@
commit e2eba551d2f4226400a6bb7871fef439ad1318dd
Author: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Date: Wed Jun 12 11:57:04 2019 -0500
ASoC: SOF: ignore unrecoverable CTX_SAVE IPC errors at suspend
As part of the suspend flow, a context save IPC message is
sent to the firmware before powering down the DSP. If errors
are met, the suspend flow is aborted with current code.
Change the behaviour such that if firmware returns -EBUSY or
-EAGAIN, return the error codes to PM core as before. The device
is left in active state in this case.
If other errors are reported, print a warning but do not block the
suspend flow. As per interface specification, no valid error can be
returned in this scenario. If the hardware has hit a fatal error and
is not able to respond successfully, best recovery method is to
proceed with suspend and power off the DSP.
Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8ef1d51025d8..b7843f02ef67 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -343,11 +343,20 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
#endif
/* notify DSP of upcoming power down */
ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
- if (ret < 0) {
+ if (ret == -EBUSY || ret == -EAGAIN) {
+ /*
+ * runtime PM has logic to handle -EBUSY/-EAGAIN so
+ * pass these errors up
+ */
dev_err(sdev->dev,
"error: ctx_save ipc error during suspend %d\n",
ret);
return ret;
+ } else if (ret < 0) {
+ /* FW in unexpected state, continue to power down */
+ dev_warn(sdev->dev,
+ "ctx_save ipc error %d, proceeding with suspend\n",
+ ret);
}
/* power down all DSP cores */

View File

@@ -0,0 +1,51 @@
commit 1b7e1956860d7566325502651c6bf14f115cd91d
Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Date: Wed Jun 12 12:23:35 2019 -0500
ASoC: SOF: topology: add cpu_dai_name for DAIs
Add the cpu_dai_name member to snd_sof_dai and save the
cpu_dai_name while setting the DAI config.
The internal SOF representation will have to change at a later point
as well when we have multiple CPU dais.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 8760a4694d8f..8c3ac149bbb9 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -337,6 +337,7 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_sof_dev *sdev;
const char *name;
+ const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
struct sof_ipc_dai_config *dai_config;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index aaf459af89d0..178256e338b1 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -2360,6 +2360,9 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
if (!dai->dai_config)
return -ENOMEM;
+ /* set cpu_dai_name */
+ dai->cpu_dai_name = link->cpu_dai_name;
+
found = 1;
}
}
@@ -2624,6 +2627,8 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev,
if (!sof_dai->dai_config)
return -ENOMEM;
+ sof_dai->cpu_dai_name = link->cpu_dai_name;
+
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
config->hdr.cmd, config, size,

View File

@@ -88,6 +88,13 @@ bugfix/all/fs-add-module_softdep-declarations-for-hard-coded-cr.patch
bugfix/all/partially-revert-usb-kconfig-using-select-for-usb_co.patch
debian/revert-objtool-fix-config_stack_validation-y-warning.patch
bugfix/all/partially-revert-net-socket-implement-64-bit-timestamps.patch
bugfix/x86/ASoC-SOF-ignore-unrecoverable-CTX_SAVE-IPC-errors-at.patch
bugfix/x86/ASoC-SOF-topology-add-cpu_dai_name-for-DAIs.patch
bugfix/x86/ASoC-SOF-assign-link-DMA-channel-at-run-time.patch
bugfix/x86/ASoC-SOF-Intel-hda-release-link-DMA-for-paused-strea.patch
bugfix/x86/ASoC-SOF-dont-wake-dsp-up-in-kcontrol-IO.patch
bugfix/x86/ASoC-SOF-Intel-hda-add-new-macro-hstream_to_sof_hda_.patch
bugfix/x86/ASoC-SOF-Intel-hda-save-handle-to-sdev-in-sof_intel_.patch
# Miscellaneous features