From ff404ef11b975baf38c4472c51a6674c239e8021 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Mon, 19 Oct 2026 21:40:22 +0900 Subject: [PATCH 01/45] ASoC: cs42l56: Remove the ADC Mute Switch control The ADC Mute Switch control writes bits 2 and 3 of the Misc ADC Control register. These are the ADCA Invert and ADCB Invert bits, which have their own controls. The ADC mute bits are bits 0 and 1, and the DAI mute callback already sets them. So the control has never muted the ADCs, and nobody can be relying on it. Remove it rather than point it at the mute bits, so the ADCs are muted in one place only. Fixes: 272b5edd3b8f ("ASoC: Add support for CS42L56 CODEC") Suggested-by: Charles Keepax Link: https://lore.kernel.org/all/atXh3Br+fuH83S0q@opensource.cirrus.com/ Assisted-by: Claude:claude-opus-5-5 Signed-off-by: HyeongJun An Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20261019124022.3410793-1-sammiee5311@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l56.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 54d23857b0ad..ecce51985a11 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -419,7 +419,6 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = { CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv), SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR, CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv), - SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1), SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1), SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME, From 6cc7db9fdd06c992ee38793b2d556524fc0cf574 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 20 Oct 2026 14:57:06 +0100 Subject: [PATCH 02/45] ASoC: cs35l56: Wait for firmware timer expiry before system suspend On CS35L5x firmware older than 3.13.7 (B0 silicon) and 4.7.16 (B2 silicon) a SoundWire bus reset is not allowed within 250ms of the last firmware timer start. This is already avoided during runtime suspend by the various idle timeouts. System suspend can happen at any time so needs special protection. The driver cannot cancel the firmware timer, so it must wait long enough for the timer to expire. The delay is placed in the suspend stage because typically this has less impact on end users than delays in resuming. The amp is in PS3 power state when suspending and no new timers will be started in this state. An extra 10ms is added to the delay to avoid a race on the exact instant the firmware timer expires. There is no need to have the delay for every driver instance. The timers on each amp will expire in parallel, so the delay is only needed once. A global flag is used so that the first instance to reach its suspend() callback will delay and then clear the flag. The cs35l56_fw_idle_wait_lock mutex is to ensure this is still safe if async_suspend was ever enabled for SoundWire devices. Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Reported-by: Ferenc Lengyel Closes: https://github.com/thesofproject/linux/issues/5898 Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20261020135706.3573260-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 4 ++++ sound/soc/codecs/cs35l56-sdw.c | 27 +++++++++++++++++++++- sound/soc/codecs/cs35l56-shared.c | 38 +++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.c | 17 ++++++++++++++ sound/soc/codecs/cs35l56.h | 1 + 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 747e10ec1a27..ce6383c2c60c 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -209,6 +209,9 @@ struct snd_ctl_elem_value; #define CS35L56_GPIO_FN_GPIO 0x00000001 +/* DSP1_FW_VER */ +#define CS35L56_FW_MAIN_VERSION_MASK GENMASK(19, 0) + /* Mixer input sources */ #define CS35L56_INPUT_SRC_NONE 0x00 #define CS35L56_INPUT_SRC_ASP1RX1 0x08 @@ -446,6 +449,7 @@ int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base, int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version); void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base); +bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base); void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index e10f04d677f4..a49ad69644b5 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #define CS35L56_LATE_READ_POLL_US 10 #define CS35L56_LATE_READ_TIMEOUT_US 1000 +static DEFINE_MUTEX(cs35l56_fw_idle_wait_lock); +static bool cs35l56_fw_idle_wait_pending; + static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral, unsigned int mask, unsigned int match) @@ -387,14 +391,34 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) return 0; } +static int cs35l56_sdw_system_suspend_prepare(struct device *dev) +{ + cs35l56_fw_idle_wait_pending = true; + + return 0; +} + static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; if (cs35l56->sdw_attached) cs35l56_mask_soundwire_interrupts(cs35l56); - return cs35l56_system_suspend(dev); + ret = cs35l56_system_suspend(dev); + if (ret < 0) + return ret; + + scoped_guard(mutex, &cs35l56_fw_idle_wait_lock) { + if (cs35l56_fw_idle_wait_pending && cs35l56->needs_wait_for_fw_idle) { + dev_dbg(cs35l56->base.dev, "Wait for FW timer expiry\n"); + msleep(CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 10); + cs35l56_fw_idle_wait_pending = false; + } + } + + return 0; } static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) @@ -460,6 +484,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral) } static const struct dev_pm_ops cs35l56_sdw_pm = { + .prepare = cs35l56_sdw_system_suspend_prepare, SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 7b3e37d462d6..b37f65cd4d33 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1379,6 +1379,44 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base) } EXPORT_SYMBOL_NS_GPL(cs35l56_warn_if_firmware_missing, "SND_SOC_CS35L56_SHARED"); +bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base) +{ + unsigned int fw_ver; + bool prot_sts; + int ret; + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + switch (cs35l56_base->rev) { + case 0xb0: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x30d07) + return true; + + return false; + case 0xb2: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x40710) + return true; + + return false; + default: + return false; + } + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs35l56_needs_wait_for_firmware_timer_expiry, "SND_SOC_CS35L56_SHARED"); + void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) { __be32 pid, sid, tid; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index dd264c310e1e..a4d25c287941 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1703,6 +1703,10 @@ int cs35l56_system_resume(struct device *dev) dev_dbg(dev, "system_resume\n"); + /* Assume the firmware reset to default until we can check it */ + if (cs35l56->base.init_done) + cs35l56->needs_wait_for_fw_idle = true; + /* * We might have done a hard reset or the CS35L56 was power-cycled * so wait for control port to be ready. @@ -1717,6 +1721,16 @@ int cs35l56_system_resume(struct device *dev) if (ret) return ret; + if (cs35l56->sdw_peripheral && cs35l56->base.init_done) { + ret = pm_runtime_resume_and_get(cs35l56->base.dev); + if (ret < 0) + return ret; + + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + pm_runtime_put_autosuspend(cs35l56->base.dev); + } + /* Firmware won't have been loaded if the component hasn't probed */ if (!cs35l56->component) return 0; @@ -2241,6 +2255,9 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (ret) return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + cs35l56->base.init_done = true; complete_all(&cs35l56->init_completion); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index f7cf8aa653e2..b46f410f0e09 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -43,6 +43,7 @@ struct cs35l56_private { bool soft_resetting; bool sdw_attached; bool component_registered; + bool needs_wait_for_fw_idle; struct completion init_completion; int speaker_id; From bef34efba74d87d5c20e3bb93539fa45b3cb8a83 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 20 Oct 2026 10:46:33 +0200 Subject: [PATCH 03/45] ASoC: SOF: amd: add ACPI dependency The newly added acp_asp_mailbox.c file makes no sense without ACPI and fails to build in that configuration: sound/soc/sof/amd/acp_asp_mailbox.c: In function 'acp7x_asp_send_cmd': sound/soc/sof/amd/acp_asp_mailbox.c:59:22: error: invalid use of undefined type 'struct acpi_device' 59 | handle = adev->handle; | ^~ Address this by adding the required Kconfig dependency at the top level here. Fixes: cec3598305a6 ("ASoC: SOF: amd: add ASP carveout firmware loading for ACP7.B/7.F") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20261020084656.2725461-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index d64f682cfbea..baa0fbf07ab2 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -7,6 +7,7 @@ config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD + depends on ACPI depends on X86 || COMPILE_TEST help This adds support for Sound Open Firmware for AMD platforms. From 872215109fc880de7a03fa30437346f9212649e8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 21 Oct 2026 12:17:42 +0200 Subject: [PATCH 04/45] ASoC: wm8994: fix MCLK gating for AIF clocks sourced from MCLK2 aif_mclk_set() masks the AIFnCLK_SRC field of the AIFn clocking register, but compares the result with unshifted field values. The MCLK2 source (0x08) therefore never matches, so MCLK2 is never enabled for an AIF clocked from it, and the AIF relies on other users of that clock to keep it running. On the Exynos4412 Trats2 board, AIF1 and SYSCLK run from MCLK2, the 32768Hz PMIC clock, while the codec is idle. That clock is shared with the WLAN and Bluetooth chips, which enable it only once their drivers are loaded. Until then the codec runs from a stopped clock: FLL1 fails to lock when configured from that state, and the codec floods the kernel log with FIFO error interrupts once a digital path is powered up: wm8994-codec wm8994-codec: Timed out waiting for FLL lock wm8994-codec wm8994-codec: FIFO error Booting with clk_ignore_unused, which keeps that clock running from the bootloader, makes both issues disappear. Fixing just the comparison is not enough. The AIF clock source can be changed while the AIF clock is running, for example the Midas machine driver switches AIF1 between MCLK2 and FLL1 while AIF1CLK is kept enabled for jack detection. Looking up the clock again on disable can then release a different MCLK than the one enabled, leaking the enable count of one clock and underflowing the other. Shift the field value before decoding it, and remember which MCLK has been enabled for each AIF, so that the same clock is released on disable. While at it, report the MCLK number 1-based in the error message. Fixes: 001b83d395ab ("ASoC: wm8994: Add support for MCLKn clock gating") Signed-off-by: Marek Szyprowski Reviewed-by: Charles Keepax Reviewed-by: Sylwester Nawrocki Link: https://patch.msgid.link/20261021101750.3297770-2-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 28 ++++++++++++++++++---------- sound/soc/codecs/wm8994.h | 1 + 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index c65a7769a9d2..e21adcaf3477 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1059,13 +1059,24 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl unsigned int offset, val, clk_idx; int ret; + /* + * The AIF clock source may be changed while the AIF clock is + * running, so release the MCLK which has been enabled for it + * instead of the one currently selected. + */ + if (!enable) { + clk_disable_unprepare(wm8994->aif_mclk[aif]); + wm8994->aif_mclk[aif] = NULL; + return 0; + } + if (aif) offset = 4; else offset = 0; val = snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1 + offset); - val &= WM8994_AIF1CLK_SRC_MASK; + val = (val & WM8994_AIF1CLK_SRC_MASK) >> WM8994_AIF1CLK_SRC_SHIFT; switch (val) { case 0: @@ -1078,16 +1089,13 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl return 0; } - if (enable) { - ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); - if (ret < 0) { - dev_err(component->dev, "Failed to enable MCLK%d\n", - clk_idx); - return ret; - } - } else { - clk_disable_unprepare(wm8994->mclk[clk_idx].clk); + ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK%d\n", + clk_idx + 1); + return ret; } + wm8994->aif_mclk[aif] = wm8994->mclk[clk_idx].clk; return 0; } diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index b28398aa9e48..5b22cfab317e 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -81,6 +81,7 @@ struct wm8994_priv { struct wm_hubs_data hubs; struct wm8994 *wm8994; struct clk_bulk_data mclk[WM8994_NUM_MCLK]; + struct clk *aif_mclk[2]; int sysclk[2]; int sysclk_rate[2]; int mclk_rate[2]; From 693574504051a19383640cfb99524a6b69e4624f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 21 Oct 2026 12:17:43 +0200 Subject: [PATCH 05/45] ASoC: wm8994: don't lose the AIF clock ratio when staying at 32kHz SYSCLK When SYSCLK drops below 50kHz, the AIFnCLK rate ratio is saved and temporarily lowered to 128fs for accessory detection, then restored once SYSCLK gets fast enough again. The ratio is however saved again on each clock reconfiguration done while SYSCLK is still slow, which overwrites the original ratio with the 128fs one. This happens on the Exynos4412 Trats2 board, which set MCLK2 as SYSCLK twice during the card probe and then configure FLL1 while still running from MCLK2. Once SYSCLK is switched to FLL1, the 128fs ratio is "restored", so AIF1 is left configured for 48kHz at 128fs while being clocked at 256fs. If any digital path is powered up before a stream reconfigures the AIF, the codec raises FIFO error interrupts every 10ms, flooding the kernel log: wm8994-codec wm8994-codec: FIFO error Save the original ratio only when entering the low SYSCLK mode. While at it, move the code duplicated in _wm8994_set_fll() and wm8994_set_dai_sysclk() to a common helper. Fixes: 6730049a4763 ("ASoC: wm8994: Lower AIFnCLK divisor when dropping to 32kHz") Signed-off-by: Marek Szyprowski Reviewed-by: Charles Keepax Reviewed-by: Sylwester Nawrocki Link: https://patch.msgid.link/20261021101750.3297770-3-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 94 +++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index e21adcaf3477..e4ea9d41107b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2207,6 +2207,44 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, return 0; } +/* + * If SYSCLK is less than 50kHz adjust AIFnCLK dividers for detection, + * restoring the original ones once SYSCLK is fast enough again. + */ +static void wm8994_update_aif_clk_rate(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { + /* Don't overwrite the saved dividers with the 128fs ones */ + if (!wm8994->aifdiv[0]) { + dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); + + wm8994->aifdiv[0] = snd_soc_component_read(component, + WM8994_AIF1_RATE) + & WM8994_AIF1CLK_RATE_MASK; + wm8994->aifdiv[1] = snd_soc_component_read(component, + WM8994_AIF2_RATE) + & WM8994_AIF1CLK_RATE_MASK; + } + + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, 0x1); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, 0x1); + } else if (wm8994->aifdiv[0]) { + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, + wm8994->aifdiv[0]); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, + wm8994->aifdiv[1]); + + wm8994->aifdiv[0] = 0; + wm8994->aifdiv[1] = 0; + } +} + static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, unsigned int freq_in, unsigned int freq_out) { @@ -2441,33 +2479,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, configure_clock(component); - /* - * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers - * for detection. - */ - if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { - dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); - - wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) - & WM8994_AIF1CLK_RATE_MASK; - wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) - & WM8994_AIF1CLK_RATE_MASK; - - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, 0x1); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, 0x1); - } else if (wm8994->aifdiv[0]) { - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, - wm8994->aifdiv[0]); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, - wm8994->aifdiv[1]); - - wm8994->aifdiv[0] = 0; - wm8994->aifdiv[1] = 0; - } + wm8994_update_aif_clk_rate(component); return 0; } @@ -2585,33 +2597,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, configure_clock(component); - /* - * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers - * for detection. - */ - if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { - dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); - - wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) - & WM8994_AIF1CLK_RATE_MASK; - wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) - & WM8994_AIF1CLK_RATE_MASK; - - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, 0x1); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, 0x1); - } else if (wm8994->aifdiv[0]) { - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, - wm8994->aifdiv[0]); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, - wm8994->aifdiv[1]); - - wm8994->aifdiv[0] = 0; - wm8994->aifdiv[1] = 0; - } + wm8994_update_aif_clk_rate(component); return 0; } From 7916b55b100973b5fb5b974e8f7c8ae9f3ac1d2e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 21 Oct 2026 12:17:44 +0200 Subject: [PATCH 06/45] ASoC: wm8994: fix MCLK index used for setting MCLK rate wm8994_set_dai_sysclk() passes the AIF index (dai->id - 1) to wm8994_set_mclk_rate(), which uses it to index the MCLK clocks and their cached rates. As a result, selecting MCLK2 as the AIF1 clock source sets and reads back the rate of MCLK1 instead, and the rate of MCLK1 is then stored as the MCLK2 rate. On the Exynos4412 Trats2 board, where MCLK1 is a fixed 24MHz clock and MCLK2 a 32768Hz clock used when the codec is idle, this gives: wm8994-codec wm8994-codec: AIF1 using MCLK2 at 24000000Hz so the driver assumes a 12MHz SYSCLK while it actually runs at 32kHz. This skips the 128fs AIF clock ratio setup for low SYSCLK rates and makes the mic detection rate configuration use the wrong clock rate. Pass the MCLK index matching the selected clock source instead. Fixes: 419e2f508336 ("ASoC: wm8994: Add support for setting MCLK clock rate") Signed-off-by: Marek Szyprowski Reviewed-by: Charles Keepax Reviewed-by: Sylwester Nawrocki Link: https://patch.msgid.link/20261021101750.3297770-4-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index e4ea9d41107b..8adb3de546dd 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2539,7 +2539,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, case WM8994_SYSCLK_MCLK1: wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1; - ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK1, &freq); if (ret < 0) return ret; @@ -2552,7 +2552,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, /* TODO: Set GPIO AF */ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2; - ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK2, &freq); if (ret < 0) return ret; From d9c23ac1217ab094e6bd9ab95e159ec72c8baca5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 21 Oct 2026 12:17:45 +0200 Subject: [PATCH 07/45] ASoC: samsung: midas_wm1811: run FLL1 at the AIF1 configured rate when idle When the codec is brought up without an active stream, for example because the mixer state restored by alsactl powers up a digital path, midas_start_fll1() sets FLL1 to the default 11.2896MHz rate. The codec's AIF1 however keeps the configuration of the last stream, or its reset default of 48kHz at 256fs before the first stream, which needs a 12.288MHz AIF1 clock. The digital paths then run with AIF1 clocked at a rate not matching its configuration. Remember the FLL1 rate requested by the last hw_params() call, which matches the AIF1 configuration kept in the codec, and use it when no specific rate is requested. Change the default rate used before the first stream to 12.288MHz, matching the codec's default AIF1 configuration. Fixes: fd0ea9cd9698 ("ASoC: samsung: Add sound support for Midas boards") Signed-off-by: Marek Szyprowski Link: https://patch.msgid.link/20261021101750.3297770-5-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index d43af5069baf..11466dd3539e 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -25,7 +25,8 @@ */ #define MCLK1_RATE 24000000U #define MCLK2_RATE 32768U -#define DEFAULT_FLL1_RATE 11289600U +/* Matches the codec's default AIF1 configuration: 48kHz, 256fs */ +#define DEFAULT_FLL1_RATE 12288000U struct midas_priv { struct gpio_desc *gpio_fm_sel; @@ -34,6 +35,7 @@ struct midas_priv { struct gpio_desc *gpio_headset_key; struct iio_channel *adc_headset_detect; unsigned int fll1_rate; + unsigned int aif1clk_rate; struct snd_soc_jack headset_jack; }; @@ -171,11 +173,12 @@ static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) if (!rate) rate = priv->fll1_rate; /* - * If no new rate is requested, set FLL1 to a sane default for jack - * detection. + * If no new rate is requested, set FLL1 to the rate the codec's AIF1 + * is currently configured for, as its digital paths can be powered + * up without an active stream. */ if (!rate) - rate = DEFAULT_FLL1_RATE; + rate = priv->aif1clk_rate ?: DEFAULT_FLL1_RATE; if (rate != priv->fll1_rate && priv->fll1_rate) { /* while reconfiguring, switch to MCLK2 for SYSCLK */ @@ -241,6 +244,7 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct midas_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned int pll_out; /* AIF1CLK should be at least 3MHz for "optimal performance" */ @@ -249,6 +253,8 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream, else pll_out = params_rate(params) * 256; + priv->aif1clk_rate = pll_out; + return midas_start_fll1(rtd, pll_out); } From 8731e33a3b98ea1ccade059b3a1ce06b7dd66b17 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 22 Oct 2026 12:15:28 +0200 Subject: [PATCH 08/45] ASoC: wm8994: follow AIF clock source changes in MCLK gating The AIF clock source can be changed by configure_aif_clock() while the AIF clock is running, for example the Midas machine driver switches AIF1 between MCLK2 and FLL1 while AIF1CLK is kept enabled for jack detection. aif_mclk_set() only enables the MCLK selected at the time the AIF clock is turned on, so an MCLK selected later this way is never enabled, and the codec may run from a stopped clock. Track whether the AIF clock is running and, if so, enable the newly selected MCLK in configure_aif_clock() before switching to it, then release the previously enabled one. Fixes: 001b83d395ab ("ASoC: wm8994: Add support for MCLKn clock gating") Signed-off-by: Marek Szyprowski Link: https://patch.msgid.link/20261022101531.775891-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 27 +++++++++++++++++++++++++++ sound/soc/codecs/wm8994.h | 1 + 2 files changed, 28 insertions(+) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 8adb3de546dd..6b91e02f130b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -163,9 +163,12 @@ static void wm8958_micd_set_rate(struct snd_soc_component *component) static int configure_aif_clock(struct snd_soc_component *component, int aif) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct clk *mclk = NULL; + bool switch_mclk; int rate; int reg1 = 0; int offset; + int ret; if (aif) offset = 4; @@ -174,11 +177,13 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) switch (wm8994->sysclk[aif]) { case WM8994_SYSCLK_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; rate = wm8994->mclk_rate[0]; break; case WM8994_SYSCLK_MCLK2: reg1 |= 0x8; + mclk = wm8994->mclk[WM8994_MCLK2].clk; rate = wm8994->mclk_rate[1]; break; @@ -196,6 +201,20 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) return -EINVAL; } + /* + * If the AIF clock is running, enable the new MCLK before switching + * to it and release the previous one afterwards. + */ + switch_mclk = wm8994->aif_clk_on[aif] && mclk != wm8994->aif_mclk[aif]; + if (switch_mclk) { + ret = clk_prepare_enable(mclk); + if (ret < 0) { + dev_err(component->dev, + "Failed to enable MCLK for AIF%d\n", aif + 1); + return ret; + } + } + if (rate >= 13500000) { rate /= 2; reg1 |= WM8994_AIF1CLK_DIV; @@ -210,6 +229,11 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, reg1); + if (switch_mclk) { + clk_disable_unprepare(wm8994->aif_mclk[aif]); + wm8994->aif_mclk[aif] = mclk; + } + return 0; } @@ -1067,6 +1091,7 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl if (!enable) { clk_disable_unprepare(wm8994->aif_mclk[aif]); wm8994->aif_mclk[aif] = NULL; + wm8994->aif_clk_on[aif] = false; return 0; } @@ -1086,6 +1111,7 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl clk_idx = WM8994_MCLK2; break; default: + wm8994->aif_clk_on[aif] = true; return 0; } @@ -1096,6 +1122,7 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl return ret; } wm8994->aif_mclk[aif] = wm8994->mclk[clk_idx].clk; + wm8994->aif_clk_on[aif] = true; return 0; } diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 5b22cfab317e..e7df0a1a2314 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -82,6 +82,7 @@ struct wm8994_priv { struct wm8994 *wm8994; struct clk_bulk_data mclk[WM8994_NUM_MCLK]; struct clk *aif_mclk[2]; + bool aif_clk_on[2]; int sysclk[2]; int sysclk_rate[2]; int mclk_rate[2]; From ad45d5369c566d1ae332fb52e8d9ffabe85465fc Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 21 Oct 2026 17:09:29 +0800 Subject: [PATCH 09/45] ASoC: rt722: enable mono speaker on Dell SKU 0EBA Dell SKU 0EBA uses only the left audio channel and a single speaker. Add the corresponding settings to support this configuration. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20261021090929.2135891-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 18 ++++++++++++++++++ sound/soc/codecs/rt722-sdca.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 149cb6617126..1dad8012d904 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1652,6 +1652,17 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); } +static const struct dmi_system_id dmi_dell_mono_spk_quirk_table[] = { + { + .ident = "Dell Pro 5 Micro P5M1270", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0EBA"), + } + }, + {} +}; + static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) { unsigned int mic_func_status; @@ -1741,6 +1752,13 @@ static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) if (rt722->hw_vid == RT722_VB) regmap_write(rt722->regmap, 0x2f54, 0x00); + /* This Dell machine only uses the mono speaker */ + if (dmi_check_system(dmi_dell_mono_spk_quirk_table)) { + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL2, 0x391a); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL2, 0x0010, 0x0010); + } + /* clear flag */ regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index b654b14334d4..dd245ae9bd44 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -68,9 +68,11 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_ANALOG_BIAS_CTL3 0x04 #define RT722_MISC_CTRL1 0x07 #define RT722_JD_CTRL1 0x09 +#define RT722_MISC_CTRL2 0x0d #define RT722_LDO2_3_CTL1 0x0e #define RT722_LDO1_CTL 0x1a #define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL2 0x38 #define RT722_CLSD_CTRL6 0x3c #define RT722_COMBO_JACK_AUTO_CTL1 0x45 #define RT722_COMBO_JACK_AUTO_CTL2 0x46 From 8cf9607d2d855629260fc78e6b54ea7a98525708 Mon Sep 17 00:00:00 2001 From: Alex Kazinsky Date: Fri, 23 Oct 2026 17:08:52 +0400 Subject: [PATCH 10/45] ASoC: tlv320aic32x4: Prevent division by zero in divider callbacks A zero rate requested through clk_set_rate() or clk_round_rate() reaches clk_aic32x4_div_determine_rate() when the minimum rate is zero. Clamping the requested rate to the allowed range leaves it unchanged, causing division by zero when calculating the divisor. A zero parent rate can also cause division by zero in determine_rate(). For example, if reading a parent divider's register fails, clk_aic32x4_div_recalc_rate() returns zero, which the clock framework caches as the parent's rate. A subsequent rate request for a child divider receives this value in best_parent_rate. With a nonzero requested rate, the calculated divisor is zero. It passes the existing upper-bound check and causes division by zero in the resulting rate calculation. The set_rate callback can receive a zero rate when an ancestor's rate changes. clk_calc_subtree() calculates descendant rates through recalc_rate() without calling their determine_rate callbacks. If reading a descendant divider's register fails, clk_aic32x4_div_recalc_rate() returns zero. This becomes the descendant's new_rate, which clk_change_rate() passes to clk_aic32x4_div_set_rate(), causing division by zero. Reject zero rates in both callbacks and reject a zero divisor in clk_aic32x4_div_determine_rate(). Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a51b50062091 ("ASoC: tlv320aic32x4: Model DAC/ADC dividers in CCF") Cc: stable@vger.kernel.org Signed-off-by: Alex Kazinsky Link: https://patch.msgid.link/20261023130854.93639-1-a.kazinsky@ideco.ru Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4-clk.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index ad15ce9c6079..27d186c72315 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -318,6 +318,9 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_aic32x4 *div = to_clk_aic32x4(hw); u8 divisor; + if (!rate) + return -EINVAL; + divisor = DIV_ROUND_UP(parent_rate, rate); if (divisor > AIC32X4_DIV_MAX) return -EINVAL; @@ -331,8 +334,11 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, { unsigned long divisor; + if (!req->rate) + return -EINVAL; + divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate); - if (divisor > AIC32X4_DIV_MAX) + if (!divisor || divisor > AIC32X4_DIV_MAX) return -EINVAL; req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor); From e0b17f84f6e3fa1201b8e2857094023c42ce5636 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 16 Oct 2026 15:50:47 +0700 Subject: [PATCH 11/45] ASoC: amd: raven: Fix memory leak in acp3x_dma_close() acp3x_dma_open() allocates a struct i2s_stream_instance for each substream, but acp3x_dma_close() never frees it, leaking it on every open/close cycle. Free it in acp3x_dma_close(). Fixes: 9f7041b71a2f ("ASoC: amd: closing specific instance.") Reported-by: sashiko-reviews Closes: https://lore.kernel.org/all/sashiko-outbox-173760@kernel.org/ Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20261016085047.535265-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 765057499021..0862a431b2d1 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -360,6 +360,8 @@ static int acp3x_dma_close(struct snd_soc_component *component, } } + kfree(ins); + return 0; } From bcb2efda7abf56f212133044f135d88f68796fff Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Thu, 22 Oct 2026 10:53:09 +0800 Subject: [PATCH 12/45] ASoC: codecs: es8375: Modify Register Configuration Modify the Initialization Configuration and Clock Table Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261022025309.24028-1-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8375.c | 136 +++++++++++++++++++------------------- sound/soc/codecs/es8375.h | 3 + 2 files changed, 72 insertions(+), 67 deletions(-) diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index bed872eafc95..501a3eda5a89 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -199,68 +199,68 @@ struct _coeff_div { static const struct _coeff_div coeff_div[] = { {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, - {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, + {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {32, 1536000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, - {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, - {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, - {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, - {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, - {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2}, - {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, - {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, - {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, - {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, - {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, - {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2}, - {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, - {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, - {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2}, - {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, - {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2}, - {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, - {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, - {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2}, - {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, - {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, - {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2}, - {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, - {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, - {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, - {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, - {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, - {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, - {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, - {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, - {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2}, - {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2}, - {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2}, - {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2}, - {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2}, + {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {36, 1728000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {48, 2304000, 48000, 0x05, 0x91, 0x53, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {50, 2400000, 48000, 0x05, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {64, 3072000, 48000, 0x05, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {72, 3456000, 48000, 0x05, 0x91, 0x53, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {96, 4608000, 48000, 0x15, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {100, 4800000, 48000, 0x03, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {128, 6144000, 48000, 0x03, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {144, 6912000, 48000, 0x03, 0x80, 0x31, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {192, 9216000, 48000, 0x15, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {250, 12000000, 48000, 0x25, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {256, 12288000, 48000, 0x0B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {384, 18432000, 48000, 0x13, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {400, 19200000, 48000, 0x1B, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {500, 24000000, 48000, 0x23, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {512, 24576000, 48000, 0x1B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2}, + {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2}, + {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2}, + {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2}, + {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2}, + {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2}, + {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x1F, 0x1F, 2, 2}, + {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x23, 0x1F, 2, 2}, + {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x25, 0x27, 2, 2}, + {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x2F, 0x1F, 2, 2}, + {32, 3072000, 96000, 0x05, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {64, 6144000, 96000, 0x03, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {96, 9216000, 96000, 0x15, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {128, 12288000, 96000, 0x0B, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x01, 0x37, 2, 2}, }; static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) @@ -543,18 +543,20 @@ static void es8375_init(struct snd_soc_component *component) regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02); regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05); regmap_write(es8375->regmap, ES8375_CSM1, 0x82); - regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20); - regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20); - regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28); - regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x10); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x10); + regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x2F); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xEC); regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0); regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE); regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8); + regmap_write(es8375->regmap, ES8375_VSEL2, 0x05); + regmap_write(es8375->regmap, ES8375_LP_DAC, 0xCC); regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03); regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16); regmap_write(es8375->regmap, ES8375_RESET1, 0x00); msleep(80); - regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00); + regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x08); regmap_write(es8375->regmap, ES8375_CSM1, 0x86); regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B); regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00); @@ -566,7 +568,7 @@ static void es8375_init(struct snd_soc_component *component) regmap_write(es8375->regmap, ES8375_ADC2, 0x00); regmap_write(es8375->regmap, ES8375_DAC2, 0x00); regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88); - regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE3); regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0); regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40); regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE); diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h index 11e3ceec9b68..b48d03b1f689 100644 --- a/sound/soc/codecs/es8375.h +++ b/sound/soc/codecs/es8375.h @@ -49,6 +49,9 @@ #define ES8375_ANALOG_SPK2 0x29 #define ES8375_VMID_SEL 0x2D #define ES8375_ANALOG1 0x2E +#define ES8375_VSEL1 0x2F +#define ES8375_VSEL2 0x30 +#define ES8375_LP_DAC 0x31 #define ES8375_ANALOG2 0x32 #define ES8375_ANALOG3 0x37 #define ES8375_ADC2DAC_CLKTRI 0xF8 From cc3fc99f1bce93c3ae3771ef98cb91a566da857e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 19 Oct 2026 19:47:24 -0300 Subject: [PATCH 13/45] ASoC: fsl_utils: Tolerate fractional PLL rate residue fsl_asoc_constrain_rates() admits a sample rate only when an audio PLL divides it exactly. On i.MX8M Plus, fractional audio PLLs may read a few hertz off their nominal rate after clk_set_rate(). For example, audio_pll1 can read 393215997 Hz after restoring 393216000 Hz. This residue can make the helper exclude a valid sample-rate family, causing a 48 kHz stream to fail or be resampled after PLL trimming. Accept remainders within 100 Hz of either end of the divisor range. The other rate family differs by tens of kilohertz, so the tolerance does not make the families overlap. Observed on a custom i.MX8MP-based board: after 44.1 kHz playback and PLL trimming, subsequent 48 kHz playback was silently resampled. Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20261019224724.236388-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_utils.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 7651c64bc837..74c6ebc0e3d9 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -153,6 +153,9 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, } EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); +/* Fractional audio PLLs may sit a few Hz off nominal */ +#define FSL_RATE_CONSTR_TOL_HZ 100 + /** * fsl_asoc_constrain_rates - constrain rates according to clocks * @@ -172,6 +175,7 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, { int i, j, k = 0; u64 clk_rate[3]; + u32 rate, rem; *target_constr = *original_constr; if (pll8k_clk || pll11k_clk || ext_clk) { @@ -181,10 +185,25 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, clk_rate[0] = clk_get_rate(pll8k_clk); clk_rate[1] = clk_get_rate(pll11k_clk); clk_rate[2] = clk_get_rate(ext_clk); + rate = original_constr->list[i]; for (j = 0; j < 3; j++) { - if (clk_rate[j] != 0 && - do_div(clk_rate[j], original_constr->list[i]) == 0) { - target_rates[k++] = original_constr->list[i]; + if (clk_rate[j] == 0) + continue; + rem = do_div(clk_rate[j], rate); + /* + * The audio PLLs are fractional and cannot + * always reproduce their nominal rate: after + * any clk_set_rate() they may sit a few Hz + * off (observed on i.MX8M Plus: audio_pll1 + * reads 393215997 after restoring 393216000), + * which must not disqualify the entire rate + * family. Accept near-integer ratios; the + * other family misses by tens of kHz, so a + * small tolerance cannot mix families. + */ + if (rem <= FSL_RATE_CONSTR_TOL_HZ || + rem >= rate - FSL_RATE_CONSTR_TOL_HZ) { + target_rates[k++] = rate; target_constr->count++; break; } From 9080b18ad561a495bf74926cbc52e23cae8887c0 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 26 Oct 2026 19:21:37 +0800 Subject: [PATCH 14/45] ASoC: amd: yc: Add DMI quirk for HP Laptop 14-em0xxx Add a DMI quirk for the HP Laptop 14-em0xxx fixing the issue where the internal microphone was not detected. Tested-by: Juan Benedict Tanoto Closes: https://bugzilla.kernel.org/show_bug.cgi?id=222184 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20261026112137.257723-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 41f8c0885865..ae26524453e8 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -759,6 +759,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8BD6"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8DC2"), + } + }, { .driver_data = &acp6x_card, .matches = { From f3c7bee984aa0ba2b48a44e16a6f3ec2c27f851e Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 16 Oct 2026 03:29:17 -0300 Subject: [PATCH 15/45] ASoC: qcom: qdsp6: q6afe: fix clk vote response type mismatch The response sent by the firmware when requesting a clock vote (opcode AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST) does not actually have the same opcode + status payload as APR_BASIC_RSP_RESULT. Rather, it returns one single u32 which is the client_handle that must be used in future unvote requests for the same clock. As a result of this type confusion, the status returned by the callback to q6afe_vote_lpass_core_hw was actually an out-of-bounds read. It was only interpreted as success (0) most of the time due to luck, but there are some reports of random errors such as: [ 20.961100] qcom-q6afe aprsvc:service:4:4: AFE failed to vote (3) [ 20.961131] Failed to prepare clk 'core': -110 Fix by correctly interpreting the response as a single u32, and actually store it as the client_handle to ensure unvote would work correctly. Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/5976946.DvuYhMxLoT@antlia/ Fixes: 55e07531d922 ("ASoC: q6dsp: q6afe: add lpass hw voting support") Reviewed-by: Srinivas Kandagatla Signed-off-by: Val Packett Link: https://patch.msgid.link/20261016063536.598610-1-val@packett.cool Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index f0ff1350e9dd..9ac51c304ead 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -393,6 +393,7 @@ struct q6afe { struct q6core_svc_api_info ainfo; struct mutex lock; struct aprv2_ibasic_rsp_result_t result; + uint32_t vote_result; wait_queue_head_t wait; struct list_head port_list; spinlock_t port_list_lock; @@ -1010,13 +1011,14 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da const struct aprv2_ibasic_rsp_result_t *res; const struct apr_hdr *hdr = &data->hdr; struct q6afe_port *port; + uint32_t *vote_res; if (!data->payload_size) return 0; - res = data->payload; switch (hdr->opcode) { case APR_BASIC_RSP_RESULT: { + res = data->payload; if (res->status) { dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n", res->opcode, res->status); @@ -1043,8 +1045,10 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da } break; case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + vote_res = data->payload; afe->result.opcode = hdr->opcode; - afe->result.status = res->status; + afe->result.status = 0; + afe->vote_result = *vote_res; wake_up(&afe->wait); break; default: @@ -1955,6 +1959,8 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST); if (ret) dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id); + else + *client_handle = afe->vote_result; return ret; } From 4b832a2af523cbf857a977414c0f07d7f90a4c9d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:42 +0800 Subject: [PATCH 16/45] ASoC: qcom: q6apm: Fix unmap regions response handling The DSP acknowledges APM_CMD_SHARED_MEM_UNMAP_REGIONS with a GPR_BASIC_RSP_RESULT packet whose payload carries the acknowledged opcode and the status. apm_callback() stores hdr->opcode, i.e. GPR_BASIC_RSP_RESULT itself, as the result opcode, so audioreach_send_cmd_sync(), which waits for 0x0100100d, never sees a match and gives up after five seconds even though the DSP has already processed the command: qcom-apm gprsvc:service:2:1: CMD timeout for [100100d] opcode Each mapped graph costs a five second stall on unmap, and the forced status of 0 also hides real DSP failures from the caller. Record the opcode and status from the response payload, like the other basic-result commands do, and only drop the cached mapping handle when the DSP reports success. Fixes: 8ea6e25c8536 ("ASoC: qcom: q6apm: Add support for early buffer mapping on DSP") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index b6b1ef6833b3..fa7f5dfca418 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -960,17 +960,16 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) wake_up(&apm->wait); break; case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; - rsp = data->payload; - - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info) - info->mem_map_handle = 0; - else + apm->result.opcode = result->opcode; + apm->result.status = result->status; + if (result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info && !result->status) + info->mem_map_handle = 0; + wake_up(&apm->wait); break; default: From 43c69604271d8b54c494fde70bd4a9eaab7a974d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:43 +0800 Subject: [PATCH 17/45] ASoC: qcom: q6apm: Clear the right handle on pos buffer unmap Push/pull graphs map two shared-memory regions per graph, the circular buffer and the position buffer, and keep a separate DSP handle for each. The map path tags its token with APM_MMAP_TOKEN_MAP_TYPE_POS_BUF so the response lands in the right handle, but the unmap path sends a bare graph ID and the response handler always clears mem_map_handle. Unmapping the position buffer therefore wipes the circular buffer's handle and leaves pos_buf_mem_map_handle set. A subsequent unmap of the circular buffer is skipped, leaking that DSP mapping, and a remap of the position buffer is skipped because the stale handle looks valid. Tag the unmap token the same way as the map token and decode it in the response handler to clear the matching handle. Fixes: 7c1ac23b178a ("ASoC: qcom: audioreach: Add support for shared memory push/pull modules") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-3-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index fa7f5dfca418..0e5219a6513f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -329,8 +329,9 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; + uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), - APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + APM_CMD_SHARED_MEM_UNMAP_REGIONS, (graph_id | pos_mask)); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -966,9 +967,16 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info && !result->status) - info->mem_map_handle = 0; + graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF; + + info = idr_find(&apm->graph_info_idr, graph_id); + if (info && !result->status) { + if (is_pos_buf) + info->pos_buf_mem_map_handle = 0; + else + info->mem_map_handle = 0; + } wake_up(&apm->wait); break; From a51c5dd7f7fc7cdf4ae28fec4a013d8418d1cb53 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:44 +0800 Subject: [PATCH 18/45] ASoC: qcom: q6apm: Unmap DSP buffers before removing the topology Since buffers are mapped once in pcm_new() and unmapped in pcm_free(), the unmap relies on the graph info still being registered. During card teardown, however, soc_cleanup_card_resources() removes the components before it frees the PCM runtimes. q6apm-audio's remove callback unloads the topology, which frees every audioreach_graph_info, so by the time q6apm_dai_pcm_free() runs the idr lookup fails and the unmap silently returns -ENODEV. The DSP keeps the old mapping. When the card is bound again, for example after unbinding and binding any codec on it, pcm_new() maps the same buffer once more, the DSP rejects it and the front-end PCM is not created: qcom-apm gprsvc:service:2:1: Error (1) Processing 0x0100100c cmd qcom-apm gprsvc:service:2:1: DSP returned error[100100c] 1 q6apm-dai ...: Audio Start: Buffer Allocation failed rc = -22 snd-sc8280xp sound: ASoC: can't create pcm MultiMedia1 Playback :-22 Release the circular and position buffer mappings of all graphs in q6apm_audio_remove() while the graph info still exists. The unmap helper now takes the q6apm instance directly, since it is called from the APM component rather than from a q6apm-dai child device. Fixes: 8ea6e25c8536 ("ASoC: qcom: q6apm: Add support for early buffer mapping on DSP") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-4-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 0e5219a6513f..bed3b64e8310 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -322,11 +322,10 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr } EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id, +static int __q6apm_unmap_memory_fixed_region(struct q6apm *apm, unsigned int graph_id, bool is_pos_buf) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; @@ -358,13 +357,15 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, false); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, false); } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, true); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, true); } EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer); @@ -851,6 +852,21 @@ static int q6apm_audio_probe(struct snd_soc_component *component) static void q6apm_audio_remove(struct snd_soc_component *component) { + struct q6apm *apm = dev_get_drvdata(component->dev); + struct audioreach_graph_info *info; + int id; + + /* + * The card core removes components before freeing the PCMs, so + * q6apm-dai's pcm_free() runs after the topology (and graph_info) + * is gone and can no longer unmap. Release the DSP mappings here, + * otherwise the next pcm_new() fails to map the same buffer again. + */ + idr_for_each_entry(&apm->graph_info_idr, info, id) { + __q6apm_unmap_memory_fixed_region(apm, id, false); + __q6apm_unmap_memory_fixed_region(apm, id, true); + } + /* remove topology */ snd_soc_tplg_component_remove(component); } From d25ac9e30cab579c1c99b8f541e092b40ee53012 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 2 Oct 2026 09:15:31 +0800 Subject: [PATCH 19/45] ASoC: soc-acpi-intel-arl-match: add rt712 on link 0 config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add rt712 on link 0 configuration on ARL platform. Export all supported endpoints and the endpoint will be skipped if it is not listed in the SDCA DisCo table. Signed-off-by: Bard Liao Tested-by: Patrick Daether Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20261002011532.3468261-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../soc/intel/common/soc-acpi-intel-arl-match.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 1033f2d45c8a..5a088005b221 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -482,6 +482,15 @@ static const struct snd_soc_acpi_link_adr arl_rt712_l0_rt1320_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr arl_rt712_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt712_0_agg_adr), + .adr_d = rt712_0_agg_adr, + }, + {} +}; + static const struct snd_soc_acpi_codecs arl_essx_83x6 = { .num_codecs = 3, .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, @@ -589,6 +598,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", .get_function_tplg_files = sof_sdw_get_tplg_files, }, + { + .link_mask = BIT(0), + .links = arl_rt712_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-dummy.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, { .link_mask = BIT(0), .links = arl_cs42l43_l0, From 51dc835b59caca983ed27065233be4b81cd8ac14 Mon Sep 17 00:00:00 2001 From: JJ Gault Date: Fri, 2 Oct 2026 09:15:32 +0800 Subject: [PATCH 20/45] ASoC: Intel: soc-acpi-intel-arl-match: Add RT713 + dual RT1320 support Add a SoundWire machine-driver match entry for boards with an RT713 (SDCA "VB" jack/dmic variant) on link 0 and two RT1320 speaker amps sharing link 2 (distinguished by unique_id 0/1, left/right). This exact codec pairing is already supported on Panther Lake (ptl_sdw_rt713_vb_l2_rt1320_l13) and Lunar Lake (lnl_sdw_rt713_vb_l2_rt1320_l13); Arrow Lake was missing the corresponding entry, so affected boards fell back to the generic skl_hda_dsp_generic driver and only exposed HDMI outputs. Confirmed on an MSI Raider 16 Max HX B2WI (MS-2651): the kernel now correctly identifies the hardware and no longer falls back to the generic HDA driver. No dedicated topology has been built for this link0/link2 layout yet, so sof_tplg_filename is set to a placeholder ("sof-arl-dummy.tplg") rather than pointing at an existing but unrelated topology, per review feedback. Signed-off-by: JJ Gault Signed-off-by: Bard Liao Link: https://patch.msgid.link/20261002011532.3468261-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-arl-match.c | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 5a088005b221..7cb5100cef7f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -70,6 +70,23 @@ static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* DMIC Endpoint */ + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device cs35l56_2_lr_adr[] = { { .adr = 0x00023001FA355601ull, @@ -301,6 +318,35 @@ static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = { } }; +/* + * MSI Raider 16 Max HX B2WI (MS-2651): RT713 (jack/dmic, "VB" SDCA variant) + * on link 0, two RT1320 speaker amps sharing link 2 (unique_id 0 = left, + * unique_id 1 = right). + */ +static const struct snd_soc_acpi_adr_device rt713_vb_0_adr[] = { + { + .adr = 0x000030025D071301ull, + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt713" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_2_lr_adr[] = { + { + .adr = 0x000230025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1320-1" + }, + { + .adr = 0x000231025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + static const struct snd_soc_acpi_link_adr arl_n_mrd_es9356_link1[] = { { .mask = BIT(1), @@ -454,6 +500,20 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr arl_rt713_vb_l0_rt1320_l2[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt713_vb_0_adr), + .adr_d = rt713_vb_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_lr_adr), + .adr_d = rt1320_2_lr_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = { { .mask = BIT(0), @@ -569,6 +629,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg", .get_function_tplg_files = sof_sdw_get_tplg_files, }, + { + .link_mask = BIT(0) | BIT(2), + .links = arl_rt713_vb_l0_rt1320_l2, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-arl-dummy.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, { .link_mask = BIT(0) | BIT(2), .links = arl_rt722_l0_rt1320_l2, From e21fb64a8c4aecae31ec63f994423ac944705c3e Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 23 Oct 2026 18:13:10 +0100 Subject: [PATCH 21/45] ASoC: qcom: audioreach: fix module cfg blob size handling audioreach_get_module_priv_data() passes mod_array->size, a byte count, as the element count of the __le32 data[] flex array. The allocation and the copy are four times too large, and the memcpy() can read past the end of the topology firmware buffer. The size is also never checked against the private data it must lie within, and a zero size makes the loop spin forever. Convert the size to a word count, bound the blob by the remaining private data and reject a zero size. Also check the returned pointer before storing it in mod->data, as an error pointer there is dereferenced when the graph is opened. Fixes: c7ed4c2debfd ("ASoC: qcom: audioreach: add support for static calibration") Cc: stable@vger.kernel.org Signed-off-by: Ravi Hothi Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index faafbc5c0ad0..612d8f03e6e9 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -312,22 +312,37 @@ audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private) for (sz = 0; sz < le32_to_cpu(private->size); ) { const struct snd_soc_tplg_vendor_array *mod_array; + u32 array_size; mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); + array_size = le32_to_cpu(mod_array->size); + if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) { struct audioreach_module_priv_data *pdata; + u32 nwords; - pdata = kzalloc_flex(*pdata, data, - le32_to_cpu(mod_array->size)); + /* size is the byte count of the __le32 data[] */ + nwords = array_size / sizeof(__le32); + + /* the blob must lie within the private data */ + if (sizeof(*pdata) + (u64)array_size > + le32_to_cpu(private->size) - sz) + return ERR_PTR(-EINVAL); + + pdata = kzalloc_flex(*pdata, data, nwords); if (!pdata) return ERR_PTR(-ENOMEM); - memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data, - le32_to_cpu(mod_array->size))); + memcpy(pdata, ((u8 *)private->data + sz), + struct_size(pdata, data, nwords)); return pdata; } - sz = sz + le32_to_cpu(mod_array->size); + /* a zero size would never advance the walk */ + if (!array_size) + return ERR_PTR(-EINVAL); + + sz = sz + array_size; } return NULL; @@ -611,6 +626,12 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon return mod ? PTR_ERR(mod) : -ENODEV; mod->data = audioreach_get_module_priv_data(&tplg_w->priv); + if (IS_ERR(mod->data)) { + int rc = PTR_ERR(mod->data); + + mod->data = NULL; + return rc; + } dobj = &w->dobj; dobj->private = mod; From 00535515e3c14fbee20de4a1aa61cfb3390f0f50 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 23 Oct 2026 18:13:11 +0100 Subject: [PATCH 22/45] ASoC: codecs: wsa884x: Accept temperatures across the full trim range wsa884x_get_temp() rejects any reading outside 5..45 degC with -EAGAIN. A speaker that has been playing, or sits in a warm laptop, easily runs above 45 degC, so its temperature becomes unreadable when it matters. The check only needs to drop bogus conversions, such as the all-zero reading before the PA is first enabled. Bound it by the -10..150 degC two-point trim range instead. Fixes: 6b99dc62d940 ("ASoC: codecs: wsa884x: Implement temperature reading and hwmon") Cc: stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index a367c94bfb4d..f0857c0588d5 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -720,10 +720,11 @@ /* * Device will report senseless data in many cases, so discard any measurements - * outside of valid range. + * outside of the two-point trim range. Do not narrow this to ambient: a + * speaker that has been playing easily runs well above 45 degC. */ -#define WSA884X_LOW_TEMP_THRESHOLD 5 -#define WSA884X_HIGH_TEMP_THRESHOLD 45 +#define WSA884X_LOW_TEMP_THRESHOLD WSA884X_T1_TEMP +#define WSA884X_HIGH_TEMP_THRESHOLD WSA884X_T2_TEMP struct wsa884x_priv { struct regmap *regmap; @@ -1929,8 +1930,8 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n", val, dmeas, d1, d2); - if ((val > WSA884X_LOW_TEMP_THRESHOLD) && - (val < WSA884X_HIGH_TEMP_THRESHOLD)) { + if (val >= WSA884X_LOW_TEMP_THRESHOLD && + val <= WSA884X_HIGH_TEMP_THRESHOLD) { wsa884x->temperature = val; *temp = val * 1000; ret = 0; From 6a55cf0d9dd2e2493a5030a4f7b0413d31165e45 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 23 Oct 2026 18:13:12 +0100 Subject: [PATCH 23/45] ASoC: codecs: wsa883x: Accept temperatures across the full trim range wsa883x_get_temp() rejects any reading outside 5..45 degC with -EAGAIN, so a speaker that has been playing, or sits in a warm chassis, becomes unreadable above 45 degC. Bound the check by the -10..150 degC two-point trim range instead, and use inclusive comparisons, as in_range() excludes the upper bound. Fixes: a02c42d41af7 ("ASoC: codecs: wsa883x: Implement temperature reading and hwmon") Cc: stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 442cf9f3eaac..ccd4404c1644 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -456,10 +456,11 @@ /* * Device will report senseless data in many cases, so discard any measurements - * outside of valid range. + * outside of the two-point trim range. Do not narrow this to ambient: a + * speaker that has been playing easily runs well above 45 degC. */ -#define WSA883X_LOW_TEMP_THRESHOLD 5 -#define WSA883X_HIGH_TEMP_THRESHOLD 45 +#define WSA883X_LOW_TEMP_THRESHOLD WSA883X_T1_TEMP +#define WSA883X_HIGH_TEMP_THRESHOLD WSA883X_T2_TEMP struct wsa883x_priv { struct regmap *regmap; @@ -1430,7 +1431,7 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) unsigned int dmeas_msb = 0, dmeas_lsb = 0; int d1, d2, dmeas; unsigned int mask; - int ret, range; + int ret; long val; guard(mutex)(&wsa883x->sp_lock); @@ -1493,8 +1494,8 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) } val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1)); - range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD; - if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) { + if (val >= WSA883X_LOW_TEMP_THRESHOLD && + val <= WSA883X_HIGH_TEMP_THRESHOLD) { wsa883x->temperature = val; *temp = val * 1000; ret = 0; From 54c121eaeed8ee7751935db8100b6b5c57124db9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 23 Oct 2026 18:13:13 +0100 Subject: [PATCH 24/45] ASoC: codecs: wsa884x: disable runtime PM on unbind Probe enables runtime PM but nothing disables it on unbind. The pending autosuspend then runs wsa884x_runtime_suspend() after the devm-managed regmap is freed, and dereferences NULL: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020 pc : regcache_cache_only+0x18/0x80 lr : wsa884x_runtime_suspend+0x24/0x40 [snd_soc_wsa884x] Use devm_pm_runtime_enable() so runtime PM is disabled before the regmap is freed. Fixes: aa21a7d4f68a ("ASoC: codecs: wsa884x: Add WSA884x family of speakers") Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-5-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index f0857c0588d5..3c008224d32e 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -2125,7 +2125,9 @@ static int wsa884x_probe(struct sdw_slave *pdev, pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; return devm_snd_soc_register_component(dev, &wsa884x_component_drv, From ea63182d71956dc33c6f08dc4171f1052f8409a9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 23 Oct 2026 18:13:14 +0100 Subject: [PATCH 25/45] ASoC: codecs: wsa883x: disable runtime PM on unbind Probe enables runtime PM but nothing disables it on unbind, or when registering the component fails. A pending autosuspend can then run wsa883x_runtime_suspend() after the devm-managed regmap is freed, and dereference NULL, as seen on wsa884x. Use devm_pm_runtime_enable() so runtime PM is disabled before the regmap is freed. Fixes: 43b8c7dc85a1 ("ASoC: codecs: add wsa883x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-6-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index ccd4404c1644..b09ee3db7ed5 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1669,7 +1669,9 @@ static int wsa883x_probe(struct sdw_slave *pdev, pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto err; ret = devm_snd_soc_register_component(dev, &wsa883x_component_drv, From a6093f98cbfc4a717ef3d1048b0e38fb2035be98 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 23 Oct 2026 18:13:15 +0100 Subject: [PATCH 26/45] ASoC: codecs: wsa884x: Fix VI sense port channel count The VI sense port carries both the voltage and the current sense data of the speaker, but it is described with max_ch = 1 and, since commit 060fac202eb8 ("ASoC: codecs: wsa884x: Correct VI sense channel mask"), a channel mask of 0x1, so only half of the V/I pair is transferred. Describe the port as two channels and set the channel mask back to 0x3. Fixes: 060fac202eb8 ("ASoC: codecs: wsa884x: Correct VI sense channel mask") Cc: Krzysztof Kozlowski Signed-off-by: Ravi Hothi Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-7-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 3c008224d32e..acf32af5aae3 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -859,7 +859,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { .num = WSA884X_PORT_VISENSE + 1, .type = SDW_DPN_SIMPLE, .min_ch = 1, - .max_ch = 1, + .max_ch = 2, .simple_ch_prep_sm = true, .read_only_wordlength = true, }, @@ -892,7 +892,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { }, [WSA884X_PORT_VISENSE] = { .num = WSA884X_PORT_VISENSE + 1, - .ch_mask = 0x1, + .ch_mask = 0x3, }, [WSA884X_PORT_CPS] = { .num = WSA884X_PORT_CPS + 1, From 9728b328d7adf228d5f691d0ec43bb7a6d07e44b Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 23 Oct 2026 18:13:16 +0100 Subject: [PATCH 27/45] ASoC: codecs: wsa883x: Fix VI sense port channel count The VI sense port carries both the voltage and the current sense data of the speaker, but it is described with max_ch = 1 and, since commit ed3b274abc40 ("ASoC: codecs: wsa883x: Correct VI sense channel mask"), a channel mask of 0x1, so only half of the V/I pair is transferred. Describe the port as two channels and set the channel mask back to 0x3. Fixes: ed3b274abc40 ("ASoC: codecs: wsa883x: Correct VI sense channel mask") Cc: Krzysztof Kozlowski Signed-off-by: Ravi Hothi Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261023171316.819857-8-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index b09ee3db7ed5..17d8babc6123 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -552,7 +552,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = { .num = WSA883X_PORT_VISENSE + 1, .type = SDW_DPN_SIMPLE, .min_ch = 1, - .max_ch = 1, + .max_ch = 2, .simple_ch_prep_sm = true, .read_only_wordlength = true, } @@ -573,7 +573,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { }, [WSA883X_PORT_VISENSE] = { .num = WSA883X_PORT_VISENSE + 1, - .ch_mask = 0x1, + .ch_mask = 0x3, }, }; From 5532cec202507a3e3460bf12deafa0688009dab7 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 26 Oct 2026 14:42:49 +0800 Subject: [PATCH 28/45] ASoC: es9356-sdca: Modify the timing and configuration of headphone events Change the time it takes to create a workqueue from 280 ms to 50 ms to ensure faster response to button presses. Modify the settings related to buttons after plugging in the headset to ensure more accurate button type recognition. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261026064251.15017-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index b8b3ffc13fd5..4bb51dd8df07 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -352,7 +352,7 @@ static int es9356_sdca_headset_detect(struct es9356_sdw_priv *es9356) if (reg) { regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0), reg); - regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x75); + regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x74); } else { regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); } @@ -403,7 +403,7 @@ static void es9356_interrupt_handler(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4); mod_delayed_work(system_power_efficient_wq, - &es9356->button_detect_work, msecs_to_jiffies(280)); + &es9356->button_detect_work, msecs_to_jiffies(50)); } } @@ -450,7 +450,7 @@ static void es9356_button_detect_handler(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4); mod_delayed_work(system_power_efficient_wq, - &es9356->button_detect_work, msecs_to_jiffies(280)); + &es9356->button_detect_work, msecs_to_jiffies(50)); } return; @@ -1000,10 +1000,10 @@ static int es9356_sdw_interrupt_callback(struct sdw_slave *slave, count++; } while (stat != 0 && count < retry); - /* The 280 ms figure was determined through testing */ + /* The 50 ms figure was determined through testing */ if (status->sdca_cascade && !es9356->disable_irq) mod_delayed_work(system_power_efficient_wq, - &es9356->interrupt_handle_work, msecs_to_jiffies(280)); + &es9356->interrupt_handle_work, msecs_to_jiffies(50)); mutex_unlock(&es9356->disable_irq_lock); return 0; From ddc9472baf11727144a78bf5ad81ca596baf5d7c Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 26 Oct 2026 14:42:50 +0800 Subject: [PATCH 29/45] ASoC: es9356-sdca: Add writeable_reg Set ES9356_VERSION and the HID-related registers to read-only. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261026064251.15017-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 15 +++++++++++++++ sound/soc/codecs/es9356.h | 1 + 2 files changed, 16 insertions(+) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index 4bb51dd8df07..a22e92d1e5cc 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -724,10 +724,25 @@ static bool es9356_sdca_volatile_register(struct device *dev, unsigned int reg) } } +static bool es9356_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES9356_VERSION: + case ES9356_BUF_ADDR_HID: + case ES9356_HID_BYTE2: + case ES9356_HID_BYTE3: + case ES9356_HID_BYTE4: + return false; + default: + return true; + } +} + static const struct regmap_config es9356_sdca_regmap = { .reg_bits = 32, .val_bits = 16, .volatile_reg = es9356_sdca_volatile_register, + .writeable_reg = es9356_writeable_register, .max_register = 0x45ffffff, .cache_type = REGCACHE_MAPLE, .use_single_read = true, diff --git a/sound/soc/codecs/es9356.h b/sound/soc/codecs/es9356.h index 2a676f36590f..ceb571903008 100644 --- a/sound/soc/codecs/es9356.h +++ b/sound/soc/codecs/es9356.h @@ -4,6 +4,7 @@ #define __ES9356_H__ /*ES9356 Implementation-define*/ +#define ES9356_VERSION 0x2000 #define ES9356_FLAGS_HP 0x2003 #define ES9356_CSM_RESET 0x2020 #define ES9356_FUC_RESET 0x2021 From 6d7dbcb5f8e41ba63e1fefad902d1d5f51a3980b Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 26 Oct 2026 14:42:51 +0800 Subject: [PATCH 30/45] ASoC: es9356-sdca: Modify the Initialization Configuration Read the chip version number and modify the initialization configuration Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261026064251.15017-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 81 +++++++++++++++++++++++++++++++-------- sound/soc/codecs/es9356.h | 4 ++ 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index a22e92d1e5cc..9c70ccd5b310 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -749,58 +749,97 @@ static const struct regmap_config es9356_sdca_regmap = { .use_single_write = true, }; -static void es9356_register_init(struct es9356_sdw_priv *es9356) +static int es9356_register_init(struct es9356_sdw_priv *es9356) { + unsigned int version; + int ret; + + ret = regmap_read(es9356->regmap, ES9356_VERSION, &version); + if (ret < 0) { + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; + } + + regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x2c); + if (!(version >> 1)) + regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x11); + else + regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x00); + + regmap_write(es9356->regmap, ES9356_SPKCLK_CTL, 0xc3); regmap_write(es9356->regmap, ES9356_STATE, 0x02); regmap_write(es9356->regmap, ES9356_ENDPOINT_MODE, 0x24); regmap_write(es9356->regmap, ES9356_PRE_DIV_CTL, 0x00); regmap_write(es9356->regmap, ES9356_ADC_OSR, 0x18); - regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x13); + regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x11); regmap_write(es9356->regmap, ES9356_DAC_OSR, 0x16); regmap_write(es9356->regmap, ES9356_CLK_CTL, 0x0f); - regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01); - regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30); + if (!(version >> 1)) + regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30); + else + regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x10); + + regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01); regmap_write(es9356->regmap, ES9356_DETCLK_CTL, 0x51); - regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x10); + regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x00); regmap_write(es9356->regmap, ES9356_MICBIAS_CTL, 0x10); regmap_write(es9356->regmap, ES9356_HPDETECT_CTL, 0x07); regmap_write(es9356->regmap, ES9356_ADC_ANA, 0x30); - regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xa8); + regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xaa); regmap_write(es9356->regmap, ES9356_ADC_INT, 0xaa); regmap_write(es9356->regmap, ES9356_ADC_LP, 0x19); regmap_write(es9356->regmap, ES9356_VMID1SEL, 0xbc); regmap_write(es9356->regmap, ES9356_VMID_TIME, 0x0b); regmap_write(es9356->regmap, ES9356_STATE_TIME, 0xbb); - regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x77); + regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x7a); regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); - regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15); + + if ((version & GENMASK(1, 0)) == 0x03) + regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15); + else + regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x25); + + regmap_write(es9356->regmap, ES9356_SPARE_REGISTER3, 0x00); regmap_write(es9356->regmap, ES9356_KEY_PRESS_TIME, 0xff); regmap_write(es9356->regmap, ES9356_KEY_RELEASE_TIME, 0xff); regmap_write(es9356->regmap, ES9356_KEY_HOLD_TIME, 0x0f); - regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00); + + if ((version & GENMASK(1, 0)) == 0x03) + regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x04); + else + regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00); + regmap_write(es9356->regmap, ES9356_KEYD_DETECT, 0x18); regmap_write(es9356->regmap, ES9356_MICBIAS_RES, 0x03); - regmap_write(es9356->regmap, ES9356_BUTTON_CHARGE, 0x00); - regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x13); - regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4); + regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x1a); + + if (!(version >> 1)) { + regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xc3); + } else { + regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4); + regmap_write(es9356->regmap, ES9356_SPK_SCP, 0x55); + } regmap_write(es9356->regmap, ES9356_SPK_VOLUME, 0x33); regmap_write(es9356->regmap, ES9356_DAC_VROI, 0x01); regmap_write(es9356->regmap, ES9356_DAC_LP, 0x00); regmap_write(es9356->regmap, ES9356_HP_IBIAS, 0x04); regmap_write(es9356->regmap, ES9356_HP_LP, 0x03); - regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x65); + regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x6c); regmap_write(es9356->regmap, ES9356_SPKBIAS_COMP, 0x09); - regmap_write(es9356->regmap, ES9356_VMID1STL, 0x00); + regmap_write(es9356->regmap, ES9356_VMID1STL, 0x01); regmap_write(es9356->regmap, ES9356_VMID2STL, 0x00); regmap_write(es9356->regmap, ES9356_VSEL, 0xfc); regmap_write(es9356->regmap, ES9356_IBIASGEN, 0x10); - regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x0d); + regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x05); regmap_write(es9356->regmap, ES9356_STATE, 0x0e); regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x00); regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x08); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE, 0x31); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_WINSIZE, 0x3D); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_RESULT1, 0x80); regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); @@ -821,11 +860,14 @@ static void es9356_register_init(struct es9356_sdw_priv *es9356) SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); + + return 0; } static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + int ret = 0; if (es9356->hw_init) return 0; @@ -840,7 +882,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - es9356_register_init(es9356); + ret = es9356_register_init(es9356); + if (ret < 0) { + regcache_cache_only(es9356->regmap, true); + goto err_out; + } } pm_runtime_get_noresume(&slave->dev); @@ -867,10 +913,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) es9356->hw_init = true; +err_out: pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); - return 0; + return ret; } static int es9356_sdw_update_status(struct sdw_slave *slave, diff --git a/sound/soc/codecs/es9356.h b/sound/soc/codecs/es9356.h index ceb571903008..c147ecdd893f 100644 --- a/sound/soc/codecs/es9356.h +++ b/sound/soc/codecs/es9356.h @@ -38,6 +38,7 @@ #define ES9356_MICBIAS_CTL 0x2047 #define ES9356_HPDETECT_CTL 0x2048 #define ES9356_MICBIAS_RES 0x2049 +#define ES9356_SPARE_REGISTER3 0x204a /*CLK*/ #define ES9356_CLK_SEL 0x2050 @@ -82,6 +83,9 @@ #define ES9356_DAC_RAMP 0x2093 #define ES9356_DAC_VPPSCALE 0x2094 #define ES9356_DAC_SWAP 0x2097 +#define ES9356_DAC_AUTOMUTE 0x2098 +#define ES9356_DAC_AUTOMUTE_WINSIZE 0x2099 +#define ES9356_DAC_AUTOMUTE_RESULT1 0x209B #define ES9356_SPKCMP_VPPSC 0x20A0 #define ES9356_CALIBRATION_TIME 0x20A1 #define ES9356_CALIBRATION_SETTING 0x20A2 From c80f86ed857e21dcdd389559d3e62bf53ff39f9a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 22 Oct 2026 11:09:22 +0200 Subject: [PATCH 31/45] ASoC: codecs: wsa881x: Fix shared powerdown GPIO handling On Qualcomm RB5 both WSA8815 speaker amplifiers share a single powerdown GPIO (TLMM 130), which is handled by the shared GPIO proxy. Since commit b30973e8c392 ("gpio: shared: make the voting mechanism adaptable") the proxy treats the value set by the first user on request as the default and lets any single user vote for the opposite value. The driver requests the GPIO de-asserted (GPIOD_OUT_LOW), so powering the amplifier up became the default and asserting powerdown became the winning vote. Once both amplifiers are runtime suspended, resuming them deadlocks: the first one releases its powerdown vote and waits for SoundWire enumeration, but the line stays asserted, because the second amplifier is still suspended and votes for powerdown. The wait times out and playback fails: wsa881x-codec sdw:1:0:0217:2110:00:3: Initialization not complete wsa881x-codec sdw:1:0:0217:2110:00:3: ASoC error (-110): at snd_soc_pcm_component_pm_runtime_get() on sdw:1:0:0217:2110:00:3 Request the GPIO asserted, so that powerdown is the default state and the line is de-asserted as long as any amplifier needs to be powered, then de-assert it to power up the amplifier as before. Also use gpiod_set_value_cansleep() instead of gpiod_direction_output() in the runtime PM callbacks. The line is already configured as an output and the proxy resets its default value when the direction is set while it has only a single user, which can happen if the other amplifiers have not been probed yet. Fixes: bfa336cee332 ("ASoC: wsa881x: Move custom workaround to gpiolib-of") Fixes: b30973e8c392 ("gpio: shared: make the voting mechanism adaptable") Signed-off-by: Marek Szyprowski Link: https://patch.msgid.link/20261022090923.769464-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 47ac3e4985a5..4f3e6bc29c7e 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1115,11 +1115,19 @@ static int wsa881x_probe(struct sdw_slave *pdev, if (!wsa881x) return -ENOMEM; - wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); + /* + * The powerdown GPIO may be shared between several amplifiers. Request + * it asserted, so the shared GPIO proxy treats powerdown as the default + * state and keeps the line de-asserted as long as any amplifier needs + * it, then power up this amplifier. + */ + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(wsa881x->sd_n)) return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), "Shutdown Control GPIO not found\n"); + gpiod_set_value_cansleep(wsa881x->sd_n, 0); + dev_set_drvdata(dev, wsa881x); wsa881x->slave = pdev; wsa881x->dev = dev; @@ -1154,7 +1162,7 @@ static int wsa881x_runtime_suspend(struct device *dev) struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); @@ -1169,11 +1177,11 @@ static int wsa881x_runtime_resume(struct device *dev) struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); int ret; - gpiod_direction_output(wsa881x->sd_n, 0); + gpiod_set_value_cansleep(wsa881x->sd_n, 0); ret = sdw_slave_wait_for_init(slave, WSA881X_PROBE_TIMEOUT); if (ret) { - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); return ret; } @@ -1182,7 +1190,7 @@ static int wsa881x_runtime_resume(struct device *dev) if (ret) { regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); return ret; } From dd64271fe1b935252cf454a72a558cbdb7be57c5 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 26 Oct 2026 13:48:36 +0800 Subject: [PATCH 32/45] ASoC: codecs: es*: Modify the registers related to ALC RECOVERY The register related to "ALC Capture Recovery Level" is ES8326_ALC_RECOVERY, not ES8326_ALC_LEVEL. Fixes: 5c439937775d ("ASoC: codecs: add support for ES8326") Reviewed-by: HyeongJun An Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261026054837.13618-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a95b3b8c75dd..a1b93f6fafd9 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -353,7 +353,7 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE, 4, 0x07, 0, dre_gate_tlv), SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), - SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, + SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_RECOVERY, 0, 4, 0, drc_recovery_tlv), SOC_ENUM("ALC Capture Winsize", alc_winsize), SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, From 29b867d8351de378704775d3733bfb589c48ab1f Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 26 Oct 2026 13:48:37 +0800 Subject: [PATCH 33/45] ASoC: codecs: es*: Modify the maximum value of DMIC_GAIN The Kcontrol related to DMIC_GAIN affects bits[3:2]. However, the old code sets ES8375_DMIC_GAIN_MAX to 4, which causes the Kcontrol to affect bits [4:2],this is incorrect. Therefore, change ES8375_DMIC_GAIN_MAX to 3. Fixes: de2b3119f9f7 ("ASoC: codecs: add support for ES8375") Reviewed-by: HyeongJun An Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20261026054837.13618-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8375.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h index b48d03b1f689..de133437cefe 100644 --- a/sound/soc/codecs/es8375.h +++ b/sound/soc/codecs/es8375.h @@ -94,7 +94,7 @@ // Function values #define ES8375_ADC_OSR_GAIN_MAX 0x3F -#define ES8375_DMIC_GAIN_MAX 0x04 +#define ES8375_DMIC_GAIN_MAX 0x03 #define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F #define ES8375_AUTOMUTE_NG_MAX 0x07 #define ES8375_ADC_VOLUME_MAX 0xFF From 79cfb5aebbf0417418c2ac25669fade7f3e58ef2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 20 Oct 2026 08:38:51 +0100 Subject: [PATCH 34/45] ASoC: SDCA: propagate sdw_slave_read_prop() failure in class_read_prop sdw_slave_read_prop() can fail with -ENOMEM; propagate the return instead of leaving slave->prop partially initialised. Fixes: 2d877d0659cb ("ASoC: SDCA: Add basic SDCA class driver") Cc: stable@vger.kernel.org Reviewed-by: Charles Keepax Assisted-by: LLM Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261020073856.1177749-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 79fdb672be1d..bcc660aeb739 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -27,8 +27,11 @@ static int class_read_prop(struct sdw_slave *sdw) { struct sdw_slave_prop *prop = &sdw->prop; + int ret; - sdw_slave_read_prop(sdw); + ret = sdw_slave_read_prop(sdw); + if (ret) + return ret; prop->use_domain_irq = true; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | From 86331b76b32d7a83fb3e64641a5111746bf0f50c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 20 Oct 2026 08:38:52 +0100 Subject: [PATCH 35/45] ASoC: SDCA: drop pm_runtime reference on probe failure path sdca_class_probe() takes an extra usage count with pm_runtime_get_noresume() to keep the device powered on until the deferred boot work has run. If devm_pm_runtime_enable() then fails, that reference is never dropped. Drop it on the error path. Fixes: 2d877d0659cb ("ASoC: SDCA: Add basic SDCA class driver") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Srinivas Kandagatla Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20261020073856.1177749-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index bcc660aeb739..ca311209c6d1 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -197,8 +197,10 @@ int sdca_class_probe(struct sdw_slave *sdw, pm_runtime_get_noresume(dev); ret = devm_pm_runtime_enable(dev); - if (ret) + if (ret) { + pm_runtime_put_sync(dev); return ret; + } queue_work(system_long_wq, &drv->boot_work); From d34a818a331f7e270fa9dc3db3d96f6a0d4fa7ab Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 20 Oct 2026 08:38:53 +0100 Subject: [PATCH 36/45] ASoC: SDCA: re-enable IRQ on pm_runtime_force_suspend failure The SoundWire IRQ was disabled before pm_runtime_force_suspend() but never re-enabled when the force suspend failed, leaving the codec unable to receive interrupts. Fixes: 2d877d0659cb ("ASoC: SDCA: Add basic SDCA class driver") Cc: stable@vger.kernel.org Reviewed-by: Charles Keepax Assisted-by: LLM Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20261020073856.1177749-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index ca311209c6d1..ee61c983dc57 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -265,6 +265,7 @@ int sdca_class_system_suspend(struct sdca_class_drv *drv) ret = pm_runtime_force_suspend(drv->dev); if (ret) { dev_err(drv->dev, "failed to force suspend: %d\n", ret); + enable_irq(drv->sdw->irq); return ret; } From 55df18877471ade98ad0a1c12c95f78ba54ec5cc Mon Sep 17 00:00:00 2001 From: Filippo Falezza Date: Tue, 27 Oct 2026 01:33:56 +0000 Subject: [PATCH 37/45] ASoC: qcom: sm8250: Limit RX digital gain to 0 dB The RX macro interpolator gains ("RX_RXn Digital Volume") run from -84 dB to +40 dB. PipeWire and PulseAudio map full volume to the top of a hardware control's range, so a UCM device that uses one of them as its volume control plays 40 dB hot at 100% and clips. Set a platform limit of 84 (0 dB) on the RX0, RX1 and RX2 interpolator gains when the RX codec DMA link is initialised, the way the sc8280xp and x1e80100 machine drivers limit their WSA gains. 0 dB is the default of these controls and the value alsa-ucm-conf's rx-macro init sets, so existing configurations are unaffected. A control that does not exist on a board is skipped. Signed-off-by: Filippo Falezza Reviewed-by: Prasad Kumpatla Link: https://patch.msgid.link/20261027-sm8250-rx-gain-v1-1-386ee6d971db@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 5866f1f34b3d..d776a17f1fd3 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -39,6 +39,16 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) case USB_RX: return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, &data->usb_offload_jack_setup); + case RX_CODEC_DMA_RX_0: + /* + * The RX macro digital gain goes up to +40 dB. Userspace that + * maps full volume to the top of the range (PipeWire, PulseAudio) + * would clip there, so stop the interpolator gains at 0 dB. + */ + snd_soc_limit_volume(rtd->card, "RX_RX0 Digital Volume", 84); + snd_soc_limit_volume(rtd->card, "RX_RX1 Digital Volume", 84); + snd_soc_limit_volume(rtd->card, "RX_RX2 Digital Volume", 84); + fallthrough; default: return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } From 7d357eae973a1e1933f81f55aa1b3b4e3064570f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 10:38:38 +0200 Subject: [PATCH 38/45] ASoC: hdac_hda: Allocate the private data at codec driver bind The SOF HDA code allocates struct hdac_hda_priv and stores it as drvdata of the codec device before the codec driver is bound. The driver core clears drvdata on unbind, so after unbinding the codec driver via sysfs a subsequent bind dereferences a NULL pointer in hdac_hda_dev_probe(). All information in the private data can be derived from the codec device itself, so allocate and set it up in hdac_hda_dev_probe() as a device managed resource, which is released on unbind. Remove the allocation from SOF, keeping the check for iDisp codecs without display audio component. Fixes: 3fd63658caed ("ASoC: Intel: Drop hdac_ext usage for codec device creation") Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20261027083840.28813-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 13 ++++++++++++- sound/soc/sof/intel/hda-codec.c | 23 ++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 7414be08d3d5..46617fc88277 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -619,10 +619,21 @@ static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { static int hdac_hda_dev_probe(struct hdac_device *hdev) { - struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev); + struct hdac_hda_priv *hda_pvt; struct hdac_ext_link *hlink; int ret; + hda_pvt = devm_kzalloc(&hdev->dev, sizeof(*hda_pvt), GFP_KERNEL); + if (!hda_pvt) + return -ENOMEM; + + hda_pvt->codec = dev_to_hda_codec(&hdev->dev); + hda_pvt->dev_index = hdev->addr; + /* Intel iDisp (HDMI/DP) codec */ + if (((hdev->vendor_id >> 16) & 0xFFFF) == 0x8086) + hda_pvt->need_display_power = true; + dev_set_drvdata(&hdev->dev, hda_pvt); + /* hold the ref while we probe */ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index fd371850b0d6..32fcc1cd3f92 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -15,8 +15,6 @@ #include "hda.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) -#include "../../codecs/hdac_hda.h" - #define CODEC_PROBE_RETRIES 3 #define IDISP_VID_INTEL 0x80860000 @@ -148,7 +146,6 @@ static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, i /* probe individual codec */ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) { - struct hdac_hda_priv *hda_priv; struct hda_bus *hbus = sof_to_hbus(sdev); struct hda_codec *codec; u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | @@ -168,27 +165,15 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", address, resp); - hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL); - if (!hda_priv) - return -ENOMEM; - codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY); ret = PTR_ERR_OR_ZERO(codec); if (ret < 0) return ret; - hda_priv->codec = codec; - hda_priv->dev_index = address; - dev_set_drvdata(&codec->core.dev, hda_priv); - - if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) { - if (!hbus->core.audio_component) { - dev_dbg(sdev->dev, - "iDisp hw present but no driver\n"); - ret = -ENOENT; - goto out; - } - hda_priv->need_display_power = true; + if ((resp & 0xFFFF0000) == IDISP_VID_INTEL && !hbus->core.audio_component) { + dev_dbg(sdev->dev, "iDisp hw present but no driver\n"); + ret = -ENOENT; + goto out; } if (is_generic_config(hbus)) From 08a44bde821d4fe22c0b6e63da04b3c59ac243a8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 10:38:39 +0200 Subject: [PATCH 39/45] ASoC: hdac_hda: Restore the codec device type on unbind hdac_hda_codec_probe() changes the type of the codec device to HDA_DEV_ASOC to prevent snd_hda_codec_unregister() from unregistering the device when the card is freed. The type is never restored, and hda_bus_match() requires the device and driver types to be equal. After unbinding the codec driver via sysfs the HDA_DEV_LEGACY codec drivers no longer match the device and bind fails with -ENODEV. Restore HDA_DEV_LEGACY with a device managed action registered before the component, so it runs after the component and the card are gone. Fixes: b60ee2e281b6 ("ASoC: hdac_hda: overwrite hdev type to HDA_DEV_ASOC") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20261027083840.28813-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 46617fc88277..cae5711e979c 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -617,6 +617,13 @@ static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { .endianness = 1, }; +static void hdac_hda_restore_dev_type(void *data) +{ + struct hdac_device *hdev = data; + + hdev->type = HDA_DEV_LEGACY; +} + static int hdac_hda_dev_probe(struct hdac_device *hdev) { struct hdac_hda_priv *hda_pvt; @@ -634,6 +641,11 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev) hda_pvt->need_display_power = true; dev_set_drvdata(&hdev->dev, hda_pvt); + /* Allow the legacy codec driver to match again after unbind */ + ret = devm_add_action(&hdev->dev, hdac_hda_restore_dev_type, hdev); + if (ret) + return ret; + /* hold the ref while we probe */ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { From f26c0d5f91d918bb66cbbc8916f41836adb38346 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 10:38:40 +0200 Subject: [PATCH 40/45] ASoC: hdac_hda: Balance the runtime PM disable in codec remove hdac_hda_codec_remove() disables runtime PM of the codec device before releasing the HDA link and calling the codec driver's remove callback, so that no runtime resume can race with the teardown and access the link or the already freed codec driver data. snd_hda_codec_cleanup_for_unbind() then disables runtime PM again. On the next probe snd_hda_codec_register() enables runtime PM only once, leaving it disabled after the ASoC card or the codec driver has been unbound and bound again: the codec stays powered and runtime_status reads 'unsupported'. Re-enable runtime PM at the end of hdac_hda_codec_remove() to balance its own disable. The codec remains disabled until it is registered again. Fixes: c3ec8ac82105 ("ASoC: hdac_hda: fix memleak on module unload") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20261027083840.28813-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index cae5711e979c..bd4b113401a8 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -554,6 +554,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) return; } + /* prevent runtime PM callbacks while the link and the codec are torn down */ pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); @@ -561,6 +562,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); + + /* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */ + pm_runtime_enable(&hdev->dev); } static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { From e32773a86100a0a6e5e4b30c50b773de34b5c6c3 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 27 Oct 2026 14:02:47 +0800 Subject: [PATCH 41/45] ASoC: rt721: Remove invalid register defaults Egor Poderiagin reported runtime resume failures caused by rejected writes during regcache_sync(). Remove the PDE23 REQ_POWER_STATE default and the defaults for the volatile MBQ registers 0x2000046 and 0x6100006. Also remove 0x2000046 from the MBQ readable and volatile register lists. This allows cache sync to complete without suppressing sync errors. Thanks to Egor Poderiagin for the detailed report and testing of the default removals. Closes: https://lore.kernel.org/linux-sound/8a12a2d9-1b56-4e00-8ccc-cf31325f8366@crazyrussian.pro/ Fixes: 7a0886826308 ("ASoC: codecs: rt721-sdca-sdw: Propagate regcache_sync() errors") Signed-off-by: Oder Chiou Link: https://patch.msgid.link/20261027-rt721fix-v1-1-42cd4b1711d2@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt721-sdca-sdw.c | 2 -- sound/soc/codecs/rt721-sdca-sdw.h | 4 ---- 2 files changed, 6 deletions(-) diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index fc34a0537d5f..0c78799793bd 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -92,7 +92,6 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x2000026: case 0x200002c: case 0x200003c: - case 0x2000046: case 0x5810000: case 0x5810033: case 0x5810036: @@ -174,7 +173,6 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x2000026: case 0x200002c: case 0x2000030: - case 0x2000046: case 0x2000067: case 0x2000084: case 0x2000086: diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h index fe04a3726334..eb093992759c 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.h +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -63,8 +63,6 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, @@ -91,13 +89,11 @@ static const struct reg_default rt721_sdca_mbq_defaults[] = { { 0x2000003, 0x0000 }, { 0x2000013, 0x8001 }, { 0x200003c, 0x0000 }, - { 0x2000046, 0x3400 }, { 0x5b10017, 0x1b07 }, { 0x5f00044, 0x6040 }, { 0x5f00045, 0x3333 }, { 0x5f00048, 0x0000 }, { 0x6100005, 0x0005 }, - { 0x6100006, 0x0000 }, { 0x6100007, 0x0007 }, { 0x610000d, 0x0051 }, { 0x6100010, 0x0180 }, From 99203e8d8968def973e82bb712ea959c6a46c16e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 16:46:55 +0200 Subject: [PATCH 42/45] ASoC: hdac_hda: Keep runtime PM state in sync across codec remove/probe After the codec has been removed, its runtime PM status is RPM_SUSPENDED while hdac_hda_codec_probe() powers the codec up via snd_hda_codec_device_new(). The pm_runtime_put()/snd_hda_power_down() calls on probe and registration then see an already suspended device, the codec suspend callback is not called and the codec, its HDA link and the codec_powered bit stay set, preventing the controller from runtime suspending. The remove has a similar problem with the codec link accounting: hdac_hda_codec_remove() drops the link reference taken in probe without looking at the runtime PM state of the codec. If the codec was already runtime suspended, the link reference has been dropped by the codec suspend callback and the remove drops it a second time. Resume the codec in hdac_hda_codec_remove() before disabling runtime PM so that the link reference and the codec_powered bit are always in the same state as after the probe, and mark the device active on probe once the codec has been powered up. Finally mark the codec device suspended at the end of the remove. Its runtime PM is disabled at this point and if only the ASoC card is unbound (e.g. by unbinding the machine driver), a following system suspend would otherwise call the runtime suspend callback on the already cleaned up codec via pm_runtime_force_suspend(). This is a followup patch to address Sashiko reports. Link: https://lore.kernel.org/linux-sound/20261027083840.28813-4-peter.ujfalusi@linux.intel.com/ Fixes: c3ec8ac82105 ("ASoC: hdac_hda: fix memleak on module unload") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20261027144655.31988-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index bd4b113401a8..48eb95973b7a 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -514,6 +514,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, false); + /* The codec is in D0, the status is RPM_SUSPENDED after a previous unbind */ + pm_runtime_set_active(&hdev->dev); + /* match for forbid call in snd_hda_codec_device_new() */ pm_runtime_allow(&hdev->dev); @@ -554,6 +557,12 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) return; } + /* + * Resume the codec to have its link reference (dropped below) and + * codec_powered bit in the same state as after the probe. + */ + pm_runtime_get_sync(&hdev->dev); + /* prevent runtime PM callbacks while the link and the codec are torn down */ pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); @@ -565,6 +574,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) /* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */ pm_runtime_enable(&hdev->dev); + pm_runtime_put_noidle(&hdev->dev); + /* The codec is cleaned up, system suspend must skip it from now on */ + pm_runtime_set_suspended(&hdev->dev); } static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { From 3b54e739273752b370e49b09976da152fa3b802c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 23 Oct 2026 17:28:18 +0700 Subject: [PATCH 43/45] ASoC: xilinx: formatter_pcm: Fix read-modify-write of AUD_CTRL in hw_params xlnx_formatter_pcm_hw_params() does a read-modify-write on XLNX_AUD_CTRL but only ORs in the new data width and active channel values without clearing the old ones. If hw_params is called more than once on an open stream, these fields may retain bits from the previous configuration, resulting in an incorrect sample width or channel count. Clear both fields before writing the new values, and use FIELD_PREP() so the values cannot spill into neighbouring bits. Fixes: 6f6c3c36f091 ("ASoC: xlnx: add pcm formatter platform driver") Signed-off-by: bui duc phuc Reviewed-by: Gaddipati, Naveen Link: https://patch.msgid.link/20261023102818.523331-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 304220d9aaf5..781508aef885 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -6,6 +6,7 @@ // // Author: Maruthi Srinivas Bayyavarapu +#include #include #include #include @@ -55,8 +56,8 @@ #define CFG_S2MM_XFER_SHIFT 29 #define CFG_S2MM_PKG_MASK BIT(28) -#define AUD_CTRL_DATA_WIDTH_SHIFT 16 -#define AUD_CTRL_ACTIVE_CH_SHIFT 19 +#define AUD_CTRL_DATA_WIDTH_MASK GENMASK(18, 16) +#define AUD_CTRL_ACTIVE_CH_MASK GENMASK(22, 19) #define PERIOD_CFG_PERIODS_SHIFT 16 #define PERIODS_MIN 2 @@ -506,28 +507,29 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB); val = readl(stream_data->mmio + XLNX_AUD_CTRL); + val &= ~(AUD_CTRL_DATA_WIDTH_MASK | AUD_CTRL_ACTIVE_CH_MASK); bits_per_sample = params_width(params); switch (bits_per_sample) { case 8: - val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_8); break; case 16: - val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_16); break; case 20: - val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_20); break; case 24: - val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_24); break; case 32: - val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_32); break; default: return -EINVAL; } - val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT; + val |= FIELD_PREP(AUD_CTRL_ACTIVE_CH_MASK, active_ch); writel(val, stream_data->mmio + XLNX_AUD_CTRL); val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT) From bbe41586cdfa9aeb95d24b5682ce3a0f528e9648 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 19 Oct 2026 15:58:20 +0900 Subject: [PATCH 44/45] ASoC: imx-pcm-rpmsg: Flush pending work in hw_free imx_rpmsg_terminate_all() queues the TERMINATE message to the workqueue and returns, while the DAI hw_free() synchronously gates the audio clock. The two can then run concurrently, and this concurrent access to the shared audio clock path can cause a stop failure or even hang the system. Flush the workqueue in a new component hw_free(), which runs before the DAI hw_free(), so TERMINATE is fully handled before the clock is gated. CLOSE stays the last message. Signed-off-by: Chancel Liu Reviewed-by: Frank Li Reviewed-by: Shengjiu Wang Link: https://patch.msgid.link/20261019-b4-imx-pcm-rpmsg-concurrent-v1-1-c1fb8c33f4db@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-pcm-rpmsg.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 85b7c929a28a..6a0174d6c78d 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -178,6 +178,16 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component, return 0; } +static int imx_rpmsg_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct rpmsg_info *info = dev_get_drvdata(component->dev); + + flush_workqueue(info->rpmsg_wq); + + return 0; +} + static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -285,9 +295,6 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component, struct rpmsg_info *info = dev_get_drvdata(component->dev); struct rpmsg_msg *msg; - /* Flush work in workqueue to make TX_CLOSE is the last message */ - flush_workqueue(info->rpmsg_wq); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { msg = &info->msg[TX_CLOSE]; msg->s_msg.header.cmd = TX_CLOSE; @@ -624,6 +631,7 @@ static const struct snd_soc_component_driver imx_rpmsg_soc_component = { .open = imx_rpmsg_pcm_open, .close = imx_rpmsg_pcm_close, .hw_params = imx_rpmsg_pcm_hw_params, + .hw_free = imx_rpmsg_pcm_hw_free, .trigger = imx_rpmsg_pcm_trigger, .pointer = imx_rpmsg_pcm_pointer, .ack = imx_rpmsg_pcm_ack, From 11ae63bd9030349a0136fb2edd1622a4a029edc8 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 28 Oct 2026 10:44:46 -0400 Subject: [PATCH 45/45] ASoC: SOF: Intel: hda: Stop the controller before freeing its DMA buffers hda_dsp_probe() starts the CORB/RIRB DMA engines through hda_dsp_ctrl_init_chip(), but neither hda_dsp_remove() nor the error path of hda_dsp_probe() stops them before hda_dsp_stream_free() frees the CORB/RIRB ring buffer and the position buffer. Unsolicited responses also stay enabled, so the controller can keep writing RIRB entries into the freed page. Until commit 9e6966f70404 ("ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+"), hda_init_caps() dropped the initial references to the non-alternate links, and dropping the last one stopped the command DMA, so a probe that failed later, e.g. because the firmware is missing, did not leave it running. Now it does, and as the probe runs from a work item, the device stays bound after the failure, with bus mastering enabled and the DMA engines pointed at freed memory. Normal removal resumes the device first, which restarts the command DMA, so there the ring buffer is freed while the DMA is running until pci_disable_device() clears bus mastering. Stop the controller with hda_dsp_ctrl_stop_chip() before freeing the buffers, as is done on suspend, and export it for use from hda.c. This was reproduced by binding SOF in DSPless mode to a QEMU ich9-intel-hda: after the probe fails with "SOF firmware and/or topology file not found", CORB and RIRB DMA are still running with unsolicited responses and bus mastering enabled, and the page they use is soon reused as a page table. Fixes: 9e6966f70404 ("ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+") Signed-off-by: Sasha Levin Acked-by: Peter Ujfalusi Tested-by: Li Yifan Reported-by: Li Yifan Closes: https://github.com/thesofproject/linux/pull/5976 Link: https://patch.msgid.link/20261028144446.992948-1-sashal@kernel.org Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-ctrl.c | 1 + sound/soc/sof/intel/hda.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index aeb34310eebd..333f6bbea753 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -349,6 +349,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) bus->chip_init = false; } +EXPORT_SYMBOL_NS(hda_dsp_ctrl_stop_chip, "SND_SOC_SOF_INTEL_HDA_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for HDaudio platforms"); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29..2a40cb9bf24d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -939,6 +939,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) hda_dsp_ctrl_ppcap_enable(sdev, false); } free_ipc_irq: + hda_dsp_ctrl_stop_chip(sdev); free_irq(sdev->ipc_irq, sdev); free_irq_vector: if (sdev->msi_enabled) @@ -1006,6 +1007,9 @@ void hda_dsp_remove(struct snd_sof_dev *sdev) snd_dma_free_pages(&hda->iccmax_dmab); skip_disable_dsp: + /* stop CORB/RIRB and position buffer DMA before the buffers are freed */ + hda_dsp_ctrl_stop_chip(sdev); + free_irq(sdev->ipc_irq, sdev); if (sdev->msi_enabled) pci_free_irq_vectors(pci);