// 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. Load more
// 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);