All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
To: Cezary Rojewski <cezary.rojewski@intel.com>, alsa-devel@alsa-project.org
Cc: upstream@semihalf.com, harshapriya.n@intel.com, rad@semihalf.com,
	tiwai@suse.com, pierre-louis.bossart@linux.intel.com,
	hdegoede@redhat.com, broonie@kernel.org,
	amadeuszx.slawinski@linux.intel.com, cujomalainey@chromium.org,
	lma@semihalf.com
Subject: Re: [PATCH v3 14/17] ASoC: Intel: avs: General code loading flow
Date: Fri, 04 Mar 2022 08:54:55 -0800	[thread overview]
Message-ID: <0c3e200bd14536534115e2a44fa744a102faa107.camel@linux.intel.com> (raw)
In-Reply-To: <20220304145755.2844173-15-cezary.rojewski@intel.com>

On Fri, 2022-03-04 at 15:57 +0100, Cezary Rojewski wrote:
> Code loading is a complex procedure and requires combined effort of
> DMA
> and IPCs. With IPCs already in place, lay out ground for specific DMA
> transfer operations.
> 
> Signed-off-by: Amadeusz Sławiński <
> amadeuszx.slawinski@linux.intel.com>
> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
> ---
>  sound/soc/intel/avs/Makefile    |   2 +-
>  sound/soc/intel/avs/avs.h       |  18 +++
>  sound/soc/intel/avs/core.c      |  62 +++++++++
>  sound/soc/intel/avs/dsp.c       |  26 ++++
>  sound/soc/intel/avs/loader.c    | 237
> ++++++++++++++++++++++++++++++++
>  sound/soc/intel/avs/registers.h |   6 +
>  6 files changed, 350 insertions(+), 1 deletion(-)
>  create mode 100644 sound/soc/intel/avs/core.c
>  create mode 100644 sound/soc/intel/avs/loader.c
> 
> diff --git a/sound/soc/intel/avs/Makefile
> b/sound/soc/intel/avs/Makefile
> index d9f92c5f5407..d9c793160612 100644
> --- a/sound/soc/intel/avs/Makefile
> +++ b/sound/soc/intel/avs/Makefile
> @@ -1,5 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  
> -snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o
> +snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
>  
>  obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
> diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
> index 0034c075fa64..2527d6170417 100644
> --- a/sound/soc/intel/avs/avs.h
> +++ b/sound/soc/intel/avs/avs.h
> @@ -10,8 +10,11 @@
>  #define __SOUND_SOC_INTEL_AVS_H
>  
>  #include <linux/device.h>
> +#include <linux/firmware.h>
>  #include <sound/hda_codec.h>
> +#include <sound/hda_register.h>
>  #include "messages.h"
> +#include "registers.h"
>  
>  struct avs_dev;
>  
> @@ -32,6 +35,10 @@ struct avs_dsp_ops {
>  	irqreturn_t (* const irq_handler)(int, void *);
>  	irqreturn_t (* const irq_thread)(int, void *);
>  	void (* const int_control)(struct avs_dev *, bool);
> +	int (* const load_basefw)(struct avs_dev *, struct firmware *);
> +	int (* const load_lib)(struct avs_dev *, struct firmware *,
> u32);
> +	int (* const transfer_mods)(struct avs_dev *, bool,
> +				    struct avs_module_entry *, u32);
>  };
>  
>  #define avs_dsp_op(adev, op, ...) \
> @@ -45,6 +52,7 @@ struct avs_spec {
>  	const char *name;
>  
>  	const struct avs_dsp_ops *const dsp_ops;
> +	struct avs_fw_version min_fw_version; /* anything below is
> rejected */
>  
>  	const u32 core_init_mask;	/* used during DSP boot */
>  	const u64 attributes;		/* bitmask of AVS_PLATATTR_*
> */
> @@ -90,6 +98,7 @@ struct avs_dev {
>  	struct ida ppl_ida;
>  	struct list_head fw_list;
>  	int *core_refs;
> +	char **lib_names;
>  
>  	struct completion fw_ready;
>  };
> @@ -215,4 +224,13 @@ int avs_dsp_create_pipeline(struct avs_dev
> *adev, u16 req_size, u8 priority,
>  			    bool lp, u16 attributes, u8 *instance_id);
>  int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id);
>  
> +/* Firmware loading */
> +
> +void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable);
> +void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable);
> +void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
> +
> +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
> +int avs_dsp_first_boot_firmware(struct avs_dev *adev);
> +
>  #endif /* __SOUND_SOC_INTEL_AVS_H */
> diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
> new file mode 100644
> index 000000000000..117b31ef9cd0
> --- /dev/null
> +++ b/sound/soc/intel/avs/core.c
> @@ -0,0 +1,62 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//
> +// Copyright(c) 2021 Intel Corporation. All rights reserved.
> +//
> +// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
> +//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
> +//
> +// Special thanks to:
> +//    Krzysztof Hejmowski <krzysztof.hejmowski@intel.com>
> +//    Michal Sienkiewicz <michal.sienkiewicz@intel.com>
> +//    Filip Proborszcz
> +//
> +// for sharing Intel AudioDSP expertise and helping shape the very
> +// foundation of this driver
> +//
> +
> +#include <linux/pci.h>
> +#include <sound/hdaudio.h>
> +#include "avs.h"
> +
> +static void
> +avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask,
> u32 value)
> +{
> +	struct pci_dev *pci = to_pci_dev(bus->dev);
> +	u32 data;
> +
> +	pci_read_config_dword(pci, reg, &data);
> +	data &= ~mask;
> +	data |= (value & mask);
> +	pci_write_config_dword(pci, reg, data);
> +}
> +
> +void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable)
> +{
> +	u32 value;
> +
> +	value = enable ? 0 : AZX_PGCTL_LSRMD_MASK;
> +	avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL,
> +				    AZX_PGCTL_LSRMD_MASK, value);
> +}
> +
> +static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool
> enable)
> +{
> +	u32 value;
> +
> +	value = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
> +	avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL,
> +				    AZX_CGCTL_MISCBDCGE_MASK, value);
> +}
> +
> +void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
> +{
> +	avs_hdac_clock_gating_enable(&adev->base.core, enable);
> +}
> +
> +void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
> +{
> +	u32 value;
> +
> +	value = enable ? AZX_VS_EM2_L1SEN : 0;
> +	snd_hdac_chip_updatel(&adev->base.core, VS_EM2,
> AZX_VS_EM2_L1SEN, value);
> +}
> diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
> index 5994d64d2468..a434e9918c51 100644
> --- a/sound/soc/intel/avs/dsp.c
> +++ b/sound/soc/intel/avs/dsp.c
> @@ -198,6 +198,7 @@ int avs_dsp_init_module(struct avs_dev *adev, u16
> module_id, u8 ppl_instance_id,
>  			u16 *instance_id)
>  {
>  	struct avs_module_entry mentry;
> +	bool was_loaded = false;
>  	int ret, id;
>  
>  	id = avs_module_id_alloc(adev, module_id);
> @@ -212,6 +213,16 @@ int avs_dsp_init_module(struct avs_dev *adev,
> u16 module_id, u8 ppl_instance_id,
>  	if (ret)
>  		goto err_mod_entry;
>  
> +	/* Load code into memory if this is the first instance. */
> +	if (!id && !avs_module_entry_is_loaded(&mentry)) {
> +		ret = avs_dsp_op(adev, transfer_mods, true, &mentry,
> 1);
> +		if (ret) {
> +			dev_err(adev->dev, "load modules failed: %d\n",
> ret);
> +			goto err_mod_entry;
> +		}
> +		was_loaded = true;
> +	}
> +
>  	ret = avs_ipc_init_instance(adev, module_id, id,
> ppl_instance_id,
>  				    core_id, domain, param,
> param_size);
>  	if (ret) {
> @@ -223,6 +234,8 @@ int avs_dsp_init_module(struct avs_dev *adev, u16
> module_id, u8 ppl_instance_id,
>  	return 0;
>  
>  err_ipc:
> +	if (was_loaded)
> +		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
>  	avs_dsp_put_core(adev, core_id);
>  err_mod_entry:
>  	avs_module_id_free(adev, module_id, id);
> @@ -232,12 +245,25 @@ int avs_dsp_init_module(struct avs_dev *adev,
> u16 module_id, u8 ppl_instance_id,
>  void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16
> instance_id,
>  			   u8 ppl_instance_id, u8 core_id)
>  {
> +	struct avs_module_entry mentry;
> +	int ret;
> +
>  	/* Modules not owned by any pipeline need to be freed
> explicitly. */
>  	if (ppl_instance_id == INVALID_PIPELINE_ID)
>  		avs_ipc_delete_instance(adev, module_id, instance_id);
>  
>  	avs_module_id_free(adev, module_id, instance_id);
>  
> +	ret = avs_get_module_id_entry(adev, module_id, &mentry);
> +	/* Unload occupied memory if this was the last instance. */
> +	if (!ret && mentry.type.load_type ==
> AVS_MODULE_LOAD_TYPE_LOADABLE) {
> +		if (avs_is_module_ida_empty(adev, module_id)) {
> +			ret = avs_dsp_op(adev, transfer_mods, false,
> &mentry, 1);
> +			if (ret)
> +				dev_err(adev->dev, "unload modules
> failed: %d\n", ret);
> +		}
> +	}
> +
>  	avs_dsp_put_core(adev, core_id);
>  }
>  
> diff --git a/sound/soc/intel/avs/loader.c
> b/sound/soc/intel/avs/loader.c
> new file mode 100644
> index 000000000000..47e1f9a21e43
> --- /dev/null
> +++ b/sound/soc/intel/avs/loader.c
> @@ -0,0 +1,237 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//
> +// Copyright(c) 2021 Intel Corporation. All rights reserved.
> +//
> +// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
> +//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
> +//
> +
> +#include <linux/firmware.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include "avs.h"
> +#include "messages.h"
> +#include "registers.h"
> +
> +#define AVS_FW_INIT_TIMEOUT_MS		3000
> +
> +#define AVS_ROOT_DIR			"intel/avs"
> +#define AVS_BASEFW_FILENAME		"dsp_basefw.bin"
> +#define AVS_EXT_MANIFEST_MAGIC		0x31454124
> +#define SKL_MANIFEST_MAGIC		0x00000006
> +#define SKL_ADSPFW_OFFSET		0x284
> +
> +/* Occasionally, engineering (release candidate) firmware is
> provided for testing. */
> +static bool debug_ignore_fw_version;
> +module_param_named(ignore_fw_version, debug_ignore_fw_version, bool,
> 0444);
> +MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes
> (default), 1=no");
> +
> +#define AVS_LIB_NAME_SIZE	8
> +
> +struct avs_fw_manifest {
> +	u32 id;
> +	u32 len;
> +	char name[AVS_LIB_NAME_SIZE];
> +	u32 preload_page_count;
> +	u32 img_flags;
> +	u32 feature_mask;
> +	struct avs_fw_version version;
> +} __packed;
> +
> +struct avs_fw_ext_manifest {
> +	u32 id;
> +	u32 len;
> +	u16 version_major;
> +	u16 version_minor;
> +	u32 entries;
> +} __packed;
> +
> +static int avs_fw_ext_manifest_strip(struct firmware *fw)
> +{
> +	struct avs_fw_ext_manifest *man;
> +
> +	if (fw->size < sizeof(*man))
> +		return -EINVAL;
> +
> +	man = (struct avs_fw_ext_manifest *)fw->data;
> +	if (man->id == AVS_EXT_MANIFEST_MAGIC) {
> +		fw->data += man->len;
> +		fw->size -= man->len;
> +	}
> +
> +	return 0;
> +}
> +
> +static int avs_fw_manifest_offset(struct firmware *fw)
> +{
> +	/* Header type found in first DWORD of fw binary. */
> +	u32 magic = *(u32 *)fw->data;
> +
> +	switch (magic) {
> +	case SKL_MANIFEST_MAGIC:
> +		return SKL_ADSPFW_OFFSET;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct
> firmware *fw,
> +					const struct avs_fw_version
> *min)
> +{
> +	struct avs_fw_manifest *man;
> +	int offset, ret;
> +
> +	ret = avs_fw_ext_manifest_strip(fw);
> +	if (ret)
> +		return ret;
> +
> +	offset = avs_fw_manifest_offset(fw);
> +	if (offset < 0)
> +		return offset;
> +
> +	if (fw->size < offset + sizeof(*man))
> +		return -EINVAL;
> +	if (!min)
> +		return 0;
> +
> +	man = (struct avs_fw_manifest *)(fw->data + offset);
> +	if (man->version.major != min->major ||
> +	    man->version.minor != min->minor ||
> +	    man->version.hotfix != min->hotfix ||
> +	    man->version.build < min->build) {
Isnt this check a bit too strict? Isnt a check major enough?

> +		dev_warn(adev->dev, "bad FW version %d.%d.%d.%d,
> expected %d.%d.%d.%d or newer\n",
> +			 man->version.major, man->version.minor,
> +			 man->version.hotfix, man->version.build,
> +			 min->major, min->minor, min->hotfix, min-
> >build);
> +
> +		if (!debug_ignore_fw_version)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int avs_dsp_load_basefw(struct avs_dev *adev)
> +{
> +	const struct avs_fw_version *min_req;
> +	const struct avs_spec *const spec = adev->spec;
> +	const struct firmware *fw;
> +	struct firmware stripped_fw;
> +	char *filename;
> +	int ret;
> +
> +	filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR,
> spec->name,
> +			     AVS_BASEFW_FILENAME);
> +	if (!filename)
> +		return -ENOMEM;
> +
> +	ret = avs_request_firmware(adev, &fw, filename);
> +	kfree(filename);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "request firmware failed: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	stripped_fw = *fw;
> +	min_req = &adev->spec->min_fw_version;
> +
> +	ret = avs_fw_manifest_strip_verify(adev, &stripped_fw,
> min_req);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "invalid firmware data: %d\n", ret);
> +		goto release_fw;
> +	}
> +
> +	ret = avs_dsp_op(adev, load_basefw, &stripped_fw);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "basefw load failed: %d\n", ret);
> +		goto release_fw;
> +	}
> +
> +	ret = wait_for_completion_timeout(&adev->fw_ready,
> +					  msecs_to_jiffies(AVS_FW_INIT_
> TIMEOUT_MS));
> +	if (!ret) {
> +		dev_err(adev->dev, "firmware ready timeout\n");
> +		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
> +		ret = -ETIMEDOUT;
> +		goto release_fw;
> +	}
> +
> +	return 0;
> +
> +release_fw:
> +	avs_release_last_firmware(adev);
> +	return ret;
> +}
> +
> +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
> +{
> +	int ret, i;
> +
> +	/* Full boot, clear cached data except for basefw (slot 0). */
Does this mean IMR restore is only available for base FW and not for
module libraries? Do I understand this correctly?
> +	for (i = 1; i < adev->fw_cfg.max_libs_count; i++)
> +		memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
> +
> +	avs_hda_clock_gating_enable(adev, false);
> +	avs_hda_l1sen_enable(adev, false);
> +
> +	ret = avs_dsp_load_basefw(adev);
> +
> +	avs_hda_l1sen_enable(adev, true);
> +	avs_hda_clock_gating_enable(adev, true);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	/* With all code loaded, refresh module information. */
> +	ret = avs_module_info_init(adev, true);
It is not clear if this required only after first boot or after a
suspend/resume as well.
Thanks,Ranjani


  reply	other threads:[~2022-03-04 16:55 UTC|newest]

Thread overview: 65+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-04 14:57 [PATCH v3 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 01/17] ALSA: hda: Add helper macros for DSP capable devices Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 02/17] ASoC: Export DAI register and widget ctor and dctor functions Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 03/17] ASoC: Intel: Introduce AVS driver Cezary Rojewski
2022-03-04 15:51   ` Ranjani Sridharan
2022-03-04 16:43     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 04/17] ASoC: Intel: avs: Inter process communication Cezary Rojewski
2022-03-04 16:09   ` Ranjani Sridharan
2022-03-04 17:11     ` Cezary Rojewski
2022-03-07 16:15       ` Ranjani Sridharan
2022-03-07 16:23         ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 05/17] ASoC: Intel: avs: Add code loading requests Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 06/17] ASoC: Intel: avs: Add pipeline management requests Cezary Rojewski
2022-03-04 16:13   ` Ranjani Sridharan
2022-03-04 17:15     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 07/17] ASoC: Intel: avs: Add module " Cezary Rojewski
2022-03-04 16:21   ` Ranjani Sridharan
2022-03-04 17:21     ` Cezary Rojewski
2022-03-07 16:39       ` Ranjani Sridharan
2022-03-07 16:58         ` Cezary Rojewski
2022-03-07 17:05           ` Ranjani Sridharan
2022-03-07 17:27             ` Cezary Rojewski
2022-03-07 17:47               ` Pierre-Louis Bossart
2022-03-04 14:57 ` [PATCH v3 08/17] ASoC: Intel: avs: Add power " Cezary Rojewski
2022-03-04 16:24   ` Ranjani Sridharan
2022-03-04 17:30     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 09/17] ASoC: Intel: avs: Add ROM requests Cezary Rojewski
2022-03-04 16:26   ` Ranjani Sridharan
2022-03-04 17:33     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 10/17] ASoC: Intel: avs: Add basefw runtime-parameter requests Cezary Rojewski
2022-03-04 16:31   ` Ranjani Sridharan
2022-03-04 17:37     ` Cezary Rojewski
2022-03-07 16:41       ` Ranjani Sridharan
2022-03-07 17:02         ` Cezary Rojewski
2022-03-07 17:06           ` Ranjani Sridharan
2022-03-07 17:28             ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 11/17] ASoC: Intel: avs: Firmware resources management utilities Cezary Rojewski
2022-03-04 16:41   ` Ranjani Sridharan
2022-03-04 18:02     ` Cezary Rojewski
2022-03-07 16:46       ` Ranjani Sridharan
2022-03-07 17:13         ` Cezary Rojewski
2022-03-07 17:30           ` Ranjani Sridharan
2022-03-08 16:57             ` Cezary Rojewski
2022-03-08 17:22               ` Ranjani Sridharan
2022-03-08 18:07                 ` Cezary Rojewski
2022-03-08 18:26                   ` Ranjani Sridharan
2022-03-08 18:31                     ` Cezary Rojewski
2022-03-08 19:42                       ` Pierre-Louis Bossart
2022-03-09 17:23                         ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 12/17] ASoC: Intel: avs: Declare module configuration types Cezary Rojewski
2022-03-04 16:43   ` Ranjani Sridharan
2022-03-04 18:10     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 13/17] ASoC: Intel: avs: Dynamic firmware resources management Cezary Rojewski
2022-03-04 16:47   ` Ranjani Sridharan
2022-03-04 18:15     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 14/17] ASoC: Intel: avs: General code loading flow Cezary Rojewski
2022-03-04 16:54   ` Ranjani Sridharan [this message]
2022-03-04 18:29     ` Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 15/17] ASoC: Intel: avs: Implement CLDMA transfer Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 16/17] ASoC: Intel: avs: Code loading over CLDMA Cezary Rojewski
2022-03-04 14:57 ` [PATCH v3 17/17] ASoC: Intel: avs: Code loading over HDA Cezary Rojewski
2022-03-04 16:59   ` Ranjani Sridharan
2022-03-04 18:44     ` Cezary Rojewski
2022-03-04 18:56       ` Pierre-Louis Bossart
2022-03-07 14:31         ` Cezary Rojewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0c3e200bd14536534115e2a44fa744a102faa107.camel@linux.intel.com \
    --to=ranjani.sridharan@linux.intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=amadeuszx.slawinski@linux.intel.com \
    --cc=broonie@kernel.org \
    --cc=cezary.rojewski@intel.com \
    --cc=cujomalainey@chromium.org \
    --cc=harshapriya.n@intel.com \
    --cc=hdegoede@redhat.com \
    --cc=lma@semihalf.com \
    --cc=pierre-louis.bossart@linux.intel.com \
    --cc=rad@semihalf.com \
    --cc=tiwai@suse.com \
    --cc=upstream@semihalf.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.