aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml1
-rw-r--r--include/sound/hdaudio.h1
-rw-r--r--include/sound/hdaudio_ext.h5
-rw-r--r--sound/hda/hdac_stream.c22
-rw-r--r--sound/soc/fsl/fsl_sai.c23
-rw-r--r--sound/soc/fsl/fsl_sai.h1
-rw-r--r--sound/soc/fsl/fsl_xcvr.c40
-rw-r--r--sound/soc/fsl/fsl_xcvr.h21
-rw-r--r--sound/soc/intel/avs/avs.h8
-rw-r--r--sound/soc/intel/avs/core.c97
-rw-r--r--sound/soc/intel/avs/loader.c10
-rw-r--r--sound/soc/intel/avs/pcm.c368
-rw-r--r--sound/soc/intel/avs/topology.c10
13 files changed, 543 insertions, 64 deletions
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
index 70c4111d59c7..11aab891288d 100644
--- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -26,6 +26,7 @@ properties:
- fsl,imx8mq-sai
- fsl,imx8qm-sai
- fsl,imx8ulp-sai
+ - fsl,imx93-sai
- items:
- enum:
- fsl,imx8mm-sai
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index 78f1809a4ad6..a6872537724d 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -597,6 +597,7 @@ int snd_hdac_stream_get_spbmaxfifo(struct hdac_bus *bus,
struct hdac_stream *azx_dev);
void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
bool enable, int index);
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev);
int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
struct hdac_stream *azx_dev, u32 value);
int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value);
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h
index 90fd47e05370..68ab89211de2 100644
--- a/include/sound/hdaudio_ext.h
+++ b/include/sound/hdaudio_ext.h
@@ -51,6 +51,11 @@ struct hdac_ext_stream {
void __iomem *pphc_addr;
void __iomem *pplc_addr;
+ u32 pphcllpl;
+ u32 pphcllpu;
+ u32 pphcldpl;
+ u32 pphcldpu;
+
bool decoupled:1;
bool link_locked:1;
bool link_prepared;
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 35fe2bd582ac..3b250ee7f6a7 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -821,6 +821,28 @@ void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_drsm_enable);
+/*
+ * snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
+ * @azx_dev: HD-audio core stream to await RSM for
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout.
+ */
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ u32 mask, reg;
+ int ret;
+
+ mask = 1 << azx_dev->index;
+
+ ret = read_poll_timeout(snd_hdac_reg_readl, reg, !(reg & mask), 250, 2000, false, bus,
+ bus->drsmcap + AZX_REG_DRSM_CTL);
+ if (ret)
+ dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_wait_drsm);
+
/**
* snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
* @bus: HD-audio core bus
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 81f89f6767a2..a0ea27f06997 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -872,10 +872,10 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs),
FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
- sai->soc_data->fifo_depth - FSL_SAI_MAXBURST_TX);
+ sai->soc_data->fifo_depth - sai->dma_params_tx.maxburst);
regmap_update_bits(sai->regmap, FSL_SAI_RCR1(ofs),
FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
- FSL_SAI_MAXBURST_RX - 1);
+ sai->dma_params_rx.maxburst - 1);
snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
&sai->dma_params_rx);
@@ -1416,8 +1416,10 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->dma_params_rx.addr = sai->res->start + FSL_SAI_RDR0;
sai->dma_params_tx.addr = sai->res->start + FSL_SAI_TDR0;
- sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
- sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
+ sai->dma_params_rx.maxburst =
+ sai->soc_data->max_burst[RX] ? sai->soc_data->max_burst[RX] : FSL_SAI_MAXBURST_RX;
+ sai->dma_params_tx.maxburst =
+ sai->soc_data->max_burst[TX] ? sai->soc_data->max_burst[TX] : FSL_SAI_MAXBURST_TX;
sai->pinctrl = devm_pinctrl_get(&pdev->dev);
@@ -1579,6 +1581,18 @@ static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = {
.max_register = FSL_SAI_RTCAP,
};
+static const struct fsl_sai_soc_data fsl_sai_imx93_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 4,
+ .flags = 0,
+ .max_register = FSL_SAI_MCTL,
+ .max_burst = {8, 8},
+};
+
static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
{ .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
@@ -1590,6 +1604,7 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data },
{ .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
{ .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mp_data },
+ { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 697f6690068c..197748a888d5 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -235,6 +235,7 @@ struct fsl_sai_soc_data {
unsigned int reg_offset;
unsigned int flags;
unsigned int max_register;
+ unsigned int max_burst[2];
};
/**
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index c043efe4548d..2a6802fb2a8b 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -934,6 +934,14 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
{ FSL_XCVR_RX_DPTH_CTRL_SET, 0x00002C89 },
{ FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00002C89 },
{ FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00002C89 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_SET, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_TSCR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCTR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCRR, 0x00000000 },
{ FSL_XCVR_TX_DPTH_CTRL, 0x00000000 },
{ FSL_XCVR_TX_DPTH_CTRL_SET, 0x00000000 },
{ FSL_XCVR_TX_DPTH_CTRL_CLR, 0x00000000 },
@@ -944,6 +952,14 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
{ FSL_XCVR_TX_CS_DATA_3, 0x00000000 },
{ FSL_XCVR_TX_CS_DATA_4, 0x00000000 },
{ FSL_XCVR_TX_CS_DATA_5, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_SET, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_TSCR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCTR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCRR, 0x00000000 },
{ FSL_XCVR_DEBUG_REG_0, 0x00000000 },
{ FSL_XCVR_DEBUG_REG_1, 0x00000000 },
};
@@ -975,6 +991,14 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_RX_DPTH_CTRL_SET:
case FSL_XCVR_RX_DPTH_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_TSCR:
+ case FSL_XCVR_RX_DPTH_BCR:
+ case FSL_XCVR_RX_DPTH_BCTR:
+ case FSL_XCVR_RX_DPTH_BCRR:
case FSL_XCVR_TX_DPTH_CTRL:
case FSL_XCVR_TX_DPTH_CTRL_SET:
case FSL_XCVR_TX_DPTH_CTRL_CLR:
@@ -985,6 +1009,14 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_TX_CS_DATA_3:
case FSL_XCVR_TX_CS_DATA_4:
case FSL_XCVR_TX_CS_DATA_5:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_TSCR:
+ case FSL_XCVR_TX_DPTH_BCR:
+ case FSL_XCVR_TX_DPTH_BCTR:
+ case FSL_XCVR_TX_DPTH_BCRR:
case FSL_XCVR_DEBUG_REG_0:
case FSL_XCVR_DEBUG_REG_1:
return true;
@@ -1017,6 +1049,10 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_RX_DPTH_CTRL_SET:
case FSL_XCVR_RX_DPTH_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
case FSL_XCVR_TX_DPTH_CTRL_SET:
case FSL_XCVR_TX_DPTH_CTRL_CLR:
case FSL_XCVR_TX_DPTH_CTRL_TOG:
@@ -1026,6 +1062,10 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_TX_CS_DATA_3:
case FSL_XCVR_TX_CS_DATA_4:
case FSL_XCVR_TX_CS_DATA_5:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
return true;
default:
return false;
diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h
index 7f2853c60085..4769b0fca21d 100644
--- a/sound/soc/fsl/fsl_xcvr.h
+++ b/sound/soc/fsl/fsl_xcvr.h
@@ -49,6 +49,16 @@
#define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188
#define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL 0x1C0
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_SET 0x1C4
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR 0x1C8
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG 0x1CC
+
+#define FSL_XCVR_RX_DPTH_TSCR 0x1D0
+#define FSL_XCVR_RX_DPTH_BCR 0x1D4
+#define FSL_XCVR_RX_DPTH_BCTR 0x1D8
+#define FSL_XCVR_RX_DPTH_BCRR 0x1DC
+
#define FSL_XCVR_TX_DPTH_CTRL 0x220 /* TX datapath ctrl reg */
#define FSL_XCVR_TX_DPTH_CTRL_SET 0x224
#define FSL_XCVR_TX_DPTH_CTRL_CLR 0x228
@@ -59,6 +69,17 @@
#define FSL_XCVR_TX_CS_DATA_3 0x23C
#define FSL_XCVR_TX_CS_DATA_4 0x240
#define FSL_XCVR_TX_CS_DATA_5 0x244
+
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL 0x260
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_SET 0x264
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR 0x268
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG 0x26C
+
+#define FSL_XCVR_TX_DPTH_TSCR 0x270
+#define FSL_XCVR_TX_DPTH_BCR 0x274
+#define FSL_XCVR_TX_DPTH_BCTR 0x278
+#define FSL_XCVR_TX_DPTH_BCRR 0x27C
+
#define FSL_XCVR_DEBUG_REG_0 0x2E0
#define FSL_XCVR_DEBUG_REG_1 0x2F0
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 91f78eb11bc1..8d05b27608fe 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -24,6 +24,13 @@ struct avs_tplg_library;
struct avs_soc_component;
struct avs_ipc_msg;
+#ifdef CONFIG_ACPI
+#define AVS_S0IX_SUPPORTED \
+ (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
+#else
+#define AVS_S0IX_SUPPORTED false
+#endif
+
/*
* struct avs_dsp_ops - Platform-specific DSP operations
*
@@ -127,6 +134,7 @@ struct avs_dev {
struct list_head fw_list;
int *core_refs; /* reference count per core */
char **lib_names;
+ int num_lp_paths;
struct completion fw_ready;
struct work_struct probe_work;
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 0aaded90a99a..f7bc06404dbc 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -27,6 +27,14 @@
#include "avs.h"
#include "cldma.h"
+static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK;
+module_param(pgctl_mask, uint, 0444);
+MODULE_PARM_DESC(pgctl_mask, "PCI PGCTL policy override");
+
+static u32 cgctl_mask = AZX_CGCTL_MISCBDCGE_MASK;
+module_param(cgctl_mask, uint, 0444);
+MODULE_PARM_DESC(cgctl_mask, "PCI CGCTL policy override");
+
static void
avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
{
@@ -41,19 +49,16 @@ avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable)
{
- u32 value;
+ u32 value = enable ? 0 : pgctl_mask;
- value = enable ? 0 : AZX_PGCTL_LSRMD_MASK;
- avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL,
- AZX_PGCTL_LSRMD_MASK, value);
+ avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value);
}
static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable)
{
- u32 value;
+ u32 value = enable ? cgctl_mask : 0;
- value = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
- avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, value);
+ avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value);
}
void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
@@ -63,9 +68,8 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
{
- u32 value;
+ u32 value = enable ? AZX_VS_EM2_L1SEN : 0;
- value = enable ? AZX_VS_EM2_L1SEN : 0;
snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
}
@@ -534,12 +538,30 @@ static void avs_pci_remove(struct pci_dev *pci)
pm_runtime_get_noresume(&pci->dev);
}
-static int __maybe_unused avs_suspend_common(struct avs_dev *adev)
+static int avs_suspend_standby(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = adev->base.pci;
+
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ enable_irq_wake(pci->irq);
+ pci_save_state(pci);
+
+ return 0;
+}
+
+static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power)
{
struct hdac_bus *bus = &adev->base.core;
int ret;
flush_work(&adev->probe_work);
+ if (low_power && adev->num_lp_paths)
+ return avs_suspend_standby(adev);
snd_hdac_ext_bus_link_power_down_all(bus);
@@ -577,11 +599,30 @@ static int __maybe_unused avs_suspend_common(struct avs_dev *adev)
return 0;
}
-static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge)
+static int avs_resume_standby(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = adev->base.pci;
+
+ pci_restore_state(pci);
+ disable_irq_wake(pci->irq);
+
+ snd_hdac_ext_bus_link_power_up_all(bus);
+
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_init_cmd_io(bus);
+
+ return 0;
+}
+
+static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
{
struct hdac_bus *bus = &adev->base.core;
int ret;
+ if (low_power && adev->num_lp_paths)
+ return avs_resume_standby(adev);
+
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
avs_hdac_bus_init_chip(bus, true);
@@ -599,26 +640,50 @@ static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge)
static int __maybe_unused avs_suspend(struct device *dev)
{
- return avs_suspend_common(to_avs_dev(dev));
+ return avs_suspend_common(to_avs_dev(dev), true);
}
static int __maybe_unused avs_resume(struct device *dev)
{
- return avs_resume_common(to_avs_dev(dev), true);
+ return avs_resume_common(to_avs_dev(dev), true, true);
}
static int __maybe_unused avs_runtime_suspend(struct device *dev)
{
- return avs_suspend_common(to_avs_dev(dev));
+ return avs_suspend_common(to_avs_dev(dev), true);
}
static int __maybe_unused avs_runtime_resume(struct device *dev)
{
- return avs_resume_common(to_avs_dev(dev), true);
+ return avs_resume_common(to_avs_dev(dev), true, false);
+}
+
+static int __maybe_unused avs_freeze(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), false);
+}
+static int __maybe_unused avs_thaw(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), false, true);
+}
+
+static int __maybe_unused avs_poweroff(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), false);
+}
+
+static int __maybe_unused avs_restore(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), false, true);
}
static const struct dev_pm_ops avs_dev_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(avs_suspend, avs_resume)
+ .suspend = avs_suspend,
+ .resume = avs_resume,
+ .freeze = avs_freeze,
+ .thaw = avs_thaw,
+ .poweroff = avs_poweroff,
+ .restore = avs_restore,
SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
};
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index eb10e45790e7..56bb0a59249d 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -224,11 +224,19 @@ static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *
if (ret < 0)
return ret;
+ avs_hda_power_gating_enable(adev, false);
+ avs_hda_clock_gating_enable(adev, false);
+ avs_hda_l1sen_enable(adev, false);
+
hda_cldma_set_data(cl, (void *)mod->data, mod->size);
hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
hda_cldma_stop(cl);
+ avs_hda_l1sen_enable(adev, true);
+ avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
+
if (ret) {
dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret);
avs_release_last_firmware(adev);
@@ -605,6 +613,7 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
for (i = 1; i < adev->fw_cfg.max_libs_count; i++)
memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
+ avs_hda_power_gating_enable(adev, false);
avs_hda_clock_gating_enable(adev, false);
avs_hda_l1sen_enable(adev, false);
@@ -625,6 +634,7 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
reenable_gating:
avs_hda_l1sen_enable(adev, true);
avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 293336c2fc63..ca624fbb5c0d 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -28,6 +28,8 @@ struct avs_dma_data {
* host stream assigned
*/
struct hdac_ext_stream *host_stream;
+
+ struct snd_pcm_substream *substream;
};
static struct avs_tplg_path_template *
@@ -55,8 +57,11 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
return dw->priv;
}
-static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe)
+static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe,
+ const struct snd_soc_dai_ops *ops)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_tplg_path_template *template;
struct avs_dma_data *data;
@@ -71,9 +76,13 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d
if (!data)
return -ENOMEM;
+ data->substream = substream;
data->template = template;
snd_soc_dai_set_dma_data(dai, substream, data);
+ if (rtd->dai_link->ignore_suspend)
+ adev->num_lp_paths++;
+
return 0;
}
@@ -151,15 +160,22 @@ static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *subst
return ret;
}
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops;
+
static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- return avs_dai_startup(substream, dai, false);
+ return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops);
}
static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_dma_data *data;
+ if (rtd->dai_link->ignore_suspend)
+ adev->num_lp_paths--;
+
data = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
@@ -202,30 +218,43 @@ static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct
static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct avs_dma_data *data;
int ret = 0;
data = snd_soc_dai_get_dma_data(dai, substream);
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+ break;
+ }
+
ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
if (ret < 0)
dev_err(dai->dev, "run BE path failed: %d\n", ret);
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
ret = avs_path_pause(data->path);
if (ret < 0)
dev_err(dai->dev, "pause BE path failed: %d\n", ret);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- ret = avs_path_reset(data->path);
- if (ret < 0)
- dev_err(dai->dev, "reset BE path failed: %d\n", ret);
- }
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
break;
default:
@@ -245,9 +274,11 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
.trigger = avs_dai_nonhda_be_trigger,
};
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops;
+
static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- return avs_dai_startup(substream, dai, false);
+ return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops);
}
static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
@@ -343,6 +374,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct hdac_ext_stream *link_stream;
struct avs_dma_data *data;
int ret = 0;
@@ -353,15 +385,29 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
link_stream = substream->runtime->private_data;
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_stream_start(link_stream);
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+ break;
+ }
+
ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
if (ret < 0)
dev_err(dai->dev, "run BE path failed: %d\n", ret);
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
ret = avs_path_pause(data->path);
@@ -370,11 +416,9 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
snd_hdac_ext_stream_clear(link_stream);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- ret = avs_path_reset(data->path);
- if (ret < 0)
- dev_err(dai->dev, "reset BE path failed: %d\n", ret);
- }
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
break;
default:
@@ -407,6 +451,8 @@ static const struct snd_pcm_hw_constraint_list hw_rates = {
.mask = 0,
};
+const struct snd_soc_dai_ops avs_dai_fe_ops;
+
static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -416,7 +462,7 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so
struct hdac_ext_stream *host_stream;
int ret;
- ret = avs_dai_startup(substream, dai, true);
+ ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops);
if (ret)
return ret;
@@ -443,8 +489,13 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so
static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_dma_data *data;
+ if (rtd->dai_link->ignore_suspend)
+ adev->num_lp_paths--;
+
data = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
@@ -499,7 +550,7 @@ create_err:
return ret;
}
-static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
@@ -523,9 +574,15 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so
snd_hdac_stream_cleanup(hdac_stream(host_stream));
hdac_stream(host_stream)->prepared = false;
- ret = snd_pcm_lib_free_pages(substream);
- if (ret < 0)
- dev_dbg(dai->dev, "Failed to free pages!\n");
+ return ret;
+}
+
+static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = __avs_dai_fe_hw_free(substream, dai);
+ snd_pcm_lib_free_pages(substream);
return ret;
}
@@ -571,6 +628,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
struct hdac_bus *bus;
@@ -582,17 +640,36 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
bus = hdac_stream(host_stream)->bus;
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&bus->reg_lock, flags);
snd_hdac_stream_start(hdac_stream(host_stream), true);
spin_unlock_irqrestore(&bus->reg_lock, flags);
+ /* Timeout on DRSM poll shall not stop the resume so ignore the result. */
+ if (cmd == SNDRV_PCM_TRIGGER_RESUME)
+ snd_hdac_stream_wait_drsm(hdac_stream(host_stream));
+
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+ break;
+ }
+
ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
if (ret < 0)
dev_err(dai->dev, "run FE path failed: %d\n", ret);
+
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
ret = avs_path_pause(data->path);
@@ -603,11 +680,9 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
snd_hdac_stream_stop(hdac_stream(host_stream));
spin_unlock_irqrestore(&bus->reg_lock, flags);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- ret = avs_path_reset(data->path);
- if (ret < 0)
- dev_err(dai->dev, "reset FE path failed: %d\n", ret);
- }
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset FE path failed: %d\n", ret);
break;
default:
@@ -662,6 +737,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
if (ret < 0)
return ret;
+ avs_hda_power_gating_enable(adev, false);
avs_hda_clock_gating_enable(adev, false);
avs_hda_l1sen_enable(adev, false);
@@ -669,6 +745,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
avs_hda_l1sen_enable(adev, true);
avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
if (!ret)
ret = avs_module_info_init(adev, false);
@@ -752,33 +829,226 @@ static void avs_component_remove(struct snd_soc_component *component)
}
}
+static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret;
+
+ substream = data->substream;
+ rtd = snd_pcm_substream_chip(substream);
+
+ ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai);
+ if (ret)
+ dev_err(dai->dev, "hw_params on resume failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct hdac_ext_stream *host_stream;
+ struct hdac_stream *hstream;
+ struct hdac_bus *bus;
+ int ret;
+
+ host_stream = data->host_stream;
+ hstream = hdac_stream(host_stream);
+ bus = hdac_stream(host_stream)->bus;
+
+ /* Set DRSM before programming stream and position registers. */
+ snd_hdac_stream_drsm_enable(bus, true, hstream->index);
+
+ ret = dai->driver->ops->prepare(data->substream, dai);
+ if (ret) {
+ dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret);
+ return ret;
+ }
+
+ writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL);
+ writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU);
+ writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL);
+ writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU);
+
+ /* As per HW spec recommendation, program LPIB and DPIB to the same value. */
+ snd_hdac_stream_set_lpib(hstream, hstream->lpib);
+ snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib);
+
+ return 0;
+}
+
+static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ int ret;
+
+ ret = dai->driver->ops->prepare(data->substream, dai);
+ if (ret)
+ dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ host_stream = data->host_stream;
+
+ /* Store position addresses so we can resume from them later on. */
+ hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream));
+ host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL);
+ host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU);
+ host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL);
+ host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU);
+
+ ret = __avs_dai_fe_hw_free(data->substream, dai);
+ if (ret < 0)
+ dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ int ret;
+
+ ret = dai->driver->ops->hw_free(data->substream, dai);
+ if (ret < 0)
+ dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_component_pm_op(struct snd_soc_component *component, bool be,
+ int (*op)(struct snd_soc_dai *, struct avs_dma_data *))
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct avs_dma_data *data;
+ struct snd_soc_dai *dai;
+ int ret;
+
+ for_each_component_dais(component, dai) {
+ data = dai->playback_dma_data;
+ if (data) {
+ rtd = snd_pcm_substream_chip(data->substream);
+ if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
+ ret = op(dai, data);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ data = dai->capture_dma_data;
+ if (data) {
+ rtd = snd_pcm_substream_chip(data->substream);
+ if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
+ ret = op(dai, data);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be)
+{
+ return avs_component_pm_op(component, be, &avs_dai_resume_hw_params);
+}
+
+static int avs_component_resume_prepare(struct snd_soc_component *component, bool be)
+{
+ int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data);
+
+ if (be)
+ prepare_cb = &avs_dai_resume_be_prepare;
+ else
+ prepare_cb = &avs_dai_resume_fe_prepare;
+
+ return avs_component_pm_op(component, be, prepare_cb);
+}
+
+static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be)
+{
+ int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data);
+
+ if (be)
+ hw_free_cb = &avs_dai_suspend_be_hw_free;
+ else
+ hw_free_cb = &avs_dai_suspend_fe_hw_free;
+
+ return avs_component_pm_op(component, be, hw_free_cb);
+}
+
+static int avs_component_suspend(struct snd_soc_component *component)
+{
+ int ret;
+
+ /*
+ * When freeing paths, FEs need to be first as they perform
+ * path unbinding.
+ */
+ ret = avs_component_suspend_hw_free(component, false);
+ if (ret)
+ return ret;
+
+ return avs_component_suspend_hw_free(component, true);
+}
+
+static int avs_component_resume(struct snd_soc_component *component)
+{
+ int ret;
+
+ /*
+ * When creating paths, FEs need to be last as they perform
+ * path binding.
+ */
+ ret = avs_component_resume_hw_params(component, true);
+ if (ret)
+ return ret;
+
+ ret = avs_component_resume_hw_params(component, false);
+ if (ret)
+ return ret;
+
+ /* It is expected that the LINK stream is prepared first. */
+ ret = avs_component_resume_prepare(component, true);
+ if (ret)
+ return ret;
+
+ return avs_component_resume_prepare(component, false);
+}
+
+static const struct snd_pcm_hardware avs_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
static int avs_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct snd_pcm_hardware hwparams;
/* only FE DAI links are handled here */
if (rtd->dai_link->no_pcm)
return 0;
- hwparams.info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
-
- hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE;
- hwparams.period_bytes_min = 128;
- hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2;
- hwparams.periods_min = 2;
- hwparams.periods_max = AZX_MAX_FRAG;
- hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE;
- hwparams.fifo_size = 0;
-
- return snd_soc_set_runtime_hwparams(substream, &hwparams);
+ return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware);
}
static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
@@ -840,6 +1110,8 @@ static const struct snd_soc_component_driver avs_component_driver = {
.name = "avs-pcm",
.probe = avs_component_probe,
.remove = avs_component_remove,
+ .suspend = avs_component_suspend,
+ .resume = avs_component_resume,
.open = avs_component_open,
.pointer = avs_component_pointer,
.mmap = avs_component_mmap,
@@ -1120,9 +1392,15 @@ static int avs_component_hda_open(struct snd_soc_component *component,
struct hdac_ext_stream *link_stream;
struct hda_codec *codec;
- /* only BE DAI links are handled here */
- if (!rtd->dai_link->no_pcm)
- return avs_component_open(component, substream);
+ if (!rtd->dai_link->no_pcm) {
+ struct snd_pcm_hardware hwparams = avs_pcm_hardware;
+
+ /* RESUME unsupported for de-coupled HD-Audio capture. */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hwparams.info &= ~SNDRV_PCM_INFO_RESUME;
+
+ return snd_soc_set_runtime_hwparams(substream, &hwparams);
+ }
codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
@@ -1155,6 +1433,8 @@ static const struct snd_soc_component_driver avs_hda_component_driver = {
.name = "avs-hda-pcm",
.probe = avs_component_hda_probe,
.remove = avs_component_hda_remove,
+ .suspend = avs_component_suspend,
+ .resume = avs_component_resume,
.open = avs_component_hda_open,
.close = avs_component_hda_close,
.pointer = avs_component_pointer,
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 8a9f9fc48938..e845eaf0a1e7 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1405,6 +1405,11 @@ static int avs_widget_load(struct snd_soc_component *comp, int index,
if (!le32_to_cpu(dw->priv.size))
return 0;
+ if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) {
+ dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n");
+ w->ignore_suspend = false;
+ }
+
tplg = acomp->tplg;
mach = dev_get_platdata(comp->card->dev);
@@ -1442,6 +1447,11 @@ static int avs_dai_load(struct snd_soc_component *comp, int index,
static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link,
struct snd_soc_tplg_link_config *cfg)
{
+ if (link->ignore_suspend && !AVS_S0IX_SUPPORTED) {
+ dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n");
+ link->ignore_suspend = false;
+ }
+
if (!link->no_pcm) {
/* Stream control handled by IPCs. */
link->nonatomic = true;