From 7d3fb78b550301e43fdc60312aed733069694426 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 4 Jun 2026 11:12:44 +0100 Subject: ASoC: wm_adsp: Fix NULL dereference when removing firmware controls In wm_adsp_control_remove() check that the priv pointer is not NULL before attempting to cleanup what it points to. When cs_dsp creates a control it calls wm_adsp_control_add_cb() so that wm_adsp can create its own private control data. There are two cases where private data is not created: 1. The control is a SYSTEM control, so an ALSA control is not created. 2. The codec driver has registered a control_add() callback that hides the control, so wm_adsp_control_add() is not called. When cs_dsp_remove destroys its control list it calls wm_adsp_control_remove() for each control. But wm_adsp_control_remove() was attempting to cleanup the private data pointed to by cs_ctl->priv without checking the pointer for NULL. Signed-off-by: Richard Fitzgerald Fixes: 0700bc2fb94c ("ASoC: wm_adsp: Separate generic cs_dsp_coeff_ctl handling") Link: https://patch.msgid.link/20260604101244.1402862-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index a637e22c3929..ca630c9948e4 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -679,6 +679,9 @@ static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) { struct wm_coeff_ctl *ctl = cs_ctl->priv; + if (!ctl) + return; + cancel_work_sync(&ctl->work); kfree(ctl->name); -- cgit v1.2.3 From 6042c91df60e825625bc7d5c5c3b5a87b91d5805 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 9 Jun 2026 21:38:44 +0530 Subject: ASoC: SOF: amd: fix for ipc flags check Firmware will set dsp_ack to 1 when firmware sends response for the IPC command issued by host. Similarly dsp_msg flag will be updated to 1. During ACP D0 entry, the value read from the sof_dsp_ack_write scratch flag can be uninitialized. A non-zero garbage value is treated as a pending DSP IPC ack before SOF_FW_BOOT_COMPLETE, causing a spurious "IPC reply before FW_BOOT_COMPLETE" log. Fix the condition checks for ipc flags. Fixes: 738a2b5e2cc9 ("ASoC: SOF: amd: Add IPC support for ACP IP block") Link: https://github.com/thesofproject/linux/pull/5642 Signed-off-by: Vijendar Mukunda Tested-by: Umang Jain Link: https://patch.msgid.link/20260609160938.3717513-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-ipc.c | 4 ++-- sound/soc/sof/amd/acp.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 3cd4674dd800..94025bc799ea 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -181,14 +181,14 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) } dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write); - if (dsp_msg) { + if (dsp_msg == ACP_DSP_MSG_SET) { snd_sof_ipc_msgs_rx(sdev); acp_dsp_ipc_host_done(sdev); ipc_irq = true; } dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write); - if (dsp_ack) { + if (dsp_ack == ACP_DSP_ACK_SET) { if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { guard(spinlock_irq)(&sdev->ipc_lock); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 2b7ea8c64106..7bcb76676a98 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -116,6 +116,8 @@ #define ACP_SRAM_PAGE_COUNT 128 #define ACP6X_SDW_MAX_MANAGER_COUNT 2 #define ACP70_SDW_MAX_MANAGER_COUNT ACP6X_SDW_MAX_MANAGER_COUNT +#define ACP_DSP_MSG_SET 1 +#define ACP_DSP_ACK_SET 1 enum clock_source { ACP_CLOCK_96M = 0, -- cgit v1.2.3 From 25b17c06040fae60518b4ff9c46f2bb12285d538 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 9 Jun 2026 21:38:45 +0530 Subject: ASoC: SOF: amd: set ipc flags to zero As per design, set IPC conf structure flags to zero during acp init sequence. Link: https://github.com/thesofproject/linux/pull/5642 Signed-off-by: Vijendar Mukunda Tested-by: Umang Jain Link: https://patch.msgid.link/20260609160938.3717513-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index f615b8d1c802..e6af8927baa0 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -377,6 +377,33 @@ void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]); } +static int acp_init_scratch_mem_ipc_flags(struct snd_sof_dev *sdev) +{ + u32 dsp_msg_write, dsp_ack_write, host_msg_write, host_ack_write; + + dsp_msg_write = sdev->debug_box.offset + + offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); + dsp_ack_write = sdev->debug_box.offset + + offsetof(struct scratch_ipc_conf, sof_dsp_ack_write); + host_msg_write = sdev->debug_box.offset + + offsetof(struct scratch_ipc_conf, sof_host_msg_write); + host_ack_write = sdev->debug_box.offset + + offsetof(struct scratch_ipc_conf, sof_host_ack_write); + /* Initialize host message write flag */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg_write, 0); + + /* Initialize host ack write flag */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_ack_write, 0); + + /* Initialize DSP message write flag */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write, 0); + + /* Initialize DSP ack write flag */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write, 0); + + return 0; +} + static int acp_memory_init(struct snd_sof_dev *sdev) { struct acp_dev_data *adata = sdev->pdata->hw_pdata; @@ -384,6 +411,7 @@ static int acp_memory_init(struct snd_sof_dev *sdev) snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_CNTL_OFFSET, ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK); + acp_init_scratch_mem_ipc_flags(sdev); init_dma_descriptor(adata); return 0; -- cgit v1.2.3 From 0e152e4126fa14e697d8514cdd1567b18c679f08 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 4 Jun 2026 20:58:15 +0800 Subject: ASoC: amd: yc: Add DMI quirk for ASUS EXPERTBOOK PM1403CDA Add a DMI quirk for the ASUS EXPERTBOOK PM1403CDA fixing the issue where the internal microphone was not detected. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221608 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260604125815.42297-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index b426cda529a9..ce229f5d0d46 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -808,6 +808,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "MS-17LN"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "PM1403CDA"), + } + }, {} }; -- cgit v1.2.3 From 6ad3914e06a48a02e362d0df2b1073c7c567c93d Mon Sep 17 00:00:00 2001 From: Li Jun Date: Thu, 11 Jun 2026 09:00:45 +0800 Subject: ASoC: loongson: Fix invalid position error in ls_pcm_pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "invalid position" error occurred when the DMA position descriptor returned an invalid address value (e.g., pos = -1048838144). This happened because the `bytes_to_frames()` function returns a signed value, but when `addr < runtime->dma_addr`, the subtraction produces a negative result that gets interpreted as a large unsigned integer in comparisons. when the addr is abnormal, for example,the DMA controller is abnormal in hardware,x=0 should not be a point(x == runtime->buffer_size),but a range, which includes the addr address being less than runtime ->dma1-adr, and the addr exceeding the DMA address range.the value of pos should not better a negative,return 0, maybe better. [ 32.834431][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 32.845019][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 32.855588][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 32.866145][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 32.995394][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 33.006025][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 [ 33.016748][ 2] soc-audio soc-audio: invalid position: , pos = -1048838144 Signed-off-by: Li Jun [Remove XRUN reporting I'd mistakenly avised adding on prior review -- broonie] Link: https://patch.msgid.link/20260611010045.3668574-1-lijun01@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_dma.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/loongson/loongson_dma.c b/sound/soc/loongson/loongson_dma.c index a149b643175c..f3ed14a48bd5 100644 --- a/sound/soc/loongson/loongson_dma.c +++ b/sound/soc/loongson/loongson_dma.c @@ -199,6 +199,7 @@ loongson_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct device *dev = substream->pcm->card->dev; struct loongson_runtime_data *prtd = runtime->private_data; struct loongson_dma_desc *desc; snd_pcm_uframes_t x; @@ -207,9 +208,16 @@ loongson_pcm_pointer(struct snd_soc_component *component, desc = dma_desc_save(prtd); addr = ((u64)desc->saddr_hi << 32) | desc->saddr; - x = bytes_to_frames(runtime, addr - runtime->dma_addr); - if (x == runtime->buffer_size) + if (addr < runtime->dma_addr || + addr > runtime->dma_addr + runtime->dma_bytes) { + dev_warn(dev, "WARNING! dma_addr:0x%llx\n", addr); x = 0; + } else { + x = bytes_to_frames(runtime, addr - runtime->dma_addr); + if (x == runtime->buffer_size) + x = 0; + } + return x; } -- cgit v1.2.3 From e4c60a1d4b6ccc66aefb3789cd908d4f9482eefd Mon Sep 17 00:00:00 2001 From: Kean Ren Date: Thu, 11 Jun 2026 10:37:57 +0800 Subject: ASoC: SDCA: fix NULL pointer dereference in sdca_dev_unregister_functions sdca_dev_unregister_functions() iterates over all SDCA function descriptors and calls sdca_dev_unregister() on each func_dev without checking for NULL. When a function registration has failed partway through, or the device cleanup races with probe deferral, func_dev entries may be NULL, leading to a kernel oops: BUG: kernel NULL pointer dereference, address: 0000000000000040 RIP: 0010:device_del+0x1e/0x3e0 Call Trace: sdca_dev_unregister_functions+0x37/0x60 [snd_soc_sdca] release_nodes+0x35/0xb0 devres_release_all+0x90/0x100 device_unbind_cleanup+0xe/0x80 device_release_driver_internal+0x1c1/0x200 bus_remove_device+0xc6/0x130 device_del+0x161/0x3e0 device_unregister+0x17/0x60 sdw_delete_slave+0xb6/0xd0 [soundwire_bus] sdw_bus_master_delete+0x1e/0x50 [soundwire_bus] ... sof_probe_work+0x19/0x30 [snd_sof] This was observed on a Lenovo ThinkPad X1 Carbon G14 (Panther Lake) with the SOF audio driver probe failing due to missing Panther Lake firmware, causing the subsequent cleanup of SoundWire devices to trigger the crash. Fix this with three changes: 1) Add a NULL guard in sdca_dev_unregister() so that callers do not need to pre-validate the pointer (defense in depth). 2) In sdca_dev_unregister_functions(), skip NULL func_dev entries and clear func_dev to NULL after unregistration, making the function idempotent and safe against double-invocation. 3) In sdca_dev_register_functions(), roll back all previously registered functions when a later one fails, so the function array is never left in a partially-populated state. Fixes: 4496d1c65bad ("ASoC: SDCA: add function devices") Signed-off-by: Kean Ren Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260611023757.1553960-1-rh_king@163.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_function_device.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c index feacfbc6a518..b5ca98283a88 100644 --- a/sound/soc/sdca/sdca_function_device.c +++ b/sound/soc/sdca/sdca_function_device.c @@ -82,6 +82,9 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, static void sdca_dev_unregister(struct sdca_dev *sdev) { + if (!sdev) + return; + auxiliary_device_delete(&sdev->auxdev); auxiliary_device_uninit(&sdev->auxdev); } @@ -90,14 +93,24 @@ int sdca_dev_register_functions(struct sdw_slave *slave) { struct sdca_device_data *sdca_data = &slave->sdca_data; int i; + int ret; for (i = 0; i < sdca_data->num_functions; i++) { struct sdca_dev *func_dev; func_dev = sdca_dev_register(&slave->dev, &sdca_data->function[i]); - if (IS_ERR(func_dev)) - return PTR_ERR(func_dev); + if (IS_ERR(func_dev)) { + ret = PTR_ERR(func_dev); + /* + * Unregister functions that were successfully + * registered before this failure. This also + * sets func_dev to NULL so the caller will not + * try to unregister them again. + */ + sdca_dev_unregister_functions(slave); + return ret; + } sdca_data->function[i].func_dev = func_dev; } @@ -111,7 +124,12 @@ void sdca_dev_unregister_functions(struct sdw_slave *slave) struct sdca_device_data *sdca_data = &slave->sdca_data; int i; - for (i = 0; i < sdca_data->num_functions; i++) + for (i = 0; i < sdca_data->num_functions; i++) { + if (!sdca_data->function[i].func_dev) + continue; + sdca_dev_unregister(sdca_data->function[i].func_dev); + sdca_data->function[i].func_dev = NULL; + } } EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA"); -- cgit v1.2.3