From ff404ef11b975baf38c4472c51a6674c239e8021 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Mon, 19 Oct 2026 21:40:22 +0900 Subject: [PATCH 01/71] 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/71] 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/71] 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/71] 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/71] 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/71] 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/71] 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/71] 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/71] 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 75bed736620f70f7a719da5637191f9d7b745b99 Mon Sep 17 00:00:00 2001 From: Volodymyr Lurin Date: Wed, 21 Oct 2026 22:38:39 -0400 Subject: [PATCH 10/71] ALSA: hda/realtek: Add mute LEDs quirk for HP Pavilion Plus 14-EW1XXX. The HP Pavilion Plus 14-ew1xxx with subsystem ID 103c:0x8c31 needs the ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT bit LED quirk to control speaker mute LED and ALC245_FIXUP_HP_GPIO_LED quirk to control mic mute LED. Define new ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS fixup that combine existing COEF(ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT) and GPIO (ALC245_FIXUP_HP_GPIO_LED) fixups. Add the missing quirk entry. Tested on the affected hardware: both the mute and mic-mute key LEDs respond correctly to the keyboard hotkeys after this change. Based on tests, to control mute LED, it is sufficient to flip coef bit #3. While ALC245_FIXUP_HP_X360_MUTE_LEDS works too (combines ALC245_FIXUP_HP_MUTE_LED_COEFBIT and ALC245_FIXUP_HP_GPIO_LED), it also flip coef bit #2, and side effect of this "flip" is not clear. So separate chained quirk introduced to be of safe side. Signed-off-by: Volodymyr Lurin Link: https://patch.msgid.link/20261022023839.504841-1-volodymyr.iurin@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 23aa175faedd..2f0645482108 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4492,6 +4492,7 @@ enum { ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, + ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS, ALC287_FIXUP_THINKPAD_I2S_SPK, ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC287_FIXUP_CS35L41_I2C_4_THINKPAD_ACPI, @@ -7113,6 +7114,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC245_FIXUP_HP_GPIO_LED }, + [ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_v2_coefbit, + .chained = true, + .chain_id = ALC245_FIXUP_HP_GPIO_LED + }, [ALC287_FIXUP_THINKPAD_I2S_SPK] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_bind_dacs, @@ -7951,6 +7958,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c31, "HP Pavilion Plus Laptop 14-ew1xxx", ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS), SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), From d2a1fb09414a79de340215e03fb7f85e17cf0a2b Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 22 Oct 2026 10:59:49 +0800 Subject: [PATCH 11/71] ALSA: hda/realtek: Fix internal mic on ASUS VivoBook X509JB On the ASUS VivoBook X509JB (SSID 1043:1ec1) with ALC256, the BIOS marks pin 0x13 as not connected and pin 0x1b as the internal mic, but the generic parser still defaults Capture Source to 0x13, which records only white noise. Manually selecting 0x1b works, but PipeWire/WirePlumber resets it on every stream open. Reuse ALC2XX_FIXUP_HEADSET_MIC for this SSID. It limits capture sources to 0x19 and 0x1b, removing the bogus 0x13 source so the internal mic records normally without manual mixer changes. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=222152 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20261022025950.150390-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2f0645482108..5a2f93c787a9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8234,6 +8234,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), + SND_PCI_QUIRK(0x1043, 0x1ec1, "ASUS X509JB", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), From fd47de58575a27869357a5ca2435f9b58c01b643 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 22 Oct 2026 11:11:13 +0800 Subject: [PATCH 12/71] ALSA: hda/realtek: Add quirk for HP Pavilion AiO 24 (ALC225) The HP Pavilion AIO 24-xa0xxx (codec SSID 103c:84ee) uses the ALC225 codec. The internal speakers produce no sound without a machine-specific fixup. Add a HDA_CODEC_QUIRK for this codec SSID using ALC225_FIXUP_HP_AIO_SPK so the speaker output works correctly. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221616 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20261022031113.163632-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5a2f93c787a9..e7b4543171fd 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8068,6 +8068,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + HDA_CODEC_QUIRK(0x103c, 0x84ee, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK), SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), From b4b7f5f8beabe3cb92028b5b3b6d1b3dac5363fc Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Thu, 22 Oct 2026 15:14:56 +0800 Subject: [PATCH 13/71] ALSA: usb-audio: Add iface reset and delay quirk for RYDZ Technology USB Headphone Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: New USB device found, idVendor=0124, idProduct=0c36 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: USB Headphone usb 1-1: Manufacturer: RYDZ Technology usb 1-1: SerialNumber: 202601131108 Signed-off-by: Lianqin Hu Link: https://patch.msgid.link/20261022071456.14256-1-hulianqin@vivo.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 43eea724779a..6aa212c2cab6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2496,6 +2496,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x0124, 0x0c36, /* RYDZ Technology USB Headphone */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x0c56, /* HP Elite x3 Lap Dock */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ From 3a003faf38273b8c6b566a0c437914d8d1f82fb3 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 22 Oct 2026 10:14:36 +0200 Subject: [PATCH 14/71] ALSA: ac97: Use snprintf() in snd_ac97_get_name() To prepare for removing strlcat() from the kernel [1], use snprintf() to append the codec name and the optional revision. Also use snprintf() for the unknown vendor fallback. Fix some formatting while at it. No functional change. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20261022081437.1029-2-blum@kernel.org Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_codec.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 207ce083593f..f0ebe6d45836 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1832,36 +1832,39 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, size_t maxlen, int modem) { const struct ac97_codec_id *pid; + size_t len; - sprintf(name, "0x%x %c%c%c", id, - printable(id >> 24), - printable(id >> 16), - printable(id >> 8)); pid = look_for_codec_id(snd_ac97_codec_id_vendors, id); - if (! pid) + if (!pid) { + snprintf(name, maxlen, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); return; + } strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); - } + } + len = strnlen(name, maxlen); pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strlcat(name, " ", maxlen); - strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) - sprintf(name + strlen(name), " rev %u", id & ~pid->mask); + snprintf(name + len, maxlen - len, " %s rev %u", + pid->name, id & ~pid->mask); + else + snprintf(name + len, maxlen - len, " %s", pid->name); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } } else { - int l = strlen(name); - snprintf(name + l, maxlen - l, " id %x", id & 0xff); + snprintf(name + len, maxlen - len, " id %x", id & 0xff); } } From a44a446d24180a7d3d8e712a39a5d8e6243ab688 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 22 Oct 2026 10:22:47 +0200 Subject: [PATCH 15/71] ALSA: ump: Use safe_append_string() in snd_ump_update_group_attrs() To prepare for removing strlcat() from the kernel [1], use the existing safe_append_string() helper to append the group name separator. No functional change. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20261022082247.1682-2-blum@kernel.org Signed-off-by: Takashi Iwai --- sound/core/ump.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/ump.c b/sound/core/ump.c index f6482a9bfe61..04ef66298943 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -559,6 +559,7 @@ static void snd_ump_proc_read(struct snd_info_entry *entry, /* update dir_bits and active flag for all groups in the client */ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) { + static const char separator[] = ", "; struct snd_ump_block *fb; struct snd_ump_group *group; int i; @@ -598,7 +599,8 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) if (!*fb->info.name) continue; if (*group->name) - strlcat(group->name, ", ", sizeof(group->name)); + safe_append_string(group->name, sizeof(group->name), + separator, sizeof(separator)); safe_append_string(group->name, sizeof(group->name), fb->info.name, sizeof(fb->info.name)); } From 677dc50304129465dc4626da01a3c0991dc89537 Mon Sep 17 00:00:00 2001 From: renpanpan Date: Thu, 22 Oct 2026 16:54:38 +0800 Subject: [PATCH 16/71] ALSA: hda: Force MSI for Hygon H34M51A board The Hygon HDA controller (PCI 1d94:14a9) has AZX_DCAPS_NO_MSI set in the PCI ID table to use legacy INTx, as MSI may lose interrupts under heavy load on some Hygon platforms (commit 41212ff3e4ed ("ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio")). However, on the Kunlun H34M51A board (PCI subsystem 1d94:c950) the INTx interrupt line is not wired. /proc/interrupts shows zero count for the HDA IRQ from boot, so the RIRB interrupt handler is never called. Without RIRB processing, azx_rirb_get_response() times out after 1 s, switches to polling mode, and the resulting slow codec communication delays card registration by ~20 s and breaks unsolicited jack-detection event delivery. Force MSI on for subsystem 1d94:c950 by adding it to msi_deny_list with value 1. The list is consulted in check_msi() before the AZX_DCAPS_NO_MSI check, so H34M51A gets MSI enabled while other Hygon platforms retain INTx. Signed-off-by: renpanpan Link: https://patch.msgid.link/20261022085438.1315679-1-rppxj178543@163.com Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 6b80b59b8743..af01b2669c49 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -1668,6 +1668,7 @@ static const struct snd_pci_quirk msi_deny_list[] = { SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ + SND_PCI_QUIRK(0x1d94, 0xc950, "Hygon H34M51A", 1), /* INTx broken */ {} }; From 9a8b6b459e3a0e677cec7c0cab18602b6f3bba45 Mon Sep 17 00:00:00 2001 From: Shahab Ghodsi Date: Thu, 22 Oct 2026 12:31:49 +0330 Subject: [PATCH 17/71] ALSA: hda/realtek: Add quirk for ASUS ExpertBook B3402FVA The ASUS ExpertBook B3402FVA (PCI SSID 1043:1e03) uses an ALC256 codec. Headphone output is extremely quiet and distorted, with only the loudest parts audible. The BIOS does not enable the headphone depop circuit. Reuse ALC256_FIXUP_ASUS_HPE, which sets COEF 0x0f to 0x7778 to enable the headphone depop circuit. This fixup is already used for the ASUS TM420 and B1400CEPE. The COEF write was verified manually with hda-verb on this machine; headphones become loud and clear. The chained pin change on 0x19 was also applied manually; the headset microphone worked before and after, and audio playback was unaffected. Closes: https://github.com/thesofproject/linux/issues/5996 Signed-off-by: Shahab Ghodsi Link: https://patch.msgid.link/20261022090149.304576-1-shahab16774@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e7b4543171fd..c499165b80e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8223,6 +8223,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e03, "ASUS ExpertBook B3402FVA", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e3e, "ASUS VivoBook M515DA/X515DAP", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), From 8cf9607d2d855629260fc78e6b54ea7a98525708 Mon Sep 17 00:00:00 2001 From: Alex Kazinsky Date: Fri, 23 Oct 2026 17:08:52 +0400 Subject: [PATCH 18/71] 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 19/71] 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 20/71] 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 21/71] 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 0d01f90b07e877ef31f9959e9b0bc6bcca9b99df Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Thu, 22 Oct 2026 18:32:41 +0200 Subject: [PATCH 22/71] ALSA: sh: aica: Fix spu_memset() stride spu_memset() writes one 32-bit word per iteration, but advances the destination offset by only one byte. spu_reset() calls it with 0x200000 / 4 words to clear the 2 MiB of sound RAM, so: - three out of four writel() calls target an unaligned address. The SH4 can't do that. Each one traps and is emulated by the unaligned access fixup. This call to spu_memset() causes 393216 unaligned accesses, visible in /proc/cpu/alignment. - the clear covered only about 512 KiB of sound RAM, not 2 MiB. Advance the offset by the size of the written word, to clear the full sound RAM using aligned writes. Fixes: 198de43d758c ("[ALSA] Add ALSA support for the SEGA Dreamcast PCM device") Signed-off-by: Florian Fuchs Acked-by: Adrian McMenamin Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261022163241.2504922-1-fuchsfl@gmail.com --- sound/sh/aica.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 078fc7fc08f9..04f80c8c89c0 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -94,7 +94,7 @@ static void spu_memset(u32 toi, u32 what, int length) local_irq_save(flags); writel(what, toi + SPU_MEMORY_BASE); local_irq_restore(flags); - toi++; + toi += sizeof(what); } } From e4a21e95c5577dda3a4049b8eb7f2df7d24b3134 Mon Sep 17 00:00:00 2001 From: Andrey Zakharov Date: Fri, 23 Oct 2026 13:50:27 +0300 Subject: [PATCH 23/71] ALSA: emu10k1: Pass destination size to copy_string() copy_string() copies the bus and input/output names into fixed-size arrays of struct snd_emu10k1_fx8010_info using strcpy() and sprintf(), and it has no information about the size of the destination buffer. All source strings are static and shorter than the 32-byte destination arrays (the longest one is 20 characters), so this is safe today, but the safety should not depend on the contents of the name tables. The earlier strcpy() to strscpy() conversions skipped this function because dst is a plain pointer. Pass the destination size to copy_string() and use strscpy() and snprintf() instead. No functional changes intended. Testing: compile-tested only with clang (W=1) and sparse (C=1). The function is reached only through the SNDRV_EMU10K1_IOCTL_INFO ioctl of the EMU10K1 hardware, which is not available under QEMU. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Andrey Zakharov Link: https://patch.msgid.link/20261023105027.53135-1-dedemorow@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emufx.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index a33817aba17f..d49866d9961e 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2423,12 +2423,12 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file) return 0; } -static void copy_string(char *dst, const char *src, const char *null, int idx) +static void copy_string(char *dst, size_t size, const char *src, const char *null, int idx) { if (src == NULL) - sprintf(dst, "%s %02X", null, idx); + snprintf(dst, size, "%s %02X", null, idx); else - strcpy(dst, src); + strscpy(dst, src, size); } static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, @@ -2446,12 +2446,16 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask; extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask; for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { - copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res); - copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->fxbus_names[res], sizeof(info->fxbus_names[res]), + *fxbus, "FXBUS", res); + copy_string(info->extin_names[res], sizeof(info->extin_names[res]), + extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); } for (res = 16; res < 32; res++, extout++) - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); info->gpr_controls = emu->fx8010.gpr_count; } From d74eb09af56beda9cbe310e9f6554ff1f35f280f Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 22 Oct 2026 18:58:20 +0800 Subject: [PATCH 24/71] ALSA: pcm: Drain proc callbacks at device disconnect Disconnecting an individual PCM leaves its proc files accessible until snd_pcm_free_stream(). HDA codec-only sysfs unbind can take this path while the controller and card remain registered. An open proc file pins the card module, but does not hold a codec PCM reference. With CONFIG_SND_VERBOSE_PROCFS, a prealloc write is buffered until file release. Without an attached PCM runtime, its release can replace the DMA buffer after final PCM cleanup has already freed the preallocation: HDA codec unbind prealloc file release ---------------- --------------------- hda_codec_driver_remove() snd_hda_codec_disconnect_pcms() snd_device_disconnect() snd_pcm_dev_disconnect() /* proc nodes remain */ snd_hda_codec_cleanup_for_unbind() codec_release_pcms() snd_device_free() snd_pcm_free() free preallocated buffer snd_info_text_entry_release() snd_pcm_lib_preallocate_proc_write() replace DMA buffer remove stream proc nodes free substream The later proc rundown can itself invoke this buffered write and leave the replacement allocation without a final free. A concurrent release can also overlap the earlier buffer cleanup. Extract snd_info_disconnect_entry() from snd_info_free_entry() to remove proc nodes and drain callbacks without freeing the info entry objects. Use it for both stream directions at PCM disconnect. Finish the existing PCM state and device disconnection under open_mutex, then drop that lock before proc rundown: even an otherwise idle proc file can run its buffered release callback, which takes open_mutex, during proc_remove(). Keep entry ownership and final cleanup unchanged, including the saved OSS child entries. As with existing info entry teardown, callers must serialize registration with disconnection and release. Suggested-by: Takashi Iwai Assisted-by: LLM Signed-off-by: Cen Zhang Link: https://patch.msgid.link/pm-pmb-2439c851f82c443c3600-v7-2774a482b7f03e1fef60@gmail.com Signed-off-by: Takashi Iwai --- include/sound/info.h | 2 ++ sound/core/info.c | 23 +++++++++++++++---- sound/core/pcm.c | 54 ++++++++++++++++++++++++++------------------ 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/include/sound/info.h b/include/sound/info.h index adbc506860d6..f216291e5c41 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -117,6 +117,7 @@ struct snd_info_entry *snd_info_create_module_entry(struct module *module, struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent); +void snd_info_disconnect_entry(struct snd_info_entry *entry); void snd_info_free_entry(struct snd_info_entry *entry); int snd_info_card_create(struct snd_card *card); @@ -164,6 +165,7 @@ static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; } static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; } +static inline void snd_info_disconnect_entry(struct snd_info_entry *entry) { } static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; } static inline int snd_info_card_create(struct snd_card *card) { return 0; } diff --git a/sound/core/info.c b/sound/core/info.c index c4d57d0809ad..023b4736aa6d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -737,6 +737,23 @@ static void snd_info_clear_entries(struct snd_info_entry *entry) entry->p = NULL; } +/** + * snd_info_disconnect_entry - remove proc nodes and drain their callbacks + * @entry: root of the info entry subtree + * + * Retains the info entries for snd_info_free_entry(). Callers must serialize + * registration and destruction, and must not hold locks needed by callbacks. + */ +void snd_info_disconnect_entry(struct snd_info_entry *entry) +{ + if (entry && entry->p) { + proc_remove(entry->p); + guard(mutex)(&info_mutex); + snd_info_clear_entries(entry); + } +} +EXPORT_SYMBOL(snd_info_disconnect_entry); + /** * snd_info_free_entry - release the info entry * @entry: the info entry @@ -749,11 +766,7 @@ void snd_info_free_entry(struct snd_info_entry * entry) if (!entry) return; - if (entry->p) { - proc_remove(entry->p); - guard(mutex)(&info_mutex); - snd_info_clear_entries(entry); - } + snd_info_disconnect_entry(entry); /* free all children at first */ list_for_each_entry_safe(p, n, &entry->children, list) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1e4e0ecb3dbd..064df4cd3b50 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -520,6 +520,11 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) return 0; } +static void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) +{ + snd_info_disconnect_entry(pstr->proc_root); +} + static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { snd_info_free_entry(pstr->proc_root); @@ -580,6 +585,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) #else /* !CONFIG_SND_VERBOSE_PROCFS */ static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } +static inline void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) { } static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } #endif /* CONFIG_SND_VERBOSE_PROCFS */ @@ -1094,33 +1100,37 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int cidx; guard(mutex)(®ister_mutex); - guard(mutex)(&pcm->open_mutex); - wake_up(&pcm->open_wait); - list_del_init(&pcm->list); + scoped_guard(mutex, &pcm->open_mutex) { + wake_up(&pcm->open_wait); + list_del_init(&pcm->list); - for_each_pcm_substream(pcm, cidx, substream) { - snd_pcm_stream_lock_irq(substream); - if (substream->runtime) { - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - /* to be sure, set the state unconditionally */ - __snd_pcm_set_state(substream->runtime, - SNDRV_PCM_STATE_DISCONNECTED); - wake_up(&substream->runtime->sleep); - wake_up(&substream->runtime->tsleep); + for_each_pcm_substream(pcm, cidx, substream) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime) { + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + /* to be sure, set the state unconditionally */ + __snd_pcm_set_state(substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + wake_up(&substream->runtime->sleep); + wake_up(&substream->runtime->tsleep); + } + snd_pcm_stream_unlock_irq(substream); } - snd_pcm_stream_unlock_irq(substream); - } - for_each_pcm_substream(pcm, cidx, substream) - snd_pcm_sync_stop(substream, false); + for_each_pcm_substream(pcm, cidx, substream) + snd_pcm_sync_stop(substream, false); - pcm_call_notify(pcm, n_disconnect); - for (cidx = 0; cidx < 2; cidx++) { - if (pcm->streams[cidx].dev) - snd_unregister_device(pcm->streams[cidx].dev); - free_chmap(&pcm->streams[cidx]); + pcm_call_notify(pcm, n_disconnect); + for (cidx = 0; cidx < 2; cidx++) { + if (pcm->streams[cidx].dev) + snd_unregister_device(pcm->streams[cidx].dev); + free_chmap(&pcm->streams[cidx]); + } } + /* Proc rundown can run release callbacks which take open_mutex. */ + for (cidx = 0; cidx < 2; cidx++) + snd_pcm_stream_proc_disconnect(&pcm->streams[cidx]); return 0; } From 73e38d178bbd28fe2c30443e6c0cf74e3f584bdb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 24 Oct 2026 10:00:10 +0200 Subject: [PATCH 25/71] ALSA: usb-audio: Correct a typo in UMC404HD model comment The recently added quirk code has a comment for 1397:0509 as if it were UMC204HD, but it's UMC404HD. Correct it. Fixes: e88f4749c02f ("ALSA: usb-audio: Avoid applying boot quirk for Behringer UMC204HD") Suggested-by: Gergo Koteles Link: https://patch.msgid.link/20261024080011.2258391-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6aa212c2cab6..b6be3a80d9c9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1794,7 +1794,7 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, /* the quirk seems breaking on some Behringer devices */ switch (id) { case USB_ID(0x1397, 0x0508): /* UMC204HD */ - case USB_ID(0x1397, 0x0509): /* UMC204HD */ + case USB_ID(0x1397, 0x0509): /* UMC404HD */ return 0; } return snd_usb_cm1a_boot_quirk(dev); From 9b30046de03acb3b3f2135d45af6be7ee0b565b6 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 24 Oct 2026 16:02:47 +0800 Subject: [PATCH 26/71] ALSA: ump: Drain sequencer callbacks before freeing their client seq_ump_client_free() cancels group_notify_work while the endpoint still publishes seq_ops and seq_client. A received Function Block update can enter the sequencer callback after cancellation and queue the work again. Even disabling the work cannot protect a callback that already loaded seq_client and then resumes after the client has been freed. A USB UMP endpoint can continue receiving while its sequencer binding is removed. The following order uses the same endpoint and client: Sequencer binding removal UMP input completion ------------------------- -------------------- cancel_work_sync() load seq_ops and seq_client delete sequencer client clear endpoint binding kfree(client) schedule group_notify_work read client->seq_client Cover all calls through seq_ops with SRCU in the UMP core. Withdraw the operations pointer and wait for those callbacks before canceling work and deleting the client. No producer can then queue work after the final cancellation, and both the client and callback code remain available until the last in-flight callback returns. Publish seq_ops with release/acquire ordering so the separately stored seq_client is initialized before any callback uses it. Use SRCU because sequencer notification delivery can sleep; no callback needs to change its existing execution context or allocation behavior. Fixes: 4a16a3af0571 ("ALSA: seq: ump: Handle FB info update") Assisted-by: LLM Signed-off-by: Cen Zhang Link: https://patch.msgid.link/pm-pmb-00625126b81966d1bf1d-v2-915f6bbabe1fd458c3ab@gmail.com Signed-off-by: Takashi Iwai --- include/sound/ump.h | 5 +++ sound/core/seq/seq_ump_client.c | 5 +-- sound/core/ump.c | 59 ++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/include/sound/ump.h b/include/sound/ump.h index 73f97f88e2ed..387143783a9b 100644 --- a/include/sound/ump.h +++ b/include/sound/ump.h @@ -66,6 +66,7 @@ struct snd_ump_endpoint { #if IS_ENABLED(CONFIG_SND_SEQUENCER) struct snd_seq_device *seq_dev; + /* Published after seq_client; calls are covered by UMP core SRCU. */ const struct snd_seq_ump_ops *seq_ops; void *seq_client; #endif @@ -126,6 +127,10 @@ int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val); int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol); void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump); +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +void snd_ump_detach_seq(struct snd_ump_endpoint *ump); +#endif + /* * Some definitions for UMP */ diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 4c1e81db376f..ca9dcec19883 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -353,12 +353,12 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) /* release the client resources */ static void seq_ump_client_free(struct seq_ump_client *client) { + snd_ump_detach_seq(client->ump); cancel_work_sync(&client->group_notify_work); if (client->seq_client >= 0) snd_seq_delete_kernel_client(client->seq_client); - client->ump->seq_ops = NULL; client->ump->seq_client = NULL; kfree(client); @@ -523,7 +523,8 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) snd_seq_kernel_client_put(cptr); ump->seq_client = client; - ump->seq_ops = &seq_ump_ops; + /* Publish the initialized client before callbacks can observe seq_ops. */ + smp_store_release(&ump->seq_ops, &seq_ump_ops); return 0; error: diff --git a/sound/core/ump.c b/sound/core/ump.c index 04ef66298943..d5a5322082f1 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,31 @@ #define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args) #define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +DEFINE_STATIC_SRCU(ump_seq_srcu); + +static const struct snd_seq_ump_ops *seq_ops_load(struct snd_ump_endpoint *ump) +{ + /* Pair with publication after seq_client has been initialized. */ + return smp_load_acquire(&ump->seq_ops); +} + +/** + * snd_ump_detach_seq - stop callbacks to a sequencer binding + * @ump: UMP endpoint + * + * Withdraw the binding and wait for all callbacks already in progress. + * The caller can then drain binding work and free the sequencer client. + * Must not be called from one of the binding callbacks. + */ +void snd_ump_detach_seq(struct snd_ump_endpoint *ump) +{ + WRITE_ONCE(ump->seq_ops, NULL); + synchronize_srcu(&ump_seq_srcu); +} +EXPORT_SYMBOL_GPL(snd_ump_detach_seq); +#endif + static int snd_ump_dev_register(struct snd_rawmidi *rmidi); static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi); static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd, @@ -348,8 +374,13 @@ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count) continue; ump_handle_stream_msg(ump, ump->input_buf, n); #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops) - ump->seq_ops->input_receive(ump, ump->input_buf, n); + scoped_guard(srcu, &ump_seq_srcu) { + const struct snd_seq_ump_ops *ops; + + ops = seq_ops_load(ump); + if (ops) + ops->input_receive(ump, ump->input_buf, n); + } #endif process_legacy_input(ump, ump->input_buf, n); } @@ -701,8 +732,12 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump) static void seq_notify_ep_change(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change) - ump->seq_ops->notify_ep_change(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ump->parsed && ops && ops->notify_ep_change) + ops->notify_ep_change(ump); #endif } @@ -799,8 +834,12 @@ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump, static void seq_notify_protocol(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->switch_protocol) - ump->seq_ops->switch_protocol(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->switch_protocol) + ops->switch_protocol(ump); #endif /* CONFIG_SND_SEQUENCER */ } @@ -895,8 +934,12 @@ static void seq_notify_fb_change(struct snd_ump_endpoint *ump, struct snd_ump_block *fb) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->notify_fb_change) - ump->seq_ops->notify_fb_change(ump, fb); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->notify_fb_change) + ops->notify_fb_change(ump, fb); #endif } From a71cef6f641152c7c7bbc17d86f33a3b45cd10a9 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:49 +0800 Subject: [PATCH 27/71] ALSA: opl3: Initialize the timer before publishing a sequencer port snd_opl3_seq_probe() initializes the delayed-note timer and its lock after snd_opl3_synth_create_port() has made the native port reachable. A direct NOTEON does not require a subscription, and a fixed-duration instrument makes it enter snd_opl3_start_timer(). On sequencer-driver rebind, the card-owned instrument bank remains available. An event delivered as soon as the port appears can race the late initialization: Sequencer driver probe Direct NOTEON ---------------------- ------------- snd_opl3_synth_create_port() snd_seq_event_port_attach() publish port snd_opl3_synth_event_input() snd_opl3_note_on() snd_opl3_start_timer() use tlist and sys_timer_lock timer_setup(tlist) spin_lock_init(sys_timer_lock) sys_timer_status = 0 Initialize the timer, its lock and its status before creating the client and attaching either port. No event can then reach partially initialized timer state or have its newly armed timer reinitialized by the probe. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-1@gmail.com --- sound/drivers/opl3/opl3_seq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index b77d8e8f12fb..2bacefc3201d 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -214,6 +214,11 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) spin_lock_init(&opl3->voice_lock); + /* Initialize the timer before an attached port can receive events. */ + timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + opl3->seq_client = -1; /* allocate new client */ @@ -232,11 +237,6 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) return err; } - /* setup system timer */ - timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); - spin_lock_init(&opl3->sys_timer_lock); - opl3->sys_timer_status = 0; - #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_init_seq_oss(opl3, name); #endif From 02b1d5127f37683448ef35798b3a6310d1d38761 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:50 +0800 Subject: [PATCH 28/71] ALSA: opl3: Clear voice channels before freeing their sets The card-owned OPL3 voice array survives sequencer-driver unbind, but its chan pointers refer to the native or OSS port's channel set. Neither port's private_free callback clears those pointers before freeing its set. A direct NOTEON in the initial SYNTH mode can leave a voice allocated without a subscription. Even with fix_dur zero and no timer, unbind and rebind followed by direct PITCHBEND can read the old channel: Sequencer driver unbind/rebind Direct PITCHBEND ----------------------------- ---------------- snd_seq_delete_kernel_client() port_free() free channel set snd_opl3_seq_probe() allocate new channel set snd_opl3_pitch_ctrl() snd_opl3_update_pitch() old voice->chan is non-NULL snd_opl3_calc_pitch() read freed channel Clearing only note_off_check or the voice state does not protect this path: snd_opl3_update_pitch() tests the channel pointer itself. Key off active voices before clearing their channel pointers, delayed- note flags and state under voice_lock. Direct events need no subscription, so port deletion need not call synth_unuse() to reset the hardware. Merely marking a keyed-on voice off would retain hardware KEYON and cause a later note to miss the key-on transition. Reuse snd_opl3_kill_voice() before either channel set is freed. Visit higher-numbered voices first. In initial SYNTH mode, direct NOTEON events map channels to fixed voices without reserving them through the allocator: a 4-op primary can coexist with an independently keyed-on 2-op partner at voice + 3. Killing the primary clears that partner's software state without keying off its register. Ascending cleanup would then skip the partner. Retiring the partner first guarantees its key-off before the primary can change its state, on both register banks. The lock also excludes a timer or pitch update already using a voice. Both private_free callbacks must do the cleanup because an in-flight OSS event can refill the shared voice array after the native port has been drained and freed. That event can only reference its own still-live set; the OSS callback's final cleanup retires that reference before freeing its set as well. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-2@gmail.com --- sound/drivers/opl3/opl3_midi.c | 15 +++++++++++++++ sound/drivers/opl3/opl3_oss.c | 1 + sound/drivers/opl3/opl3_seq.c | 1 + sound/drivers/opl3/opl3_voice.h | 1 + 4 files changed, 18 insertions(+) diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 6d3c5b5a35ff..de611214cdac 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -632,6 +632,21 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice) } +void snd_opl3_clear_voices(struct snd_opl3 *opl3) +{ + int i; + + guard(spinlock_irqsave)(&opl3->voice_lock); + /* A 4-op primary clears its higher-numbered partner's state. */ + for (i = MAX_OPL3_VOICES - 1; i >= 0; i--) { + if (opl3->voices[i].state > 0) + snd_opl3_kill_voice(opl3, i); + opl3->voices[i].chan = NULL; + opl3->voices[i].note_off_check = 0; + opl3->voices[i].state = SNDRV_OPL3_ST_OFF; + } +} + /* * Release a note in response to a midi note off. */ diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 6d39b2b77b80..1f01d27955fe 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -41,6 +41,7 @@ static void snd_opl3_oss_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->oss_chset); } diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index 2bacefc3201d..e03c88377bb6 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -152,6 +152,7 @@ static void snd_opl3_synth_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->chset); } diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h index be9ccca2d952..aa81512d23c9 100644 --- a/sound/drivers/opl3/opl3_voice.h +++ b/sound/drivers/opl3/opl3_voice.h @@ -13,6 +13,7 @@ int snd_opl3_synth_use_inc(struct snd_opl3 * opl3); void snd_opl3_synth_use_dec(struct snd_opl3 * opl3); int snd_opl3_synth_setup(struct snd_opl3 * opl3); void snd_opl3_synth_cleanup(struct snd_opl3 * opl3); +void snd_opl3_clear_voices(struct snd_opl3 *opl3); /* Prototypes for opl3_midi.c */ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); From 9194d8dd4196fa3fb457de2909df468d501c01c2 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:51 +0800 Subject: [PATCH 29/71] ALSA: opl3: Shut down the timer after draining sequencer ports The delayed-note timer is independent of the sequencer ports' use-locks and runs code in snd-opl3-synth. Deleting the client drains the event callbacks but does not cancel this timer. A direct fixed-duration NOTEON without a subscription can arm it without any unuse callback to stop it. A shutdown in only the native port's private_free callback is too early to finish shared voice cleanup when the OSS port is enabled: Sequencer client deletion In-flight OSS NOTEON ------------------------- ------------------- drain native port clear native voice aliases snd_opl3_oss_event_input() refill shared voice array drain OSS port clear voice aliases again free OSS channel set client deletion returns The per-port voice cleanup prevents the timer from dereferencing a freed channel during that sequence, but a queued or running timer can still outlive client deletion and the sequencer driver itself. Shut the timer down synchronously after snd_seq_delete_kernel_client() has drained both ports. No event can then start it again, and shutdown waits for a callback already running and prevents callback self-rearming. Do this outside voice_lock and sys_timer_lock, which the callback takes. The probe initializes timer state before publishing ports on rebind. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-3@gmail.com --- sound/drivers/opl3/opl3_seq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index e03c88377bb6..1004a7df1b8c 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -259,6 +259,8 @@ static void snd_opl3_seq_remove(struct snd_seq_device *dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } + /* Stop the shared timer after all event callbacks have been drained. */ + timer_shutdown_sync(&opl3->tlist); } static struct snd_seq_driver opl3_seq_driver = { From 6b186281a11194be8349d6b14c3d605efcfaf037 Mon Sep 17 00:00:00 2001 From: Denis Kulikov Date: Mon, 26 Oct 2026 02:18:49 +0300 Subject: [PATCH 30/71] ALSA: usb-audio: Rate-limit URB submission error at stream start When two USB audio devices share a USB 2.0 hub (here a headset and a microphone), there may not be enough isochronous bandwidth to start the second stream, and usb_submit_urb() fails with -ENOSPC. PipeWire keeps retrying to start the stream every ~20 ms, and every attempt logs usb 2-1.1: cannot submit urb 0, error -28: not enough bandwidth On my desktop this was printed up to 94 times per second and flooded the kernel log. Use usb_audio_err_ratelimited() for this message, as commit 2db2be5607c4 ("ALSA: usb-audio: Rate limit usb_set_interface error reporting") already did for a neighbouring message in the same file. Tested on that desktop (X79, Linux 7.3) with the headset and the microphone on one hub, starting playback and capture on both for 20 seconds: the stock snd-usb-audio module printed the message 580 times, the patched one printed it 50 times and suppressed the rest. Signed-off-by: Denis Kulikov Link: https://patch.msgid.link/20261025231849.148149-1-dar1knessx@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 9c844c55ad3f..8dd0b3525e4b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1642,9 +1642,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) err = -ENODEV; if (err < 0) { if (!atomic_read(&ep->chip->shutdown)) - usb_audio_err(ep->chip, - "cannot submit urb %d, error %d: %s\n", - i, err, usb_error_string(err)); + usb_audio_err_ratelimited(ep->chip, + "cannot submit urb %d, error %d: %s\n", + i, err, usb_error_string(err)); goto __error; } set_bit(i, &ep->active_mask); From 5deb9c57ba93d28a52871a96f3d8794d2cbed035 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Mon, 26 Oct 2026 12:16:55 +0900 Subject: [PATCH 31/71] ALSA: pcmtest: Reject invalid fill_mode values The fill_block() function handles only fill_mode 0 (random) and 1 (pattern). With any other value it writes nothing and does not move the buffer position. A capture stream then gets no data, and a read fails with -EIO after the timeout. Nothing in the kernel log says why. Check the value in a set callback, so a bad value is rejected both when the module is loaded and when the parameter is written. Fixes: 315a3d57c64c ("ALSA: Implement the new Virtual PCM Test Driver") Assisted-by: Claude:claude-opus-5-5 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20261026031655.2699561-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/drivers/pcmtest.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index d1b0dff56cd9..422fdb10f237 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -72,13 +72,32 @@ static u8 playback_capture_test; static u8 ioctl_reset_test; static struct dentry *driver_debug_dir; +static int fill_mode_set(const char *val, const struct kernel_param *kp) +{ + short mode; + int err; + + err = kstrtos16(val, 0, &mode); + if (err) + return err; + if (mode != FILL_MODE_RAND && mode != FILL_MODE_PAT) + return -EINVAL; + fill_mode = mode; + return 0; +} + +static const struct kernel_param_ops fill_mode_ops = { + .set = fill_mode_set, + .get = param_get_short, +}; + module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for pcmtest soundcard"); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for pcmtest soundcard"); module_param(enable, bool, 0444); MODULE_PARM_DESC(enable, "Enable pcmtest soundcard."); -module_param(fill_mode, short, 0600); +module_param_cb(fill_mode, &fill_mode_ops, &fill_mode, 0600); MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)"); module_param(inject_delay, int, 0600); MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)"); From 9080b18ad561a495bf74926cbc52e23cae8887c0 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 26 Oct 2026 19:21:37 +0800 Subject: [PATCH 32/71] 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 33/71] 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 34/71] 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 35/71] 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 36/71] 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 37/71] 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 38/71] 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 39/71] 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 40/71] 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 41/71] 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 42/71] 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 43/71] 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 44/71] 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 45/71] 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 46/71] 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 47/71] 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 48/71] 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 49/71] 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 50/71] 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 51/71] 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 52/71] 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 53/71] 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 54/71] 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 06be918062adc1a5a76dc346803b136b20bdb5ae Mon Sep 17 00:00:00 2001 From: Sinan Turunc Date: Mon, 26 Oct 2026 22:36:39 +0000 Subject: [PATCH 55/71] ALSA: usb-audio: Bound packet copies by the PCM buffer size retire_capture_urb() and prepare_playback_urb() assume that one USB packet never carries more data than the whole ALSA ring buffer. Nothing enforces that: the packet size comes from the device (wMaxPacketSize, and curpacksize == maxpacksize when the class-specific endpoint descriptor sets MaxPacketsOnly), while the PCM hardware constraints only require 64-byte periods, two periods and a period time of one data interval. An 8 kHz mono U8 stream on a 128-byte buffer is legal, and a full-speed device can send 1023-byte packets into it. On the first oversized packet hwptr_done is wrapped once and stays beyond buffer_bytes (895 for 1023 bytes into 128). On the next packet the wrap arithmetic in the copy path computes bytes1 = subs->buffer_bytes - oldptr with oldptr > buffer_bytes, so bytes1 wraps to about 4 GB and memcpy() runs off the end of the URB buffer and of the vmalloc'd DMA buffer: BUG: KASAN: slab-out-of-bounds in retire_capture_urb+0x251/0x870 Read of size 4294966529 at addr ffff88800c1e2800 by task vhci_rx/82 and for playback, where copy_to_urb() has the same shape: BUG: KASAN: vmalloc-out-of-bounds in copy_to_urb.isra.0+0x188/0x430 Read of size 4294966529 at addr ffffc9000004537f by task vhci_rx/79 prepare_playback_urb+0x98a/0x1fc0 Truncate an oversized capture packet to the buffer size, and stop filling a playback URB once it holds a whole buffer. With bytes <= buffer_bytes the single wrap of hwptr_done and the two-part memcpy() in both copy paths stay inside the buffer. A device whose packets do not fit the buffer at all was never going to play correctly anyway. Reproduced on 7.3-rc7 with a USB/IP device (dummy_hcd cannot carry isochronous data) advertising a UAC1 endpoint with MaxPacketsOnly and wMaxPacketSize 1023, and a PCM client using period_size 64, periods 2. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sinan Turunc Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261026223543.620-2-enscrpt@pm.me --- sound/usb/pcm.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 682b6c1fe76b..c2f7b08607ac 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1343,6 +1343,18 @@ static void retire_capture_urb(struct snd_usb_substream *subs, bytes -= adj; subs->stream_offset_adj -= adj; } + /* + * A packet can be as large as the endpoint's maxpacksize, which + * the device chooses, while the ring buffer can be as small as + * two 64-byte periods. The hwptr_done wrap below handles one + * wrap only, so never take more than one buffer from a packet. + */ + if (unlikely(bytes > subs->buffer_bytes)) { + dev_warn_ratelimited(&subs->dev->dev, + "%u-byte packet exceeds %u-byte buffer, truncating\n", + bytes, subs->buffer_bytes); + bytes = subs->buffer_bytes; + } frames = bytes / stride; if (!subs->txfr_quirk) bytes = frames * stride; @@ -1553,8 +1565,14 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, for (i = 0; i < ctx->packets; i++) { counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); + /* + * Bound by the ring buffer as well as by the URB: + * copy_to_urb() wraps hwptr_done once, so one URB must + * never carry more than the whole buffer. + */ if (counts < 0 || - (frames + counts) * stride > ctx->buffer_size) + (frames + counts) * stride > ctx->buffer_size || + (frames + counts) * stride > subs->buffer_bytes) break; /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; From 493a77a9f20fca07ca5654f61c1b03bef035df25 Mon Sep 17 00:00:00 2001 From: Sinan Turunc Date: Mon, 26 Oct 2026 22:36:47 +0000 Subject: [PATCH 56/71] ALSA: ua101: Bound packet copies by the PCM buffer size copy_capture_data() and copy_playback_data() wrap the ring buffer position once and split the memcpy() in two at the buffer end, which is only correct when one packet holds at most buffer_size frames. The capture packet size is the device's (actual_length up to wMaxPacketSize, divided by 3 * bNrChannels from its own descriptor), the playback packet size is taken from the capture packets, and set_stream_hw() lets the PCM client pick period_bytes_min 1, periods_min 2 and a period time of 1.5 packet intervals: 9 frames at 44.1 kHz high speed, an 18-frame buffer. A device sending 672-byte mono 24-bit packets delivers 224 frames per packet. After the first packet buffer_pos is 206 and stays beyond buffer_size. On the next packet frames1 = buffer_size - buffer_pos wraps and memcpy() runs away: snd-ua101 2-1:1.0: invalid position: , pos = 206, buffer size = 18, period size = 9 BUG: KASAN: slab-out-of-bounds in capture_urb_complete+0x9eb/0xcb0 Read of size 4294966732 at addr ffff88800d73e7e0 by task vhci_rx/81 and for playback: BUG: KASAN: vmalloc-out-of-bounds in playback_work+0x789/0xac0 Read of size 4294966732 at addr ffffc9000002626a by task kworker/0:1H/80 Clamp the capture copy to buffer_size. On playback, copy at most one buffer and fill the rest of the packet with silence, so the URB length, the delay accounting and the data sent stay consistent. With that bound the single wrap and both memcpy() halves stay inside the buffer. A device whose packets do not fit the buffer never played correctly anyway. Reproduced on 7.3-rc7 with a USB/IP device presenting the UA-101 interface layout and a 672-byte isochronous endpoint. Fixes: 63978ab3e3e9 ("sound: add Edirol UA-101 support") Cc: stable@vger.kernel.org Signed-off-by: Sinan Turunc Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261026223543.620-3-enscrpt@pm.me --- sound/usb/misc/ua101.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 58d8a3cdc8b9..bd8e0ed0ba5d 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -285,13 +285,29 @@ static void playback_work(struct work_struct *work) /* fill packet with data or silence */ urb->iso_frame_desc[0].length = frames * ua->playback.frame_bytes; - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { + /* + * The packet size comes from the capture stream, + * i.e. from the device, while the ring buffer can + * be as small as two tiny periods. The single wrap + * in copy_playback_data() is only valid for + * frames <= buffer_size, so copy at most one + * buffer and send silence for the rest. + */ + unsigned int copy_frames = min(frames, + ua->playback.substream->runtime->buffer_size); + do_period_elapsed |= copy_playback_data(&ua->playback, urb, - frames); - else + copy_frames); + memset(urb->transfer_buffer + + copy_frames * ua->playback.frame_bytes, 0, + (frames - copy_frames) * + ua->playback.frame_bytes); + } else { memset(urb->transfer_buffer, 0, urb->iso_frame_desc[0].length); + } /* and off you go ... */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -320,6 +336,13 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, runtime = stream->substream->runtime; frame_bytes = stream->frame_bytes; + /* + * A packet can carry up to max_packet_bytes, the device's + * wMaxPacketSize, which may exceed the whole ring buffer; keep what + * fits so the single wrap below stays valid. + */ + if (unlikely(frames > runtime->buffer_size)) + frames = runtime->buffer_size; dest = runtime->dma_area + stream->buffer_pos * frame_bytes; if (stream->buffer_pos + frames <= runtime->buffer_size) { memcpy(dest, urb->transfer_buffer, frames * frame_bytes); From fe3b45a0875a8b0944d2adee3ca62283d876edc0 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 27 Oct 2026 10:22:27 +0900 Subject: [PATCH 57/71] ALSA: pcmtest: Honor the enable parameter The enable module parameter is declared and documented as "Enable pcmtest soundcard.", but nothing reads it. Loading the module with enable=0 still creates the card. Return -ENODEV from mod_init() when enable is false, as the pcsp driver does. Fixes: 315a3d57c64c ("ALSA: Implement the new Virtual PCM Test Driver") Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20261027012228.3797565-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/drivers/pcmtest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 422fdb10f237..66bee8a2ce34 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -712,6 +712,9 @@ static int __init mod_init(void) { int err = 0; + if (!enable) + return -ENODEV; + buf_allocated = setup_patt_bufs(); if (!buf_allocated) return -ENOMEM; From 8d61d55bedb7e1ee553ec3f43a9dc0feecb9da3c Mon Sep 17 00:00:00 2001 From: Ernst Persson Date: Tue, 27 Oct 2026 07:13:44 +0100 Subject: [PATCH 58/71] ALSA: hda/realtek: Extend quirk for HP Pavilion All-in-One 27-ca1xxx A quirk was added in commit 704de5697008 ("ALSA: hda/realtek: Add quirk for HP Pavilion All-in-One 27-ca1xxx") Also enable the quirk for the actual 89e9 model named there. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216139 Signed-off-by: Ernst Persson Link: https://patch.msgid.link/20261027-realtek-alc274-v1-1-c0eae15d0c55@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + sound/hda/controllers/intel.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c499165b80e9..23329d5887a5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7876,6 +7876,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index af01b2669c49..29679fe7f401 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2330,6 +2330,7 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */ SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP 89E9", 0), {} }; From 379ba557f555245eb9cb6c2a9d7a1eef704c786f Mon Sep 17 00:00:00 2001 From: Zak Brighton Knight Date: Mon, 26 Oct 2026 14:09:34 +0000 Subject: [PATCH 59/71] ALSA: usb-audio: Skip probe-time interface setup for MSI MAG X570S ALC4080 The Realtek ALC4080 USB audio on the MSI MAG X570S Tomahawk Max WiFi (USB ID 0db0:a073, the same ID as the MAG X570S Torpedo Max mixer map) has six streaming interfaces with 68 altsettings in total. For each of them, __snd_usb_parse_audio_interface() switches the interface to alt 0, initialises pitch and the sample rate and switches back, which costs about 64 ms per altsetting here, mostly spent inside usb_set_interface(). Probing the card therefore takes about 6.4 seconds, on every boot. That setup is redundant with snd_usb_endpoint_prepare() at stream-open time, so set QUIRK_FLAG_SKIP_IFACE_SETUP for this device. Tested on 7.2.11 with the equivalent quirk_flags=0db0:a073:skip_iface_setup module option, re-probing the device via its sysfs "authorized" attribute: probe time went from 6.45 s to 0.57 s, all 68 altsettings are still listed in /proc/asound/cardX/stream*, and playback and capture work. Assisted-by: LLM Signed-off-by: Zak Brighton Knight Link: https://patch.msgid.link/20261026140934.3575940-1-zakbrightonknight@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index b6be3a80d9c9..4ec1f2a0b359 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2661,6 +2661,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0db0, 0xa073, /* MSI MAG X570S Tomahawk/Torpedo Max (ALC4080) */ + QUIRK_FLAG_SKIP_IFACE_SETUP), DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ From a09c2f3b267e47767b8937f65930567edbb88d44 Mon Sep 17 00:00:00 2001 From: Sally Marcher Date: Mon, 26 Oct 2026 14:29:34 +0100 Subject: [PATCH 60/71] ALSA: usb-audio: Add correct datainterval in the quirks-table for Microbook II This patch fixes playback issues on the Microbook II. The correct value was in the older driver for this device (https://github.com/manuel-rhdt/snd-microbookii/blob/dd304c097bb6a9d52276ac29c6969fa02bc7674a/audio.c#L914). Datainterval needs to be set explicitly. Fixes: a634090a0f24 ("ALSA: usb-audio: Add quirk for MOTU MicroBook II") Signed-off-by: Sally Marcher Link: https://lore.kernel.org/d5dfd044-69f6-4fc3-9884-6599d88afb55@student.tugraz.at Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 6343a50fb273..18a3e66faeec 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3005,6 +3005,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, @@ -3027,6 +3028,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, From 31ce8c24dfc8210e80ff610b35e5465cf2066323 Mon Sep 17 00:00:00 2001 From: Shawn Rainey Date: Fri, 23 Oct 2026 08:25:11 -0400 Subject: [PATCH 61/71] ALSA: usb-audio: add quirk for Novation UltraNova (1235:0011) The device reports vendor-specific class on all interfaces, so the standard parser rejects it and no card is created. Add a composite quirk with fixed audio and raw-MIDI layouts. The id table entry matches interface 0 only so that the vendor control interfaces (2/3) are left unbound for userspace access. The endpoints advertise no synchronization and the device provides no feedback; both streams are locked to the USB SOF. The playback entry is marked USB_ENDPOINT_SYNC_SYNC so that generic sync-EP detection does not pair the capture data endpoint (0x82) as implicit feedback for the output stream; the capture entry needs no override, as its .ep_idx = 1 already keeps it out of sync-EP detection. Verified on hardware: audio duplex at both supported rates, raw MIDI in/out, and complete patch dumps as SysEx on the MIDI endpoints. Signed-off-by: Shawn Rainey Link: https://patch.msgid.link/20261023122513.1025684-1-rainey.shawn+kaudio@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 18a3e66faeec..4793fd1f85fe 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2359,6 +2359,69 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * Novation UltraNova: vendor-specific class descriptors, fixed + * audio and MIDI layouts. Only the audio and MIDI interfaces + * (0 and 1) are grabbed; the vendor control interfaces (2/3) + * are left unbound for userspace access. + */ + USB_DEVICE_INTERFACE_NUMBER(0x1235, 0x0011, 0), + QUIRK_DRIVER_INFO { + .vendor_name = "Novation", + .product_name = "UltraNova", + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* playback */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 392, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* capture */ + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x82, + .ep_idx = 1, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .maxpacksize = 294, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { QUIRK_DATA_RAW_BYTES(1) }, + QUIRK_COMPOSITE_END + } + } +}, { USB_DEVICE(0x1235, 0x001e), QUIRK_DRIVER_INFO { From a4311e9447bbb219044db38897f95be112f46a8a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 09:28:35 +0200 Subject: [PATCH 62/71] ALSA: hda: Reject codec reset on ASoC (ext_ops) buses Writing to the reconfig or clear sysfs nodes of a codec handled by ASoC (SOF via hdac_hda, AVS via codecs/hda) hangs the writer. snd_hda_codec_reset() sets card->shutdown and calls device_release_driver(). With ext_ops the driver unbind unregisters the ASoC component, which unbinds and frees the whole ASoC card: - snd_card_disconnect() returns early as card->shutdown is already set, so the card's child devices are not removed and snd_card_free() waits for the card release forever. - Even without that, freeing the card removes the codec's hwdep device, which owns the sysfs node the reset was issued from, deadlocking in kernfs while waiting for the active write to finish. The legacy HDA driver keeps the card across a driver rebind, ASoC does not, so the reset cannot be supported there. Return -EOPNOTSUPP and skip the misleading "codec is being used" messages in the callers. Fixes: 6bae5ea94989 ("ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers") Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=222182 Signed-off-by: Peter Ujfalusi Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20261027072835.4796-1-peter.ujfalusi@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/common/codec.c | 6 ++++++ sound/hda/common/sysfs.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 31e5fad78b0f..e367cc1c6e78 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1807,6 +1807,12 @@ int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; + /* ASoC: driver unbind frees the card, incl. the hwdep issuing the reset */ + if (bus->core.ext_ops) { + codec_err(codec, "Codec reset is not supported with ASoC\n"); + return -EOPNOTSUPP; + } + if (snd_hda_lock_devices(bus) < 0) return -EBUSY; diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c index bedf10b30885..bf65223fde10 100644 --- a/sound/hda/common/sysfs.c +++ b/sound/hda/common/sysfs.c @@ -117,6 +117,8 @@ static int clear_codec(struct hda_codec *codec) int err; err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't free.\n"); return err; @@ -132,6 +134,8 @@ static int reconfig_codec(struct hda_codec *codec) CLASS(snd_hda_power, pm)(codec); codec_info(codec, "hda-codec: reconfiguring\n"); err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't reconfigure.\n"); From 55df18877471ade98ad0a1c12c95f78ba54ec5cc Mon Sep 17 00:00:00 2001 From: Filippo Falezza Date: Tue, 27 Oct 2026 01:33:56 +0000 Subject: [PATCH 63/71] 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 64/71] 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 65/71] 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 66/71] 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 67/71] 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 68/71] 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 69/71] 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 70/71] 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 71/71] 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);