[alsa-devel] [RFC PATCH 00/12] soundwire/SOF: updated interfaces, functional integration
This series builds on the 'soundwire: add Master device support, GreyBus style' RFC'. It provides enhancements to the stream callbacks, a split initialization. Most of the SOF patches were already submitted in an earlier RFC, and feedback on the parameters was taken into account. The main change here are the API changes with a split between ACPI scan, probe, startup steps.
Known limits: a) Power management (regular suspend/resume and pm_runtime) is not supported for now as we need to run additional checks on hardware. This will be provided as a separate series. b) during validation checks on CML/ICL, initialization and playback/capture worked fine, but we observed a reproducible system freeze while doing load/unload tests, so likely an initialization missing or a leak to be fixed.
Comments and feedback welcome.
Pierre-Louis Bossart (7): ASoC: soc-acpi: add link_mask field ASoC: SOF: support alternate list of machines ASoC: SOF: Intel: add SoundWire configuration interface ASoC: SOF: Intel: add build support for SoundWire ASoC: SOF: IPC: dai-intel: move ALH declarations in header file ASoC: SOF: Intel: hda: add SoundWire stream config/free callbacks ASoC: SOF: Intel: hda: initial SoundWire machine driver autodetect
Rander Wang (5): soundwire: intel: update stream callbacks for hwparams/free stream operations soundwire: intel: add prepare support in sdw dai driver soundwire: intel: add trigger support in sdw dai driver soundwire: intel: add sdw_stream_setup helper for .startup callback soundwire: intel: free all resources on hw_free()
drivers/soundwire/intel.c | 181 ++++++++++++++++++- drivers/soundwire/intel_init.c | 2 +- include/linux/soundwire/sdw_intel.h | 40 ++++- include/sound/soc-acpi.h | 2 + include/sound/sof.h | 3 + include/sound/sof/dai-intel.h | 18 +- sound/soc/sof/intel/Kconfig | 23 +++ sound/soc/sof/intel/hda-loader.c | 9 + sound/soc/sof/intel/hda.c | 261 +++++++++++++++++++++++++++- sound/soc/sof/intel/hda.h | 36 ++++ 10 files changed, 546 insertions(+), 29 deletions(-)
When interfaces can be pin-muxed, static information from ACPI might not be enough. Add information on which links needs to be enabled by hardware/firmware for a specific machine driver to be selected.
When walking through the list of possible machines, links will be checked, which implies that configurations where multiple links are required need to be checked first.
Additional criteria will be needed later, such as which SoundWire Slave devices are actually enabled, but for now this helps detect between basic configurations.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/soc-acpi.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 35b38e41e5b2..c0fb71c7b3ad 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -75,6 +75,7 @@ struct snd_soc_acpi_mach_params { * all firmware/topology related fields. * * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @link_mask: describes required board layout, e.g. for SoundWire. * @drv_name: machine driver name * @fw_filename: firmware file name. Used when SOF is not enabled. * @board: board name @@ -90,6 +91,7 @@ struct snd_soc_acpi_mach_params { /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { const u8 id[ACPI_ID_LEN]; + const u32 link_mask; const char *drv_name; const char *fw_filename; const char *board;
The patch
ASoC: soc-acpi: add link_mask field
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From af78cec45f2d01be9d14c177e403c8021ebfd40e Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Date: Mon, 16 Sep 2019 16:42:40 -0500 Subject: [PATCH] ASoC: soc-acpi: add link_mask field
When interfaces can be pin-muxed, static information from ACPI might not be enough. Add information on which links needs to be enabled by hardware/firmware for a specific machine driver to be selected.
When walking through the list of possible machines, links will be checked, which implies that configurations where multiple links are required need to be checked first.
Additional criteria will be needed later, such as which SoundWire Slave devices are actually enabled, but for now this helps detect between basic configurations.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Link: https://lore.kernel.org/r/20190916214251.13130-2-pierre-louis.bossart@linux.... Signed-off-by: Mark Brown broonie@kernel.org --- include/sound/soc-acpi.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 35b38e41e5b2..c0fb71c7b3ad 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -75,6 +75,7 @@ struct snd_soc_acpi_mach_params { * all firmware/topology related fields. * * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @link_mask: describes required board layout, e.g. for SoundWire. * @drv_name: machine driver name * @fw_filename: firmware file name. Used when SOF is not enabled. * @board: board name @@ -90,6 +91,7 @@ struct snd_soc_acpi_mach_params { /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { const u8 id[ACPI_ID_LEN]; + const u32 link_mask; const char *drv_name; const char *fw_filename; const char *board;
For cases where an interface can be pin-muxed, we need to assess at probe time which configuration should be used. In cases such as SoundWire, we need to maintain an alternate list of machines and walk through them when the primary detection based on ACPI _HID fails.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/sof.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/include/sound/sof.h b/include/sound/sof.h index 4640566b54fe..479101736ee0 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -61,6 +61,9 @@ struct sof_dev_desc { /* list of machines using this configuration */ struct snd_soc_acpi_mach *machines;
+ /* alternate list of machines using this configuration */ + struct snd_soc_acpi_mach *alt_machines; + /* Platform resource indexes in BAR / ACPI resources. */ /* Must set to -1 if not used - add new items to end */ int resindex_lpe_base;
The patch
ASoC: SOF: support alternate list of machines
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 1466327e8eb3e59ae8e65e5c728055102fe0a60e Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Date: Mon, 16 Sep 2019 16:42:41 -0500 Subject: [PATCH] ASoC: SOF: support alternate list of machines
For cases where an interface can be pin-muxed, we need to assess at probe time which configuration should be used. In cases such as SoundWire, we need to maintain an alternate list of machines and walk through them when the primary detection based on ACPI _HID fails.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Link: https://lore.kernel.org/r/20190916214251.13130-3-pierre-louis.bossart@linux.... Signed-off-by: Mark Brown broonie@kernel.org --- include/sound/sof.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/include/sound/sof.h b/include/sound/sof.h index 4640566b54fe..479101736ee0 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -61,6 +61,9 @@ struct sof_dev_desc { /* list of machines using this configuration */ struct snd_soc_acpi_mach *machines;
+ /* alternate list of machines using this configuration */ + struct snd_soc_acpi_mach *alt_machines; + /* Platform resource indexes in BAR / ACPI resources. */ /* Must set to -1 if not used - add new items to end */ int resindex_lpe_base;
From: Rander Wang rander.wang@linux.intel.com
Rename 'config_stream' as 'params_stream' to be closer to ALSA/ASoC concepts.
Add a 'free_stream' callback in case any resources allocated in the 'params_stream' stage need to be released.
Define structures for callbacks, mainly to allow for extensions as needed.
Add the link_id and alh_stream_id parameters which are needed to align with firmware IPC needs.
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 39 +++++++++++++++++++++++----- drivers/soundwire/intel_init.c | 2 +- include/linux/soundwire/sdw_intel.h | 40 +++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c3dba6cf7730..bd87caa127b6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -521,20 +521,46 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); }
-static int intel_config_stream(struct sdw_intel *sdw, +static int intel_params_stream(struct sdw_intel *sdw, struct snd_pcm_substream *substream, struct snd_soc_dai *dai, - struct snd_pcm_hw_params *hw_params, int link_id) + struct snd_pcm_hw_params *hw_params, + int link_id, int alh_stream_id) { struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_params_data params_data;
- if (res->ops && res->ops->config_stream && res->arg) - return res->ops->config_stream(res->arg, - substream, dai, hw_params, link_id); + params_data.substream = substream; + params_data.dai = dai; + params_data.hw_params = hw_params; + params_data.link_id = link_id; + params_data.alh_stream_id = alh_stream_id;
+ if (res->ops && res->ops->params_stream && res->dev) + return res->ops->params_stream(res->dev, + ¶ms_data); return -EIO; }
+static int intel_free_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int link_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_free_data free_data; + + free_data.substream = substream; + free_data.dai = dai; + free_data.link_id = link_id; + + if (res->ops && res->ops->free_stream && res->dev) + return res->ops->free_stream(res->dev, + &free_data); + + return 0; +} + /* * bank switch routines */ @@ -646,7 +672,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
/* Inform DSP about PDI stream number */ - ret = intel_config_stream(sdw, substream, dai, params, + ret = intel_params_stream(sdw, substream, dai, params, + sdw->instance, pdi->intel_alh_id); if (ret) goto error; diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 47124fc13a4a..295637429e99 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -194,7 +194,7 @@ static struct sdw_intel_ctx link->alh = res->mmio_base + SDW_ALH_BASE; link->irq = res->irq; link->ops = res->ops; - link->arg = res->arg; + link->dev = res->dev;
/* let the SoundWire master driver to its probe */ md->driver->probe(md, link); diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 0ce3e4023074..87e63f34d93c 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -4,15 +4,39 @@ #ifndef __SDW_INTEL_H #define __SDW_INTEL_H
+/** + * struct sdw_intel_stream_params_data: configuration passed during + * the @params_stream callback, e.g. for interaction with DSP + * firmware. + */ +struct sdw_intel_stream_params_data { + struct snd_pcm_substream *substream; + struct snd_soc_dai *dai; + struct snd_pcm_hw_params *hw_params; + int link_id; + int alh_stream_id; +}; + +/** + * struct sdw_intel_stream_free_data: configuration passed during + * the @free_stream callback, e.g. for interaction with DSP + * firmware. + */ +struct sdw_intel_stream_free_data { + struct snd_pcm_substream *substream; + struct snd_soc_dai *dai; + int link_id; +}; + /** * struct sdw_intel_ops: Intel audio driver callback ops * - * @config_stream: configure the stream with the hw_params - * the first argument containing the context is mandatory */ struct sdw_intel_ops { - int (*config_stream)(void *arg, void *substream, - void *dai, void *hw_params, int stream_num); + int (*params_stream)(struct device *dev, + struct sdw_intel_stream_params_data *params_data); + int (*free_stream)(struct device *dev, + struct sdw_intel_stream_free_data *free_data); };
/** @@ -41,7 +65,7 @@ struct sdw_intel_acpi_info { * @handle: ACPI parent handle * @parent: parent device * @ops: callback ops - * @arg: callback arg + * @dev: device implementing hwparams and free callbacks * @link_mask: bit-wise mask listing links selected by the DSP driver */ struct sdw_intel_res { @@ -51,7 +75,7 @@ struct sdw_intel_res { acpi_handle handle; struct device *parent; const struct sdw_intel_ops *ops; - void *arg; + struct device *dev; u32 link_mask; };
@@ -65,7 +89,7 @@ struct sdw_intel_res { * @alh: ALH (Audio Link Hub) pointer * @irq: Interrupt line * @ops: Shim callback ops - * @arg: Shim callback ops argument + * @dev: Device implementing the callbacks for params/free */ struct sdw_intel_link_res { struct sdw_master_device *md; @@ -75,7 +99,7 @@ struct sdw_intel_link_res { void __iomem *alh; int irq; const struct sdw_intel_ops *ops; - void *arg; + struct device *dev; };
/**
Now that the SoundWire core supports the multi-step initialization, call the relevant APIs.
The actual hardware enablement can be done in two places, ideally we'd want to startup the IP as soon as possible but we need to work-out additional hardware dependencies in corner cases. For the time being the default is to startup the IP after the firmware is downloaded.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/intel/hda-loader.c | 9 ++ sound/soc/sof/intel/hda.c | 155 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 36 +++++++ 3 files changed, 200 insertions(+)
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 65c2af3fcaab..5f9f3eb26daa 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -378,6 +378,15 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) /* post fw run operations */ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) { +#ifndef SOUNDWIRE_POWER_FIRST + int ret; + + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: could not startup SoundWire links\n"); + return ret; + } +#endif /* re-enable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, true); } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 06e84679087b..d129d5c68f5e 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -18,7 +18,9 @@ #include <sound/hdaudio_ext.h> #include <sound/hda_register.h>
+#include <linux/acpi.h> #include <linux/module.h> +#include <linux/soundwire/sdw_intel.h> #include <sound/intel-nhlt.h> #include <sound/sof.h> #include <sound/sof/xtensa.h> @@ -37,6 +39,114 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
+#if IS_ENABLED(CONFIG_SOUNDWIRE_INTEL) + +static void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ + if (enable) { + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC2, + HDA_DSP_ADSPIC2_SNDW, + HDA_DSP_ADSPIC2_SNDW); + } else { + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC2, + HDA_DSP_ADSPIC2_SNDW, + 0); + } +} + +static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(sdev->dev); + + /* save ACPI info for the probe step */ + hdev = sdev->pdata->hw_pdata; + + ret = sdw_intel_acpi_scan(handle, &hdev->info); + if (ret < 0) { + dev_err(sdev->dev, "%s failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_res res; + acpi_handle handle; + void *sdw; + + handle = ACPI_HANDLE(sdev->dev); + + hdev = sdev->pdata->hw_pdata; + + memset(&res, 0, sizeof(res)); + + res.mmio_base = sdev->bar[HDA_DSP_BAR]; + res.irq = sdev->ipc_irq; + res.handle = hdev->info.handle; + res.parent = sdev->dev; + + /* + * ops and arg fields are not populated for now, + * they will be needed when the DAI callbacks are + * provided + */ + + /* we could filter links here if needed, e.g for quirks */ + res.count = hdev->info.count; + res.link_mask = hdev->info.link_mask; + + sdw = sdw_intel_probe(&res); + if (!sdw) { + dev_err(sdev->dev, "error: SoundWire probe failed\n"); + return -EINVAL; + } + + /* save context */ + hdev->sdw = sdw; + + return 0; +} + +int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + int ret; + + hdev = sdev->pdata->hw_pdata; + + ret = sdw_intel_startup(hdev->sdw); + if (ret < 0) + return ret; + hda_sdw_int_enable(sdev, true); + + return ret; +} + +static int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + hda_sdw_int_enable(sdev, false); + + if (hdev->sdw) + sdw_intel_exit(hdev->sdw); + hdev->sdw = NULL; + + return 0; +} +#endif + /* * Debug */ @@ -356,6 +466,8 @@ static int hda_init_caps(struct snd_sof_dev *sdev) int codec_num = 0; int i; #endif + struct sof_intel_hda_dev *hdev; + u32 link_mask; int ret = 0;
device_disable_async_suspend(bus->dev); @@ -384,6 +496,27 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; }
+ /* scan SoundWire capabilities exposed by DSDT */ + ret = hda_sdw_acpi_scan(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire ACPI scan error\n"); + return ret; + } + + link_mask = hdev->info.link_mask; + if (!link_mask) { + /* + * probe/allocated SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + } + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -657,8 +790,28 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
+#ifdef SOUNDWIRE_POWER_FIRST + /* need to power-up core before setting-up capabilities */ + ret = hda_dsp_core_power_up(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: could not power-up DSP subsystem\n"); + goto sdw_exit; + } + + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: could not startup SoundWire links\n"); + goto core_power_down; + } +#endif return 0;
+#ifdef SOUNDWIRE_POWER_FIRST +core_power_down: + hda_dsp_core_power_down(sdev, HDA_DSP_CORE_MASK(0)); +sdw_exit: + hda_sdw_exit(sdev); +#endif free_ipc_irq: free_irq(sdev->ipc_irq, sdev); free_hda_irq: @@ -688,6 +841,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) snd_hdac_ext_bus_device_remove(bus); #endif
+ hda_sdw_exit(sdev); + if (!IS_ERR_OR_NULL(hda->dmic_dev)) platform_device_unregister(hda->dmic_dev);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 7a2ff8b3d8a4..ff93e8eb145c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -11,6 +11,8 @@ #ifndef __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H
+#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> #include <sound/hda_codec.h> #include <sound/hdaudio_ext.h> #include "shim.h" @@ -218,6 +220,7 @@
#define HDA_DSP_ADSPIC_IPC 1 #define HDA_DSP_ADSPIS_IPC 1 +#define HDA_DSP_ADSPIC2_SNDW BIT(5)
/* Intel HD Audio General DSP Registers */ #define HDA_DSP_GEN_BASE 0x0 @@ -405,6 +408,12 @@ struct sof_intel_hda_dev {
/* DMIC device */ struct platform_device *dmic_dev; + + /* ACPI information stored between scan and probe steps */ + struct sdw_intel_acpi_info info; + + /* sdw context allocated by SoundWire driver */ + struct sdw_intel_ctx *sdw; };
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -595,6 +604,33 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
+/* + * SoundWire support + */ +#if IS_ENABLED(CONFIG_SOUNDWIRE_INTEL) +int hda_sdw_startup(struct snd_sof_dev *sdev); +#else +static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[];
Select SoundWire capabilities on newer Intel platforms, starting with CannonLake/CoffeeLake/CometLake.
As done for HDaudio, the SoundWire link is an opt-in capability. We explicitly test for ACPI to avoid warnings on unmet dependencies on the SoundWire side.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/intel/Kconfig | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 479ba249e219..1adff7be5b40 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -150,6 +150,7 @@ config SND_SOC_SOF_CANNONLAKE_SUPPORT config SND_SOC_SOF_CANNONLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -165,6 +166,7 @@ config SND_SOC_SOF_COFFEELAKE_SUPPORT config SND_SOC_SOF_COFFEELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -180,6 +182,7 @@ config SND_SOC_SOF_ICELAKE_SUPPORT config SND_SOC_SOF_ICELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -187,6 +190,7 @@ config SND_SOC_SOF_ICELAKE config SND_SOC_SOF_COMETLAKE_LP tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -202,6 +206,7 @@ config SND_SOC_SOF_COMETLAKE_LP_SUPPORT config SND_SOC_SOF_COMETLAKE_H tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -225,6 +230,7 @@ config SND_SOC_SOF_TIGERLAKE_SUPPORT config SND_SOC_SOF_TIGERLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -240,6 +246,7 @@ config SND_SOC_SOF_ELKHARTLAKE_SUPPORT config SND_SOC_SOF_ELKHARTLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -291,6 +298,22 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level
+config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + bool "SOF support for SoundWire" + depends on SOUNDWIRE && ACPI + help + This adds support for SoundWire with Sound Open Firmware + for Intel(R) platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + +config SND_SOC_SOF_INTEL_SOUNDWIRE + tristate + select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + endif ## SND_SOC_SOF_INTEL_PCI
endif ## SND_SOC_SOF_INTEL_TOPLEVEL
From: Rander Wang rander.wang@linux.intel.com
It gets sdw runtime information from dai to prepare stream.
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index bd87caa127b6..072757cd5cbf 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -710,6 +710,21 @@ static int intel_hw_params(struct snd_pcm_substream *substream, return ret; }
+static int intel_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", + __func__); + return -EIO; + } + + return sdw_prepare_stream(dma->stream); +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -756,6 +771,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -763,6 +779,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream,
From: Rander Wang rander.wang@linux.intel.com
Sdw stream is enabled and disabled in trigger function.
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 072757cd5cbf..f29c63039606 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -725,6 +725,43 @@ static int intel_prepare(struct snd_pcm_substream *substream, return sdw_prepare_stream(dma->stream); }
+static int intel_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + int ret; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_enable_stream(dma->stream); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_stream(dma->stream); + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) + dev_err(dai->dev, + "%s trigger %d failed: %d", + __func__, cmd, ret); + return ret; +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -772,6 +809,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -780,6 +818,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream,
From: Rander Wang rander.wang@linux.intel.com
The sdw stream is allocated and stored in dai to share the sdw runtime information.
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f29c63039606..ee81b1c8d860 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -628,6 +628,69 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */
+static int sdw_stream_setup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream = NULL; + char *name; + int i, ret; + + name = kzalloc(32, GFP_KERNEL); + if (!name) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf(name, 32, "%s-Playback", dai->name); + else + snprintf(name, 32, "%s-Capture", dai->name); + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); + ret = -ENOMEM; + goto error; + } + + /* Set stream pointer on CPU DAI */ + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", + dai->name); + goto release_stream; + } + + /* Set stream pointer on all CODEC DAIs */ + for (i = 0; i < rtd->num_codecs; i++) { + ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream, + substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on codec dai %s", + rtd->codec_dais[i]->name); + goto release_stream; + } + } + + return 0; + +release_stream: + sdw_release_stream(sdw_stream); +error: + kfree(name); + return ret; +} + +static int intel_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * TODO: add pm_runtime support here, the startup callback + * will make sure the IP is 'active' + */ + + return sdw_stream_setup(substream, dai); +} + static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -807,6 +870,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, }
static const struct snd_soc_dai_ops intel_pcm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger, @@ -816,6 +880,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { };
static const struct snd_soc_dai_ops intel_pdm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger,
From: Rander Wang rander.wang@linux.intel.com
Make sure all calls to the SoundWire stream API are done and involve callback
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ee81b1c8d860..452c59b0de16 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -829,6 +829,7 @@ static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; int ret;
@@ -836,12 +837,28 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) if (!dma) return -EIO;
+ ret = sdw_deprepare_stream(dma->stream); + if (ret) { + dev_err(dai->dev, "sdw_deprepare_stream: failed %d", ret); + return ret; + } + ret = sdw_stream_remove_master(&cdns->bus, dma->stream); - if (ret < 0) + if (ret < 0) { dev_err(dai->dev, "remove master from stream %s failed: %d\n", dma->stream->name, ret); + return ret; + }
- return ret; + ret = intel_free_stream(sdw, substream, dai, sdw->instance); + if (ret < 0) { + dev_err(dai->dev, "intel_free_stream: failed %d", ret); + return ret; + } + + sdw_release_stream(dma->stream); + + return 0; }
static void intel_shutdown(struct snd_pcm_substream *substream,
ALH was inserted in the wrong place during integration, add after DMIC to mirror the file used by SOF firmware.
No functional change, just text move in the same file to better track changes, if any.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/sof/dai-intel.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h index 5f1ef5565be6..04e48227f542 100644 --- a/include/sound/sof/dai-intel.h +++ b/include/sound/sof/dai-intel.h @@ -87,6 +87,15 @@ struct sof_ipc_dai_hda_params { uint32_t link_dma_ch; } __packed;
+/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ +struct sof_ipc_dai_alh_params { + struct sof_ipc_hdr hdr; + uint32_t stream_id; + + /* reserved for future use */ + uint32_t reserved[15]; +} __packed; + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
/* This struct is defined per 2ch PDM controller available in the platform. @@ -179,13 +188,4 @@ struct sof_ipc_dai_dmic_params { struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __packed;
-/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ -struct sof_ipc_dai_alh_params { - struct sof_ipc_hdr hdr; - uint32_t stream_id; - - /* reserved for future use */ - uint32_t reserved[15]; -} __packed; - #endif
These callbacks are invoked when a matching hw_params/hw_free() DAI operation takes place, and will result in IPC operations with the SOF firmware.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/intel/hda.c | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+)
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index d129d5c68f5e..02291e1de1fc 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,6 +41,74 @@
#if IS_ENABLED(CONFIG_SOUNDWIRE_INTEL)
+static int sdw_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = params_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = params_data->link_id; + int alh_stream_id = params_data->alh_stream_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | (d->id); + config.alh.stream_id = alh_stream_id; + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", + link_id, d->id, alh_stream_id); + } + + return ret; +} + +static int sdw_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = free_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = free_data->link_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | d->id; + config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to free stream for link %d dai->id %d\n", + link_id, d->id); + } + + return ret; +} + +static const struct sdw_intel_ops sdw_callback = { + .params_stream = sdw_params_stream, + .free_stream = sdw_free_stream, +}; + static void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { if (enable) { @@ -93,6 +161,8 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.irq = sdev->ipc_irq; res.handle = hdev->info.handle; res.parent = sdev->dev; + res.ops = &sdw_callback; + res.dev = sdev->dev;
/* * ops and arg fields are not populated for now,
For now we have a limited number of machine driver configurations, and we can detect them based on the link configuration returned after checking hardware and firmware (BIOS) configurations.
It's likely that in the future we will need to check for _ADR match as well, which can easily be done by extending the acpi_info structure.
There is a chance that in extreme cases where the BIOS contains too much information we would need to detect which Slave devices actually report as 'attached'. This would be more accurate than static table-based solutions, but it also introduces timing dependencies since we don't know when those devices might become attached, so will only be only be looked at if we see limitations with static methods and the usual quirks based e.g. on DMI information.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/intel/hda.c | 58 +++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-)
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 02291e1de1fc..ebd78ee8dfb7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -523,12 +523,12 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, static int hda_init_caps(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *pdata = sdev->pdata; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; - struct snd_soc_acpi_mach_params *mach_params; struct snd_soc_acpi_mach *hda_mach; - struct snd_sof_pdata *pdata = sdev->pdata; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach_params *mach_params; const char *tplg_filename; const char *idisp_str; const char *dmic_str; @@ -536,7 +536,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev) int codec_num = 0; int i; #endif - struct sof_intel_hda_dev *hdev; + struct sof_intel_hda_dev *hdev = pdata->hw_pdata; u32 link_mask; int ret = 0;
@@ -574,19 +574,49 @@ static int hda_init_caps(struct snd_sof_dev *sdev) }
link_mask = hdev->info.link_mask; - if (!link_mask) { - /* - * probe/allocated SoundWire resources. - * The hardware configuration takes place in hda_sdw_startup - * after power rails are enabled. - */ - ret = hda_sdw_probe(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: SoundWire probe error\n"); - return ret; + if (!link_mask) + goto skip_soundwire; + + /* + * probe/allocate SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + * It's entirely possible to have a mix of I2S/DMIC/SoundWire + * devices, so we allocate the resources in all cases. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + + /* + * Select SoundWire machine driver if needed using the + * alternate tables. This case deals with SoundWire-only + * machines, for mixed cases with I2C/I2S the detection relies + * on the HID list. + */ + if (!pdata->machine) { + mach = pdata->desc->alt_machines; + while (mach && mach->link_mask && mach->link_mask != link_mask) + mach++; + if (mach && mach->link_mask) { + dev_dbg(bus->dev, + "SoundWire machine driver %s topology %s\n", + mach->drv_name, + mach->sof_tplg_filename); + pdata->machine = mach; + mach->mach_params.platform = dev_name(sdev->dev); + pdata->fw_filename = mach->sof_fw_filename; + pdata->tplg_filename = mach->sof_tplg_filename; + } else { + dev_info(sdev->dev, + "No SoundWire machine driver found\n"); } }
+skip_soundwire: + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus);
participants (2)
-
Mark Brown
-
Pierre-Louis Bossart