1792 lines · 48.1 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)//// This file is provided under a dual BSD/GPLv2 license.  When using or// redistributing this file, you may do so under either license.//// Copyright(c) 2018 Intel Corporation//// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>//	    Ranjani Sridharan <ranjani.sridharan@linux.intel.com>//	    Rander Wang <rander.wang@intel.com>//          Keyon Jie <yang.jie@linux.intel.com>// /* * Hardware interface for generic Intel audio DSP HDA IP */ #include <sound/hdaudio_ext.h>#include <sound/hda_register.h> #include <linux/acpi.h>#include <linux/debugfs.h>#include <linux/module.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_intel.h>#include <sound/intel-dsp-config.h>#include <sound/intel-nhlt.h>#include <sound/soc-acpi-intel-ssp-common.h>#include <sound/soc_sdw_utils.h>#include <sound/sof.h>#include <sound/sof/xtensa.h>#include <sound/hda-mlink.h>#include "../sof-audio.h"#include "../sof-pci-dev.h"#include "../ops.h"#include "../ipc4-topology.h"#include "../../intel/common/sof-function-topology-lib.h"#include "hda.h" #include <trace/events/sof_intel.h> #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)#include <sound/soc-acpi-intel-match.h>#endif /* platform specific devices */#include "shim.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* * The default for SoundWire clock stop quirks is to power gate the IP * and do a Bus Reset, this will need to be modified when the DSP * needs to remain in D0i3 so that the Master does not lose context * and enumeration is not required on clock restart */static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;module_param(sdw_clock_stop_quirks, int, 0444);MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); static int sdw_params_stream(struct device *dev,			     struct sdw_intel_stream_params_data *params_data){	struct snd_soc_dai *d = params_data->dai;	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->substream->stream);	struct snd_sof_dai_config_data data = { 0 }; 	if (!w) {		dev_err(dev, "%s widget not found, check amp link num in the topology\n",			d->name);		return -EINVAL;	}	data.dai_index = (params_data->link_id << 8) | d->id;	data.dai_data = params_data->alh_stream_id;	data.dai_node_id = data.dai_data; 	return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);} static int sdw_params_free(struct device *dev, struct sdw_intel_stream_free_data *free_data){	struct snd_soc_dai *d = free_data->dai;	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, free_data->substream->stream);	struct snd_sof_dev *sdev = widget_to_sdev(w); 	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {		struct snd_sof_widget *swidget = w->dobj.private;		struct snd_sof_dai *dai = swidget->private;		struct sof_ipc4_copier_data *copier_data;		struct sof_ipc4_copier *ipc4_copier; 		ipc4_copier = dai->private;		ipc4_copier->dai_index = 0;		copier_data = &ipc4_copier->data; 		/* clear the node ID */		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;	} 	return 0;} struct sdw_intel_ops sdw_callback = {	.params_stream = sdw_params_stream,	.free_stream = sdw_params_free,}; static int sdw_ace2x_params_stream(struct device *dev,				   struct sdw_intel_stream_params_data *params_data){	return sdw_hda_dai_hw_params(params_data->substream,				     params_data->hw_params,				     params_data->dai,				     params_data->link_id,				     params_data->alh_stream_id);} static int sdw_ace2x_free_stream(struct device *dev,				 struct sdw_intel_stream_free_data *free_data){	return sdw_hda_dai_hw_free(free_data->substream,				   free_data->dai,				   free_data->link_id);} static int sdw_ace2x_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai){	return sdw_hda_dai_trigger(substream, cmd, dai);} static struct sdw_intel_ops sdw_ace2x_callback = {	.params_stream = sdw_ace2x_params_stream,	.free_stream = sdw_ace2x_free_stream,	.trigger = sdw_ace2x_trigger,}; static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev){	u32 interface_mask = hda_get_interface_mask(sdev);	struct sof_intel_hda_dev *hdev;	acpi_handle handle;	int ret; 	if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))		return -EINVAL; 	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)		return -EINVAL; 	return 0;} static int hda_sdw_probe(struct snd_sof_dev *sdev){	const struct sof_intel_dsp_desc *chip;	struct sof_intel_hda_dev *hdev;	struct sdw_intel_res res;	void *sdw; 	hdev = sdev->pdata->hw_pdata; 	memset(&res, 0, sizeof(res)); 	chip = get_chip_info(sdev->pdata);	if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) {		res.mmio_base = sdev->bar[HDA_DSP_BAR];		res.hw_ops = &sdw_intel_cnl_hw_ops;		res.shim_base = hdev->desc->sdw_shim_base;		res.alh_base = hdev->desc->sdw_alh_base;		res.ext = false;		res.ops = &sdw_callback;	} else {		/*		 * retrieve eml_lock needed to protect shared registers		 * in the HDaudio multi-link areas		 */		res.eml_lock = hdac_bus_eml_get_mutex(sof_to_bus(sdev), true,						      AZX_REG_ML_LEPTR_ID_SDW);		if (!res.eml_lock)			return -ENODEV; 		res.mmio_base = sdev->bar[HDA_DSP_HDA_BAR];		/*		 * the SHIM and SoundWire register offsets are link-specific		 * and will be determined when adding auxiliary devices		 */		res.hw_ops = &sdw_intel_lnl_hw_ops;		res.ext = true;		res.ops = &sdw_ace2x_callback; 		/* ACE3+ supports microphone privacy */		if (chip->hw_ip_version >= SOF_INTEL_ACE_3_0)			res.mic_privacy = true;	}	res.irq = sdev->ipc_irq;	res.handle = hdev->info.handle;	res.parent = sdev->dev; 	res.dev = sdev->dev;	res.clock_stop_quirks = sdw_clock_stop_quirks;	res.hbus = sof_to_bus(sdev); 	/*	 * 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;	struct snd_sof_pdata *pdata = sdev->pdata;	int ret; 	hdev = sdev->pdata->hw_pdata; 	if (!hdev->sdw)		return 0; 	if (pdata->machine && !pdata->machine->mach_params.link_mask)		return 0; 	ret = hda_sdw_check_lcount(sdev);	if (ret < 0)		return ret; 	return sdw_intel_startup(hdev->sdw);}EXPORT_SYMBOL_NS(hda_sdw_startup, "SND_SOC_SOF_INTEL_HDA_GENERIC"); static int hda_sdw_exit(struct snd_sof_dev *sdev){	struct sof_intel_hda_dev *hdev; 	hdev = sdev->pdata->hw_pdata; 	if (hdev->sdw)		sdw_intel_exit(hdev->sdw);	hdev->sdw = NULL; 	hda_sdw_int_enable(sdev, false); 	return 0;} bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev){	struct sof_intel_hda_dev *hdev;	bool ret = false;	u32 irq_status; 	hdev = sdev->pdata->hw_pdata; 	if (!hdev->sdw)		return ret; 	/* store status */	irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS2); 	/* invalid message ? */	if (irq_status == 0xffffffff)		goto out; 	/* SDW message ? */	if (irq_status & HDA_DSP_REG_ADSPIS2_SNDW)		ret = true; out:	return ret;}EXPORT_SYMBOL_NS(hda_common_check_sdw_irq, "SND_SOC_SOF_INTEL_HDA_GENERIC"); static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev){	u32 interface_mask = hda_get_interface_mask(sdev);	const struct sof_intel_dsp_desc *chip; 	if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))		return false; 	chip = get_chip_info(sdev->pdata);	if (chip && chip->check_sdw_irq)		return chip->check_sdw_irq(sdev); 	return false;} static irqreturn_t hda_dsp_sdw_thread(int irq, void *context){	return sdw_intel_thread(irq, context);} bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev){	struct sof_intel_hda_dev *hdev; 	hdev = sdev->pdata->hw_pdata;	if (hdev->sdw &&	    snd_sof_dsp_read(sdev, HDA_DSP_BAR,			     hdev->desc->sdw_shim_base + SDW_SHIM_WAKESTS))		return true; 	return false;}EXPORT_SYMBOL_NS(hda_sdw_check_wakeen_irq_common, "SND_SOC_SOF_INTEL_HDA_GENERIC"); static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev){	u32 interface_mask = hda_get_interface_mask(sdev);	const struct sof_intel_dsp_desc *chip; 	if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))		return false; 	chip = get_chip_info(sdev->pdata);	if (chip && chip->check_sdw_wakeen_irq)		return chip->check_sdw_wakeen_irq(sdev); 	return false;} void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev){	u32 interface_mask = hda_get_interface_mask(sdev);	struct sof_intel_hda_dev *hdev; 	if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))		return; 	hdev = sdev->pdata->hw_pdata;	if (!hdev->sdw)		return; 	sdw_intel_process_wakeen_event(hdev->sdw);}EXPORT_SYMBOL_NS(hda_sdw_process_wakeen_common, "SND_SOC_SOF_INTEL_HDA_GENERIC"); static bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev){	const struct sof_intel_dsp_desc *chip; 	chip = get_chip_info(sdev->pdata);	if (chip && chip->check_mic_privacy_irq)		return chip->check_mic_privacy_irq(sdev, true,						   AZX_REG_ML_LEPTR_ID_SDW); 	return false;} static void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev){	const struct sof_intel_dsp_desc *chip; 	chip = get_chip_info(sdev->pdata);	if (chip && chip->process_mic_privacy)		chip->process_mic_privacy(sdev, true, AZX_REG_ML_LEPTR_ID_SDW);} #else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */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_exit(struct snd_sof_dev *sdev){	return 0;} static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev){	return false;} static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context){	return IRQ_HANDLED;} static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev){	return false;} static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev){	return false;} static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } #endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ /* pre fw run operations */int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev){	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;	const struct sof_intel_dsp_desc *chip = hda->desc;	int ret; 	/* Power down DSP if left enabled to ensure a clean boot state. */	if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) {		dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); 		ret = chip->power_down_dsp(sdev);		if (ret < 0)			dev_warn(sdev->dev,				 "%s: failed to power down already-enabled DSP\n", __func__);	} 	/* disable clock gating and power gating */	return hda_dsp_ctrl_clock_power_gating(sdev, false);} /* post fw run operations */int hda_dsp_post_fw_run(struct snd_sof_dev *sdev){	int ret; 	if (sdev->first_boot) {		struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; 		ret = hda_sdw_startup(sdev);		if (ret < 0) {			dev_err(sdev->dev,				"error: could not startup SoundWire links\n");			return ret;		} 		/* Check if IMR boot is usable */		if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&		    (sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT ||		     sdev->pdata->ipc_type == SOF_IPC_TYPE_4)) {			hdev->imrboot_supported = true;			debugfs_create_bool("skip_imr_boot",					    0644, sdev->debugfs_root,					    &hdev->skip_imr_boot);		}	} 	hda_sdw_int_enable(sdev, true); 	/* re-enable clock gating and power gating */	return hda_dsp_ctrl_clock_power_gating(sdev, true);}EXPORT_SYMBOL_NS(hda_dsp_post_fw_run, "SND_SOC_SOF_INTEL_HDA_GENERIC"); /* * Debug */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG)static bool hda_use_msi = true;module_param_named(use_msi, hda_use_msi, bool, 0444);MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");#else#define hda_use_msi	(1)#endif static char *hda_model;module_param(hda_model, charp, 0444);MODULE_PARM_DESC(hda_model, "Use the given HDA board model."); static int dmic_num_override = -1;module_param_named(dmic_num, dmic_num_override, int, 0444);MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number"); static int mclk_id_override = -1;module_param_named(mclk_id, mclk_id_override, int, 0444);MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id"); static int bt_link_mask_override = -1;module_param_named(bt_link_mask, bt_link_mask_override, int, 0444);

Lines 1–500 of 1792.