diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c index 671443204e04..bacbfc55c598 100644 --- a/sound/soc/amd/yc/pci-acp6x.c +++ b/sound/soc/amd/yc/pci-acp6x.c @@ -301,7 +301,7 @@ static int snd_acp6x_suspend(struct device *dev) return ret; } -static int snd_acp6x_resume(struct device *dev) +static int snd_acp6x_runtime_resume(struct device *dev) { struct acp6x_dev_data *adata; int ret; @@ -313,8 +313,42 @@ static int snd_acp6x_resume(struct device *dev) return ret; } +static void acp6x_disable_msi_on_resume(struct pci_dev *pdev) +{ + u16 control; + + if (!pdev->msi_cap) + return; + + pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + dev_warn(&pdev->dev, + "ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n", + control); + control &= ~PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control); + } +} + +static int snd_acp6x_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + /* + * BIOS/firmware may re-enable MSI in PCI config space during + * system resume even though this driver only uses legacy INTx + * interrupts. If MSI is left enabled with stale address/data + * registers, the device will write interrupts to a bogus address + * causing IOMMU IO_PAGE_FAULT and interrupt delivery failure. + * Explicitly clear the MSI Enable bit before reinitializing + * the ACP hardware. + */ + acp6x_disable_msi_on_resume(pdev); + return snd_acp6x_runtime_resume(dev); +} + static const struct dev_pm_ops acp6x_pm = { - RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL) + RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume) }; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 9d6bcfbbf0b2..c55ae3532682 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -290,7 +290,7 @@ static const char * const cs42l52_ng_type_text[] = { }; static SOC_ENUM_SINGLE_DECL(ng_type_enum, - CS42L52_NOISE_GATE_CTL, 6, + CS42L52_NOISE_GATE_CTL, 7, cs42l52_ng_type_text); static const char * const left_swap_text[] = { diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 4d9a22a1029c..f0ea5309a6d4 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -481,7 +481,7 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = { 0, 0x3f, 0, 0), SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL, 5, 0x07, 1, alc_tlv), - SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD, + SOC_SINGLE_TLV("Limiter Cushion", CS42L56_LIM_THRESHOLD_CTL, 2, 0x07, 1, alc_tlv), SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1), diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index c8d4c68af562..324270cc5cb2 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -166,7 +166,7 @@ static const struct snd_kcontrol_new max98371_snd_controls[] = { 0, (1<host_stream; - if (runtime->state == SNDRV_PCM_STATE_XRUN) + if (runtime->state != SNDRV_PCM_STATE_PREPARED) hdac_stream(host_stream)->prepared = false; if (hdac_stream(host_stream)->prepared) return 0; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 24a1175dd690..22c15fbb93f2 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2458,11 +2458,14 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ch_map >>= 4; } - if (swidget->id == snd_soc_dapm_dai_in && ch_count == out_ref_channels) { + if ((swidget->id == snd_soc_dapm_dai_in && ch_count == out_ref_channels) || + ch_count < blob->alh_cfg.device_count) { /* * For playback DAI widgets where the channel number is equal to - * the output reference channels, set the step = 0 to ensure all - * the ch_mask is applied to all alh mappings. + * the output reference channels, or when there are fewer + * channels than aggregated devices (e.g. a mono capture + * stream split across multiple links), set step = 0 to + * ensure all the ch_mask is applied to all alh mappings. */ mask = ch_mask; step = 0; diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 64da8df15bf2..21084cd98e43 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -576,7 +576,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_ipc_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { @@ -593,7 +593,7 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) event->callback(event->cdev, sdev->fw_state); @@ -628,7 +628,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) { if (event->cdev == cdev) { diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index e0fef47a227e..13a07ef93240 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -720,7 +720,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) if (err) { regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr); - sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) && + sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) == SPDIFRX_SPDIFEN_SYNC; /* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */