diff --git a/debian/changelog b/debian/changelog index 58e055860fc3..4bf8c16d1826 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Mon, 30 Sep 2019 16:09:14 +0200 linux (5.2.17-1) unstable; urgency=medium diff --git a/debian/config/amd64/config b/debian/config/amd64/config index bbbbde2e8784..c3bd0bcc2fb0 100644 --- a/debian/config/amd64/config +++ b/debian/config/amd64/config @@ -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 diff --git a/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-add-new-macro-hstream_to_sof_hda_.patch b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-add-new-macro-hstream_to_sof_hda_.patch new file mode 100644 index 000000000000..6b8077f1bf94 --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-add-new-macro-hstream_to_sof_hda_.patch @@ -0,0 +1,25 @@ +commit f5dbba9fee801f4678a50d92c785f7f24d4ee2c6 +Author: Ranjani Sridharan +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 + Signed-off-by: Mark Brown + +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) + diff --git a/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-release-link-DMA-for-paused-strea.patch b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-release-link-DMA-for-paused-strea.patch new file mode 100644 index 000000000000..866da92cd744 --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-release-link-DMA-for-paused-strea.patch @@ -0,0 +1,168 @@ +commit 7077a07a72d38a78040873bbc13a77d1e45f8aa0 +Author: Ranjani Sridharan +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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 */ diff --git a/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-save-handle-to-sdev-in-sof_intel_.patch b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-save-handle-to-sdev-in-sof_intel_.patch new file mode 100644 index 000000000000..d27f7364491b --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-Intel-hda-save-handle-to-sdev-in-sof_intel_.patch @@ -0,0 +1,47 @@ +commit 7623ae793c28cc0928c5d1292542dbb92fc2e9e2 +Author: Ranjani Sridharan +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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 */ diff --git a/debian/patches/bugfix/x86/ASoC-SOF-assign-link-DMA-channel-at-run-time.patch b/debian/patches/bugfix/x86/ASoC-SOF-assign-link-DMA-channel-at-run-time.patch new file mode 100644 index 000000000000..8ee7c7930417 --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-assign-link-DMA-channel-at-run-time.patch @@ -0,0 +1,516 @@ +commit bdf4ad3fd01f5dc53c5d6d3b17afc98cd76d8988 +Author: Ranjani Sridharan +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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; + } + diff --git a/debian/patches/bugfix/x86/ASoC-SOF-dont-wake-dsp-up-in-kcontrol-IO.patch b/debian/patches/bugfix/x86/ASoC-SOF-dont-wake-dsp-up-in-kcontrol-IO.patch new file mode 100644 index 000000000000..e782c3798caf --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-dont-wake-dsp-up-in-kcontrol-IO.patch @@ -0,0 +1,516 @@ +commit 0c888baba8e041c92c5c1882f25b8df5c29bff9f +Author: Bard Liao +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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 */ diff --git a/debian/patches/bugfix/x86/ASoC-SOF-ignore-unrecoverable-CTX_SAVE-IPC-errors-at.patch b/debian/patches/bugfix/x86/ASoC-SOF-ignore-unrecoverable-CTX_SAVE-IPC-errors-at.patch new file mode 100644 index 000000000000..08b5e1fef990 --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-ignore-unrecoverable-CTX_SAVE-IPC-errors-at.patch @@ -0,0 +1,50 @@ +commit e2eba551d2f4226400a6bb7871fef439ad1318dd +Author: Kai Vehmanen +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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 */ diff --git a/debian/patches/bugfix/x86/ASoC-SOF-topology-add-cpu_dai_name-for-DAIs.patch b/debian/patches/bugfix/x86/ASoC-SOF-topology-add-cpu_dai_name-for-DAIs.patch new file mode 100644 index 000000000000..9347604873f5 --- /dev/null +++ b/debian/patches/bugfix/x86/ASoC-SOF-topology-add-cpu_dai_name-for-DAIs.patch @@ -0,0 +1,51 @@ +commit 1b7e1956860d7566325502651c6bf14f115cd91d +Author: Ranjani Sridharan +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 + Signed-off-by: Pierre-Louis Bossart + Signed-off-by: Mark Brown + +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, diff --git a/debian/patches/series b/debian/patches/series index c45159eee834..adbee9cd5eea 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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