From be4c9abdf04b86bb33f4a2ce59fd7cb64a739c51 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Fri, 21 Nov 2025 09:57:12 +0800 Subject: [PATCH 01/55] ALSA: hda/realtek: add quirk for HP pavilion aero laptop 13z-be200 The laptop uses ALC287 chip (as shown in /proc/asound/card1/codec#0). It seems that every HP pavilion laptop in the table uses the same quirk, so I just copied them. I have verified that the mute LED on my laptop works with this patch. For reference, here's the alsa-info of my laptop: https://alsa-project.org/db/?f=2d5f297087708610bc01816ab12052abdd4a17c0 Signed-off-by: Jacob Zhong Link: https://patch.msgid.link/tencent_E2DFA33EFDF39E0517A94FA8FF06C05C0709@qq.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 269b6c1e3b6d05..b18777c09d9c4d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6572,6 +6572,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bd6, "HP Pavilion Aero Laptop 13z-be200", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), From d26e9f669cc0a6a85cf17180c09a6686db9f4002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Tue, 25 Nov 2025 15:41:49 +0100 Subject: [PATCH 02/55] ALSA: usb-audio: fix uac2 clock source at terminal parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 8b3a087f7f65 ("ALSA: usb-audio: Unify virtual type units type to UAC3 values") usb-audio is using UAC3_CLOCK_SOURCE instead of bDescriptorSubtype, later refactored with e0ccdef9265 ("ALSA: usb-audio: Clean up check_input_term()") into parse_term_uac2_clock_source(). This breaks the clock source selection for at least my 1397:0003 BEHRINGER International GmbH FCA610 Pro. Fix by using UAC2_CLOCK_SOURCE in parse_term_uac2_clock_source(). Fixes: 8b3a087f7f65 ("ALSA: usb-audio: Unify virtual type units type to UAC3 values") Signed-off-by: René Rebe Link: https://patch.msgid.link/20251125.154149.1121389544970412061.rene@exactco.de Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 72b900505d2c39..3af71d42b9b9aa 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -921,7 +921,7 @@ static int parse_term_uac2_clock_source(struct mixer_build *state, { struct uac_clock_source_descriptor *d = p1; - term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ + term->type = UAC2_CLOCK_SOURCE << 16; /* virtual type */ term->id = id; term->name = d->iClockSource; return 0; From d041e5e748f926398ad96436a86667dea8b591e0 Mon Sep 17 00:00:00 2001 From: Ilyas Gasanov Date: Wed, 26 Nov 2025 03:54:41 +0400 Subject: [PATCH 03/55] ALSA: hda/realtek: Add quirk for HP ProBook 450 G8 My laptop, HP ProBook 450 G8 (32M40EA), has Realtek ALC236 codec on its integrated sound card, and uses GPIO pins 0x2 and 0x1 for speaker mute and mic mute LEDs correspondingly, as found out by me through hda-verb invocations. This matches the GPIO masks used by the alc236_fixup_hp_gpio_led() function. PCI subsystem vendor and device IDs happen to be 0x103c and 0x8a75, which has not been covered in the ALC2xx driver code yet. Signed-off-by: Ilyas Gasanov Link: https://patch.msgid.link/20251125235441.53629-1-public@gsnoff.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 b18777c09d9c4d..b45fcc9a3785e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6525,6 +6525,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, 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), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), From 324f3e03e8a85931ce0880654e3c3eb38b0f0bba Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Fri, 28 Nov 2025 12:06:31 +0800 Subject: [PATCH 04/55] ALSA: dice: fix buffer overflow in detect_stream_formats() The function detect_stream_formats() reads the stream_count value directly from a FireWire device without validating it. This can lead to out-of-bounds writes when a malicious device provides a stream_count value greater than MAX_STREAMS. Fix by applying the same validation to both TX and RX stream counts in detect_stream_formats(). Reported-by: Yuhao Jiang Reported-by: Junrui Luo Fixes: 58579c056c1c ("ALSA: dice: use extended protocol to detect available stream formats") Cc: stable@vger.kernel.org Reviewed-by: Takashi Sakamoto Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB7881B043FC68B4C0DA40B73DAFDCA@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-extension.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c index 02f4a8318e38e7..48bfb3ad93ce55 100644 --- a/sound/firewire/dice/dice-extension.c +++ b/sound/firewire/dice/dice-extension.c @@ -116,7 +116,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) break; base_offset += EXT_APP_STREAM_ENTRIES; - stream_count = be32_to_cpu(reg[0]); + stream_count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); err = read_stream_entries(dice, section_addr, base_offset, stream_count, mode, dice->tx_pcm_chs, @@ -125,7 +125,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) break; base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE; - stream_count = be32_to_cpu(reg[1]); + stream_count = min_t(unsigned int, be32_to_cpu(reg[1]), MAX_STREAMS); err = read_stream_entries(dice, section_addr, base_offset, stream_count, mode, dice->rx_pcm_chs, From ef5e0a02d842b2c6dfcfd9b80feb185769b892ef Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Mon, 1 Dec 2025 20:41:40 +0530 Subject: [PATCH 05/55] ALSA: rawmidi: Fix inconsistent indenting warning reported by smatch Fix smatch reported inconsistent indenting warning in rawmidi. sound/core/rawmidi.c:2115 alsa_rawmidi_init() warn: inconsistent indenting. No functional changes were introduced. Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251201151137.29536-4-hariconscious@gmail.com Signed-off-by: Takashi Iwai --- sound/core/rawmidi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 20d36a346ccab2..8969ee2757f158 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -2106,13 +2106,11 @@ EXPORT_SYMBOL(snd_rawmidi_set_ops); static int __init alsa_rawmidi_init(void) { - snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl); #ifdef CONFIG_SND_OSSEMUL - { int i; /* check device map table */ - for (i = 0; i < SNDRV_CARDS; i++) { + for (int i = 0; i < SNDRV_CARDS; i++) { if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n", i, midi_map[i]); @@ -2124,7 +2122,6 @@ static int __init alsa_rawmidi_init(void) amidi_map[i] = 1; } } - } #endif /* CONFIG_SND_OSSEMUL */ return 0; } From a30fa812222278ba8484f1817ea772960e3038c3 Mon Sep 17 00:00:00 2001 From: Chris Chiu Date: Tue, 2 Dec 2025 04:25:34 +0000 Subject: [PATCH 06/55] ALSA: hda/realtek: fix mute/micmute LEDs don't work for more HP laptops More HP laptops with codec ALC3247 need the extra coefbit2 to make mic-mute/audio-mute working. Signed-off-by: Chris Chiu Link: https://patch.msgid.link/20251202042534.174755-1-chris.chiu@canonical.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 1a8fb43887b318..4efc16ae6b5deb 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6768,13 +6768,23 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8eb7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8eb8, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8ec1, "HP 200 G2i", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ec4, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ec5, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ece, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ecf, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ed2, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8ed5, "HP EliteBook 8 Flip G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8ed6, "HP EliteBook 8 G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8ed7, "HP EliteBook 8 G2i 14", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8ed8, "HP EliteBook 8 G2i 16", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8ed9, "HP ZBook Firefly 14W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8eda, "HP ZBook Firefly 16W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), From cf5425e207351c962990da6a69aebec5948cc1f3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Dec 2025 08:08:27 +0100 Subject: [PATCH 07/55] ALSA: usb-audio: Simplify with usb_endpoint_max_periodic_payload() Recently we received a new helper function, usb_endpoint_max_periodic_payload(), for calculating the max packet size for periodic transfer. Simplify the former open code with the new helper function. Fixes: a748e1dbb2df ("ALSA: usb-audio: Fix max bytes-per-interval calculation") Suggested-by: Michal Pecio Link: https://lore.kernel.org/20251124210518.90054-1-dylan_robinson@motu.com Link: https://patch.msgid.link/20251202070828.145656-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/stream.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 074a61215de6b2..ec7d756d78d178 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -684,43 +684,13 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, return NULL; } -static unsigned int -snd_usb_max_bytes_per_interval(struct snd_usb_audio *chip, - struct usb_host_interface *alts) -{ - struct usb_host_endpoint *ep = &alts->endpoint[0]; - unsigned int max_bytes = usb_endpoint_maxp(&ep->desc); - - /* SuperSpeed isoc endpoints have up to 16 bursts of up to 3 packets each */ - if (snd_usb_get_speed(chip->dev) >= USB_SPEED_SUPER) { - int burst = 1 + ep->ss_ep_comp.bMaxBurst; - int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); - max_bytes *= burst; - max_bytes *= mult; - } - - if (snd_usb_get_speed(chip->dev) == USB_SPEED_SUPER_PLUS && - USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) { - max_bytes = le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); - } - - /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */ - if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH) { - if (usb_endpoint_is_hs_isoc_double(chip->dev, ep)) - max_bytes = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval); - else - max_bytes *= usb_endpoint_maxp_mult(&ep->desc); - } - - return max_bytes; -} - static struct audioformat * audio_format_alloc_init(struct snd_usb_audio *chip, struct usb_host_interface *alts, int protocol, int iface_no, int altset_idx, int altno, int num_channels, int clock) { + struct usb_host_endpoint *ep = &alts->endpoint[0]; struct audioformat *fp; fp = kzalloc(sizeof(*fp), GFP_KERNEL); @@ -734,7 +704,7 @@ audio_format_alloc_init(struct snd_usb_audio *chip, fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->protocol = protocol; - fp->maxpacksize = snd_usb_max_bytes_per_interval(chip, alts); + fp->maxpacksize = usb_endpoint_max_periodic_payload(chip->dev, ep); fp->channels = num_channels; fp->clock = clock; INIT_LIST_HEAD(&fp->list); From 22a03ca7c20c4ed3a75047709b0ad15160e29d58 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Dec 2025 10:34:17 +0100 Subject: [PATCH 08/55] ASoC: codecs: wcd937x: fix OF node leaks on probe failure The component match entry release function will drop the references taken while looking up the soundwire OF nodes when the platform device is unbound. Drop the additional references taken to avoid leaking them on probe failure (e.g. probe deferral) and on driver unbind. Fixes: 9be3ec196da4 ("ASoC: codecs: wcd937x: add wcd937x codec driver") Cc: Prasad Kumpatla Cc: Mohammad Rafi Shaik Signed-off-by: Johan Hovold Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251201093419.24474-2-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index f53cddf07d508b..f1ef673ed52191 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2841,7 +2841,7 @@ static int wcd937x_add_slave_components(struct wcd937x_priv *wcd937x, dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n"); return -ENODEV; } - of_node_get(wcd937x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd937x->rxnode); @@ -2850,7 +2850,7 @@ static int wcd937x_add_slave_components(struct wcd937x_priv *wcd937x, dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n"); return -ENODEV; } - of_node_get(wcd937x->txnode); + component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd937x->txnode); From 32ae6ebe171aca9ce10f5790523a9865b6c08b02 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Dec 2025 10:34:18 +0100 Subject: [PATCH 09/55] ASoC: codecs: wcd938x: fix OF node leaks on probe failure The component match entry release function will drop the references taken while looking up the soundwire OF nodes when the platform device is unbound. Drop the additional references taken to avoid leaking them on probe failure (e.g. probe deferral) and on driver unbind. Fixes: 8d78602aa87a ("ASoC: codecs: wcd938x: add basic driver") Cc: Srinivas Kandagatla Signed-off-by: Johan Hovold Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251201093419.24474-3-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index f5b7de2bc896da..cb0a0bfdb6e322 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3464,7 +3464,6 @@ static int wcd938x_add_slave_components(struct wcd938x_priv *wcd938x, return -ENODEV; } - of_node_get(wcd938x->rxnode); component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd938x->rxnode); @@ -3473,7 +3472,7 @@ static int wcd938x_add_slave_components(struct wcd938x_priv *wcd938x, dev_err(dev, "%s: Tx-device node not defined\n", __func__); return -ENODEV; } - of_node_get(wcd938x->txnode); + component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd938x->txnode); return 0; From 3ef4d9ede20db39bff34a559b04a1938fb31251e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Dec 2025 10:34:19 +0100 Subject: [PATCH 10/55] ASoC: codecs: wcd939x: fix OF node leaks on probe failure The component match entry release function will drop the references taken while looking up the soundwire OF nodes when the platform device is unbound. Drop the additional references taken to avoid leaking them on probe failure (e.g. probe deferral) and on driver unbind. Fixes: 10f514bd172a ("ASoC: codecs: Add WCD939x Codec driver") Cc: Neil Armstrong Signed-off-by: Johan Hovold Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251201093419.24474-4-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 7c5dd048438420..01f1a08f48e65d 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -3526,7 +3526,6 @@ static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, return -ENODEV; } - of_node_get(wcd939x->rxnode); component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd939x->rxnode); @@ -3535,7 +3534,7 @@ static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, dev_err(dev, "%s: Tx-device node not defined\n", __func__); return -ENODEV; } - of_node_get(wcd939x->txnode); + component_match_add_release(dev, matchptr, component_release_of, component_compare_of, wcd939x->txnode); return 0; From 2b69bee5a2d07bc0583f2a8f17d262ddf7c58466 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Dec 2025 11:14:29 +0000 Subject: [PATCH 11/55] ASoC: cs-amp-lib: Revert use of __free(kfree) back to normal C cleanup Revert commit 6797540c8b76 ("ASoC: cs-amp-lib: Use __free(kfree) instead of manual freeing"). Krzysztof Kozlowski pointed out that __free() can be dangerous. It can introduce new cleanup bugs. These are more subtle and difficult to spot than a missing goto in traditional cleanup, because they are triggered by writing regular idiomatic C code instead of using C++ conventions. As it's regular C style it's more likely to be missed because the code is as would be expected for C. The traditional goto also more obviously flags to anyone changing the code in the future that they must be careful about the cleanup. We can just revert the change. There was nothing wrong with the original code and as Krzysztof noted: "it does not make the code simpler." Signed-off-by: Richard Fitzgerald Fixes: 6797540c8b76 ("ASoC: cs-amp-lib: Use __free(kfree) instead of manual freeing") Link: https://patch.msgid.link/20251201111429.43517-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 8c9fd9980a7d60..d8f8b0259cd150 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -310,8 +309,9 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, efi_guid_t **guid, u32 *attr) { - struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL; + struct cirrus_amp_efi_data *efi_data; unsigned long data_size = 0; + u8 *data; efi_status_t status; int i, ret; @@ -339,18 +339,19 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, } /* Get variable contents into buffer */ - efi_data = kmalloc(data_size, GFP_KERNEL); - if (!efi_data) + data = kmalloc(data_size, GFP_KERNEL); + if (!data) return ERR_PTR(-ENOMEM); status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name, cs_amp_lib_cal_efivars[i].guid, - attr, &data_size, efi_data); + attr, &data_size, data); if (status != EFI_SUCCESS) { ret = -EINVAL; goto err; } + efi_data = (struct cirrus_amp_efi_data *)data; dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); if ((efi_data->count > 128) || @@ -364,9 +365,10 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, if (efi_data->size == 0) efi_data->size = data_size; - return_ptr(efi_data); + return efi_data; err: + kfree(data); dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); return ERR_PTR(ret); @@ -389,9 +391,9 @@ static int cs_amp_set_cal_efi_buffer(struct device *dev, static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, struct cirrus_amp_cal_data *out_data) { - struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL; + struct cirrus_amp_efi_data *efi_data; struct cirrus_amp_cal_data *cal = NULL; - int i; + int i, ret; efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL); if (IS_ERR(efi_data)) @@ -432,14 +434,17 @@ static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); } - if (!cal) { + if (cal) { + memcpy(out_data, cal, sizeof(*out_data)); + ret = 0; + } else { dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); - return -ENOENT; + ret = -ENOENT; } - memcpy(out_data, cal, sizeof(*out_data)); + kfree(efi_data); - return 0; + return ret; } static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, From eaf526cfa4e537af86543d77f402b9ae803f606e Mon Sep 17 00:00:00 2001 From: Ilyas Gasanov Date: Tue, 2 Dec 2025 19:49:30 +0400 Subject: [PATCH 12/55] ALSA: hda/realtek: Add PCI SSIDs to HP ProBook quirks According to HW probes, there exist upgraded variants of HP ProBook 440/450 G8 notebook PC hardware, with PCI SSIDs not yet covered: - https://linux-hardware.org/?id=pci:8086-a0c8-103c-8a76 (HP ProBook 440 G8, SSID 103C:8A76, Tiger Lake HDA, ALC236) - https://linux-hardware.org/?id=pci:8086-a0c8-103c-8a77 (HP ProBook 450 G8, SSID 103C:8A77, Tiger Lake HDA, ALC236) The HDA GPIO indices for the mic mute (0x1) and speaker mute (0x2) LEDs are virtually guaranteed to be identical for the HP ProBook 440/450 G8 variants with motherboard SSIDs 8A74/8A75 (BIOS T87) and 8A76/8A77 (BIOS T88); especially since spare keyboard parts are interchangeable between different revisions of the same notebook models, and thus necessarily have the same slot pinouts. Signed-off-by: Ilyas Gasanov Link: https://patch.msgid.link/20251202154930.74481-1-public@gsnoff.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 4efc16ae6b5deb..d90a6c01f63be4 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6598,6 +6598,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { 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), + SND_PCI_QUIRK(0x103c, 0x8a76, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a77, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), From 0ebbd45c33d0049ebf5a22c1434567f0c420b333 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 2 Dec 2025 18:16:42 +0800 Subject: [PATCH 13/55] ASoC: bcm: bcm63xx-pcm-whistler: Check return value of of_dma_configure() bcm63xx_soc_pcm_new() does not check the return value of of_dma_configure(), which may fail with -EPROBE_DEFER or other errors, allowing PCM setup to continue with incomplete DMA configuration. Add error checking for of_dma_configure() and return on failure. Fixes: 88eb404ccc3e ("ASoC: brcm: Add DSL/PON SoC audio driver") Signed-off-by: Haotian Zhang Link: https://patch.msgid.link/20251202101642.492-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/bcm/bcm63xx-pcm-whistler.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c index e3a4fcc63a56dc..efeb06ddabeb3b 100644 --- a/sound/soc/bcm/bcm63xx-pcm-whistler.c +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -358,7 +358,9 @@ static int bcm63xx_soc_pcm_new(struct snd_soc_component *component, i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev); - of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); + ret = of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); + if (ret) + return ret; ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32)); if (ret) From 270d32cd0efc2ac87584883c0c2f3eb0f47f1415 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 3 Dec 2025 18:28:36 +0800 Subject: [PATCH 14/55] ASoC: dt-bindings: cirrus,cs42xx8: Reference common DAI properties Reference the dai-common.yaml schema to allow '#sound-dai-cells' and "sound-name-prefix' to be used because cirrus,cs42xx8 is codec DAI. Signed-off-by: Shengjiu Wang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251203102836.3856471-1-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml index cd47905eb20a79..7ae72bd901f4da 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml @@ -9,6 +9,9 @@ title: Cirrus Logic CS42448/CS42888 audio CODEC maintainers: - patches@opensource.cirrus.com +allOf: + - $ref: dai-common.yaml# + properties: compatible: enum: @@ -63,7 +66,7 @@ then: - VLC-supply - VLS-supply -additionalProperties: false +unevaluatedProperties: false examples: - | From 3ee257aba1d56c3f0f1028669a8ad0f1a477f05b Mon Sep 17 00:00:00 2001 From: Hemalatha Pinnamreddy Date: Wed, 3 Dec 2025 12:16:48 +0530 Subject: [PATCH 15/55] ASoC: amd: acp: Audio is not resuming after s0ix Audio fails to resume after system exits suspend mode due to accessing incorrect ring buffer address during resume. This patch resolves issue by selecting correct address based on the ACP version. Fixes: f6f7d25b11033 ("ASoC: amd: acp: Add pte configuration for ACP7.0 platform") Signed-off-by: Hemalatha Pinnamreddy Signed-off-by: Raghavendra Prasad Mallela Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20251203064650.2554625-1-raghavendraprasad.mallela@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-legacy-common.c | 30 +++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index 3078f459e0050b..4e477c48d4bdd1 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -219,7 +219,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, SP_PB_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip); reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip); - phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START; + else + phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip)); } else { reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip); @@ -227,7 +230,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, SP_CAPT_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip); reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip); - phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START; + else + phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip)); } break; @@ -238,7 +244,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, BT_PB_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip); reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip); - phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START; + else + phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip)); } else { reg_dma_size = ACP_BT_RX_DMA_SIZE(chip); @@ -246,7 +255,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, BT_CAPT_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip); reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip); - phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START; + else + phy_addr = I2S_BT_RX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip)); } break; @@ -257,7 +269,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, HS_PB_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_HS_TX_FIFOADDR; reg_fifo_size = ACP_HS_TX_FIFOSIZE; - phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START; + else + phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_HS_TX_RINGBUFADDR); } else { reg_dma_size = ACP_HS_RX_DMA_SIZE; @@ -265,7 +280,10 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, HS_CAPT_FIFO_ADDR_OFFSET; reg_fifo_addr = ACP_HS_RX_FIFOADDR; reg_fifo_size = ACP_HS_RX_FIFOSIZE; - phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; + if (chip->acp_rev >= ACP70_PCI_ID) + phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START; + else + phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR); } break; From ae585fabb9713a43e358cf606451386757225c95 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 3 Dec 2025 18:05:28 +0800 Subject: [PATCH 16/55] ASoC: ak4458: Disable regulator when error happens Disable regulator in runtime resume when error happens to balance the reference count of regulator. Fixes: 7e3096e8f823 ("ASoC: ak4458: Add regulator support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20251203100529.3841203-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4458.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index f0b465f9ded530..783d2ef21c11c1 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -671,7 +671,15 @@ static int ak4458_runtime_resume(struct device *dev) regcache_cache_only(ak4458->regmap, false); regcache_mark_dirty(ak4458->regmap); - return regcache_sync(ak4458->regmap); + ret = regcache_sync(ak4458->regmap); + if (ret) + goto err; + + return 0; +err: + regcache_cache_only(ak4458->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies), ak4458->supplies); + return ret; } static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { From 1f8f726a2a29c28f65b30880335a1610c5e63594 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 3 Dec 2025 18:05:29 +0800 Subject: [PATCH 17/55] ASoC: ak5558: Disable regulator when error happens Disable regulator in runtime resume when error happens to balance the reference count of regulator. Fixes: 2ff6d5a108c6 ("ASoC: ak5558: Add regulator support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20251203100529.3841203-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak5558.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 683f3e472f5000..73684fc5beb1a7 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -372,7 +372,15 @@ static int ak5558_runtime_resume(struct device *dev) regcache_cache_only(ak5558->regmap, false); regcache_mark_dirty(ak5558->regmap); - return regcache_sync(ak5558->regmap); + ret = regcache_sync(ak5558->regmap); + if (ret) + goto err; + + return 0; +err: + regcache_cache_only(ak5558->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies), ak5558->supplies); + return ret; } static const struct dev_pm_ops ak5558_pm = { From 2c7e5e17c05f1d5e10e63e1baff2b362cd08dcd6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Dec 2025 15:06:12 +0100 Subject: [PATCH 18/55] ASoC: codecs: nau8325: Silence uninitialized variables warnings clang W=1 builds warn: nau8325.c:430:13: error: variable 'n2_max' is uninitialized when used here [-Werror,-Wuninitialized] which are false positive, because the variables will be always initialized when used (guarded by mclk_max!=0 check). However initializing them upfront makes the code more obvious and easier, plus it silences the warning. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251203140611.87191-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8325.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c index 3bfdb448f8bd72..e651263a981217 100644 --- a/sound/soc/codecs/nau8325.c +++ b/sound/soc/codecs/nau8325.c @@ -386,7 +386,8 @@ static int nau8325_clksrc_choose(struct nau8325 *nau8325, const struct nau8325_srate_attr **srate_table, int *n1_sel, int *mult_sel, int *n2_sel) { - int i, j, mclk, mclk_max, ratio, ratio_sel, n2_max; + int i, j, mclk, ratio; + int mclk_max = 0, ratio_sel = 0, n2_max = 0; if (!nau8325->mclk || !nau8325->fs) goto proc_err; @@ -408,7 +409,6 @@ static int nau8325_clksrc_choose(struct nau8325 *nau8325, } /* Get MCLK_SRC through 1/N, Multiplier, and then 1/N2. */ - mclk_max = 0; for (i = 0; i < ARRAY_SIZE(mclk_n1_div); i++) { for (j = 0; j < ARRAY_SIZE(mclk_n3_mult); j++) { mclk = nau8325->mclk << mclk_n3_mult[j].param; From 57d508b5f718730f74b11e0dc9609ac7976802d1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Dec 2025 15:16:45 +0100 Subject: [PATCH 19/55] ASoC: rockchip: Fix Wvoid-pointer-to-enum-cast warning (again) 'version' is an enum, thus cast of pointer on 64-bit compile test with clang W=1 causes: rockchip_pdm.c:583:17: error: cast to smaller integer type 'enum rk_pdm_version' from 'const void *' [-Werror,-Wvoid-pointer-to-enum-cast] This was already fixed in commit 49a4a8d12612 ("ASoC: rockchip: Fix Wvoid-pointer-to-enum-cast warning") but then got bad in commit 9958d85968ed ("ASoC: Use device_get_match_data()"). Discussion on LKML also pointed out that 'uintptr_t' is not the correct type and either 'kernel_ulong_t' or 'unsigned long' should be used, with several arguments towards the latter [1]. Link: https://lore.kernel.org/r/CAMuHMdX7t=mabqFE5O-Cii3REMuyaePHmqX+j_mqyrn6XXzsoA@mail.gmail.com/ [1] Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251203141644.106459-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_pdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index c1ee470ec6079d..c69cdd6f24994c 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -580,7 +580,7 @@ static int rockchip_pdm_probe(struct platform_device *pdev) if (!pdm) return -ENOMEM; - pdm->version = (enum rk_pdm_version)device_get_match_data(&pdev->dev); + pdm->version = (unsigned long)device_get_match_data(&pdev->dev); if (pdm->version == RK_PDM_RK3308) { pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m"); if (IS_ERR(pdm->reset)) From 39191ce5dbfd65fededb4f0d408d6232c45766ba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 3 Dec 2025 11:55:41 +0100 Subject: [PATCH 20/55] ASoc: qcom: q6afe: fix bad guard conversion A recent spinlock guard conversion used the wrong guard so that interrupts are no longer disabled while holding the port list lock. Based on a cursory look this appears to be safe currently, but it could cause a deadlock if one of these helpers are ever called in interrupt context. Fixes: 4b1edbb028fb ("ASoC: qcom: q6afe: Use guard() for spin locks") Cc: Srinivas Kandagatla Signed-off-by: Johan Hovold Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251203105542.24765-2-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 980851a1297608..0b01fc9e13a727 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -947,7 +947,7 @@ static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token) struct q6afe_port *p; struct q6afe_port *ret = NULL; - guard(spinlock)(&afe->port_list_lock); + guard(spinlock_irqsave)(&afe->port_list_lock); list_for_each_entry(p, &afe->port_list, node) if (p->token == token) { ret = p; @@ -1807,7 +1807,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) port->cfg_type = cfg_type; kref_init(&port->refcount); - guard(spinlock)(&afe->port_list_lock); + guard(spinlock_irqsave)(&afe->port_list_lock); list_add_tail(&port->node, &afe->port_list); return port; From 8fb817335ad8d960ea05af3882cea113e59cb4e1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 3 Dec 2025 21:29:54 -0800 Subject: [PATCH 21/55] ASoC: cros_ec_codec: Remove unnecessary selection of CRYPTO The only crypto-related functionality this codec uses is the sha256() function, which is provided by CRYPTO_LIB_SHA256. Originally CRYPTO_LIB_SHA256 was visible only when CRYPTO; however, that was fixed years ago and the libraries can now be selected on their own. So, remove the unnecessary selection of CRYPTO. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20251204052954.488568-1-ebiggers@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4eb345cb29f09f..c245a0b09cc6a3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -777,7 +777,6 @@ config SND_SOC_CQ0093VC config SND_SOC_CROS_EC_CODEC tristate "codec driver for ChromeOS EC" depends on CROS_EC - select CRYPTO select CRYPTO_LIB_SHA256 help If you say yes here you will get support for the From c34b04cc6178f33c08331568c7fd25c5b9a39f66 Mon Sep 17 00:00:00 2001 From: Denis Arefev Date: Tue, 2 Dec 2025 13:13:36 +0300 Subject: [PATCH 22/55] ALSA: hda: cs35l41: Fix NULL pointer dereference in cs35l41_hda_read_acpi() The acpi_get_first_physical_node() function can return NULL, in which case the get_device() function also returns NULL, but this value is then dereferenced without checking,so add a check to prevent a crash. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") Cc: stable@vger.kernel.org Signed-off-by: Denis Arefev Reviewed-by: Richard Fitzgerald Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251202101338.11437-1-arefev@swemel.ru --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index c0f2a3ff77a1b6..21e00055c0c443 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1901,6 +1901,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i cs35l41->dacpi = adev; physdev = get_device(acpi_get_first_physical_node(adev)); + if (!physdev) + return -ENODEV; sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); if (IS_ERR(sub)) From 210d77cca3d0494ed30a5c628b20c1d95fa04fb1 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 3 Dec 2025 12:27:03 +0800 Subject: [PATCH 23/55] ALSA: firewire-motu: fix buffer overflow in hwdep read for DSP events The DSP event handling code in hwdep_read() could write more bytes to the user buffer than requested, when a user provides a buffer smaller than the event header size (8 bytes). Fix by using min_t() to clamp the copy size, This ensures we never copy more than the user requested. Reported-by: Yuhao Jiang Reported-by: Junrui Luo Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB78810656377E79E58350D951AFD9A@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/firewire/motu/motu-hwdep.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 981c19430cb0fe..6675b23aad69ef 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -83,10 +83,11 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; event.motu_register_dsp_change.count = (consumed - sizeof(event.motu_register_dsp_change)) / 4; - if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) + if (copy_to_user(buf, &event, + min_t(long, count, sizeof(event.motu_register_dsp_change)))) return -EFAULT; - count = consumed; + count = min_t(long, count, consumed); } else { spin_unlock_irq(&motu->lock); From d740d52e342bae9c88c719b015d6065783b961a1 Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Thu, 4 Dec 2025 10:52:03 +0530 Subject: [PATCH 24/55] ALSA: usb-audio: Initialize status1 to fix uninitialized symbol errors Initialize 'status1' with a default value to resolve the static analysis smatch reported error "uninitialized symbol 'status1'". The 'status1' variable is used to create a buff using "kmemdup". So, ensure to initialize the value before it is read. Suggested-by: Takashi Iwai Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251204052201.16286-3-hariconscious@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index fe6c2cebc7f0db..a6bbb82376c126 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2545,6 +2545,7 @@ static int snd_rme_get_status1(struct snd_kcontrol *kcontrol, struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; + *status1 = 0; CLASS(snd_usb_lock, pm)(chip); if (pm.err < 0) return pm.err; From f8b1ff655586846103ef890b53527dace4f45eff Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 5 Dec 2025 15:05:54 +0000 Subject: [PATCH 25/55] ALSA: hda/realtek: Add support for HP Turbine Laptops Laptops use 2 CS35L41 Amps with HDA, using Internal boost, with I2C Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20251205150614.49590-2-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d90a6c01f63be4..0172ff22f5eae3 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6770,6 +6770,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e9e, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8eb7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8eb8, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), From 826c0b1ed09e5335abcae07292440ce72346e578 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 5 Dec 2025 15:05:55 +0000 Subject: [PATCH 26/55] ALSA: hda/realtek: Add support for ASUS UM3406GA Laptops use 2 CS35L41 Amps with HDA, using External boost, with I2C Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20251205150614.49590-3-sbinding@opensource.cirrus.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 0172ff22f5eae3..9510aa2f1bda5d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6844,6 +6844,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1584, "ASUS UM3406GA ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), From 85a6544777e0f57cce902f7217a377eb74b7d1a4 Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Fri, 5 Dec 2025 23:46:19 +0800 Subject: [PATCH 27/55] ALSA: hda: dt-bindings: add CIX IPBLOQ HDA controller support Add CIX IPBLOQ HDA controller support, which is integrated into CIX SKY1 audio subsystem. HDA controller supports 64bit, but the audio subsystem can only 32bit transaction. Use jack polling mode as there is a hardware issue, lead to interrupt strom if the RIRB interrupt enabled. Host and hdac has different view of memory, so need do dma address translation. Signed-off-by: Joakim Zhang Reviewed-by: Krzysztof Kozlowski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251205154621.3019640-2-joakim.zhang@cixtech.com --- .../bindings/sound/cix,sky1-ipbloq-hda.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/cix,sky1-ipbloq-hda.yaml diff --git a/Documentation/devicetree/bindings/sound/cix,sky1-ipbloq-hda.yaml b/Documentation/devicetree/bindings/sound/cix,sky1-ipbloq-hda.yaml new file mode 100644 index 00000000000000..02ac5f1aa926c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cix,sky1-ipbloq-hda.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cix,sky1-ipbloq-hda.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CIX IPBLOQ HDA controller + +description: + CIX IPBLOQ High Definition Audio (HDA) Controller + +maintainers: + - Joakim Zhang + +allOf: + - $ref: sound-card-common.yaml# + +properties: + compatible: + const: cix,sky1-ipbloq-hda + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: ipg + - const: per + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + +unevaluatedProperties: false + +examples: + - | + #include + + hda@70c0000 { + compatible = "cix,sky1-ipbloq-hda"; + reg = <0x70c0000 0x10000>; + interrupts = ; + clocks = <&audss_clk 7>, + <&audss_clk 8>; + clock-names = "ipg", "per"; + resets = <&audss_rst 14>; + model = "CIX SKY1 EVB HDA"; + }; From a4f2fa516e83f11c3792405599613c12efe6135e Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Fri, 5 Dec 2025 23:46:20 +0800 Subject: [PATCH 28/55] ALSA: hda/core: add addr_offset field for bus address translation Add bus addr_offset field for dma address translation, for some SoCs such as CIX SKY1 which is ARM64 Arch, HOST and HDAC has different memory view, so need to do dma address translation between HOST and HDAC. Signed-off-by: Joakim Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251205154621.3019640-3-joakim.zhang@cixtech.com --- include/sound/hdaudio.h | 3 +++ sound/hda/core/bus.c | 1 + sound/hda/core/controller.c | 12 ++++++------ sound/hda/core/stream.c | 10 +++++----- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 4e0c1d8af09f76..f11bfc6b9f428a 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -380,6 +380,9 @@ struct hdac_bus { /* factor used to derive STRIPE control value */ unsigned int sdo_limit; + + /* address offset between host and hadc */ + dma_addr_t addr_offset; }; int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, diff --git a/sound/hda/core/bus.c b/sound/hda/core/bus.c index 9b196c915f3783..81498f1e413e2a 100644 --- a/sound/hda/core/bus.c +++ b/sound/hda/core/bus.c @@ -47,6 +47,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, INIT_LIST_HEAD(&bus->hlink_list); init_waitqueue_head(&bus->rirb_wq); bus->irq = -1; + bus->addr_offset = 0; /* * Default value of '8' is as per the HD audio specification (Rev 1.0a). diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index a7c00ad801170c..69e11d62bbfa7a 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -48,8 +48,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* CORB set up */ bus->corb.addr = bus->rb.addr; bus->corb.buf = (__le32 *)bus->rb.area; - snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); - snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); + snd_hdac_chip_writel(bus, CORBLBASE, (u32)(bus->corb.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr + bus->addr_offset)); /* set the corb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); @@ -70,8 +70,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); bus->rirb.wp = bus->rirb.rp = 0; memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); - snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); - snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); + snd_hdac_chip_writel(bus, RIRBLBASE, (u32)(bus->rirb.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr + bus->addr_offset)); /* set the rirb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); @@ -625,8 +625,8 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) /* program the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { - snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); - snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); + snd_hdac_chip_writel(bus, DPLBASE, (u32)(bus->posbuf.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr + bus->addr_offset)); } bus->chip_init = true; diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c index 579ec544ef4a48..b471a038b3140d 100644 --- a/sound/hda/core/stream.c +++ b/sound/hda/core/stream.c @@ -288,16 +288,16 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading) /* program the BDL address */ /* lower BDL address */ - snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)(azx_dev->bdl.addr + bus->addr_offset)); /* upper BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->bdl.addr)); + upper_32_bits(azx_dev->bdl.addr + bus->addr_offset)); /* enable the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE)) snd_hdac_chip_writel(bus, DPLBASE, - (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE); + (u32)(bus->posbuf.addr + bus->addr_offset) | AZX_DPLBASE_ENABLE); } /* set the interrupt enable bits in the descriptor control register */ @@ -464,8 +464,8 @@ static int setup_bdle(struct hdac_bus *bus, addr = snd_sgbuf_get_addr(dmab, ofs); /* program the address field of the BDL entry */ - bdl[0] = cpu_to_le32((u32)addr); - bdl[1] = cpu_to_le32(upper_32_bits(addr)); + bdl[0] = cpu_to_le32((u32)(addr + bus->addr_offset)); + bdl[1] = cpu_to_le32(upper_32_bits(addr + bus->addr_offset)); /* program the size field of the BDL entry */ chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); /* one BDLE cannot cross 4K boundary on CTHDA chips */ From d91e9bd10125a9b0427420453b11f56228a6d6d0 Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Fri, 5 Dec 2025 23:46:21 +0800 Subject: [PATCH 29/55] ALSA: hda: add CIX IPBLOQ HDA controller support Add CIX IPBLOQ HDA controller support, which is integrated into CIX SKY1 audio subsystem. HDA controller supports 64bit, but the audio subsystem can only 32bit transaction. Use jack polling mode as there is a hardware issue, lead to interrupt strom if the RIRB interrupt enabled. Host and hdac has different view of memory, so need do dma address translation. Signed-off-by: Joakim Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251205154621.3019640-4-joakim.zhang@cixtech.com --- sound/hda/controllers/Kconfig | 14 + sound/hda/controllers/Makefile | 2 + sound/hda/controllers/cix-ipbloq.c | 436 +++++++++++++++++++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 sound/hda/controllers/cix-ipbloq.c diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 34721f50b055af..72855f2df45148 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -30,6 +30,20 @@ config SND_HDA_TEGRA To compile this driver as a module, choose M here: the module will be called snd-hda-tegra. +config SND_HDA_CIX_IPBLOQ + tristate "CIX IPBLOQ HD Audio" + depends on ARCH_CIX || COMPILE_TEST + select SND_HDA + select SND_HDA_ALIGNED_MMIO + help + Say Y here to support the HDA controller present in CIX SoCs + + This options enables support for the HD Audio controller + present in some CIX SoCs. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-cix-ipbloq. + config SND_HDA_ACPI tristate "HD Audio ACPI" depends on ACPI diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile index a4bcd055e9aef9..8967b6771d904d 100644 --- a/sound/hda/controllers/Makefile +++ b/sound/hda/controllers/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 snd-hda-intel-y := intel.o snd-hda-tegra-y := tegra.o +snd-hda-cix-ipbloq-y := cix-ipbloq.o snd-hda-acpi-y := acpi.o subdir-ccflags-y += -I$(src)/../common @@ -10,4 +11,5 @@ CFLAGS_intel.o := -I$(src) obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o +obj-$(CONFIG_SND_HDA_CIX_IPBLOQ) += snd-hda-cix-ipbloq.o obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o diff --git a/sound/hda/controllers/cix-ipbloq.c b/sound/hda/controllers/cix-ipbloq.c new file mode 100644 index 00000000000000..cc9153692ef53e --- /dev/null +++ b/sound/hda/controllers/cix-ipbloq.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2025 Cix Technology Group Co., Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hda_controller.h" + +#define CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS 1000 +#define CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS 100 + +#define CIX_IPBLOQ_SKY1_ADDR_HOST_TO_HDAC_OFFSET (-0x90000000ULL) + +struct cix_ipbloq_hda { + struct azx chip; + struct device *dev; + void __iomem *regs; + + struct reset_control *reset; + struct clk_bulk_data clocks[2]; + unsigned int nclocks; +}; + +static const struct hda_controller_ops cix_ipbloq_hda_ops; + +static int cix_ipbloq_hda_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + + return 0; +} + +static int cix_ipbloq_hda_dev_free(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + if (azx_bus(chip)->chip_init) { + azx_stop_all_streams(chip); + azx_stop_chip(chip); + } + + azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(azx_bus(chip)); + + return 0; +} + +static int cix_ipbloq_hda_probe_codec(struct cix_ipbloq_hda *hda) +{ + struct azx *chip = &hda->chip; + struct hdac_bus *bus = azx_bus(chip); + int err; + + to_hda_bus(bus)->bus_probing = 1; + + /* create codec instances */ + err = azx_probe_codecs(chip, 8); + if (err < 0) { + dev_err(hda->dev, "probe codecs failed: %d\n", err); + return err; + } + + err = azx_codec_configure(chip); + if (err < 0) { + dev_err(hda->dev, "codec configure failed: %d\n", err); + return err; + } + + err = snd_card_register(chip->card); + if (err < 0) { + dev_err(hda->dev, "card register failed: %d\n", err); + return err; + } + + chip->running = 1; + + to_hda_bus(bus)->bus_probing = 0; + + snd_hda_set_power_save(&chip->bus, CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS); + + return 0; +} + +static int cix_ipbloq_hda_init(struct cix_ipbloq_hda *hda, + struct azx *chip, + struct platform_device *pdev) +{ + const char *sname = NULL, *drv_name = "cix-ipbloq-hda"; + struct hdac_bus *bus = azx_bus(chip); + struct snd_card *card = chip->card; + struct resource *res; + unsigned short gcap; + int irq_id, err; + + hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(hda->regs)) { + dev_err(hda->dev, "failed to get and ioremap resource\n"); + return PTR_ERR(hda->regs); + } + bus->remap_addr = hda->regs; + bus->addr = res->start; + + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) { + dev_err(hda->dev, "failed to get the irq, err = %d\n", irq_id); + return irq_id; + } + + err = devm_request_irq(hda->dev, irq_id, azx_interrupt, + 0, KBUILD_MODNAME, chip); + if (err < 0) + return dev_err_probe(hda->dev, err, + "unable to request IRQ %d : err = %d\n", irq_id, err); + bus->irq = irq_id; + card->sync_irq = bus->irq; + + gcap = azx_readw(chip, GCAP); + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) { + dev_err(hda->dev, "failed to initialize streams: %d\n", err); + return err; + } + + err = azx_alloc_stream_pages(chip); + if (err < 0) { + dev_err(hda->dev, "failed to allocate stream pages: %d\n", err); + return err; + } + + /* initialize chip */ + azx_init_chip(chip, 1); + + /* codec detection */ + if (!bus->codec_mask) { + dev_err(hda->dev, "no codecs found\n"); + return -ENODEV; + } + dev_dbg(card->dev, "codec detection mask = 0x%lx\n", bus->codec_mask); + + /* driver name */ + strscpy(card->driver, drv_name, sizeof(card->driver)); + + /* shortname for card */ + sname = of_get_property(pdev->dev.of_node, "model", NULL); + if (!sname) + sname = drv_name; + if (strlen(sname) > sizeof(card->shortname)) + dev_dbg(card->dev, "truncating shortname for card\n"); + strscpy(card->shortname, sname, sizeof(card->shortname)); + + /* longname for card */ + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, bus->addr, bus->irq); + + return 0; +} + +static int cix_ipbloq_hda_create(struct cix_ipbloq_hda *hda, + struct snd_card *card, + unsigned int driver_caps) +{ + static const struct snd_device_ops ops = { + .dev_disconnect = cix_ipbloq_hda_dev_disconnect, + .dev_free = cix_ipbloq_hda_dev_free, + }; + struct azx *chip; + int err; + + chip = &hda->chip; + chip->card = card; + chip->ops = &cix_ipbloq_hda_ops; + chip->driver_caps = driver_caps; + chip->driver_type = driver_caps & 0xff; + chip->dev_index = 0; + chip->single_cmd = 0; + chip->codec_probe_mask = -1; + chip->align_buffer_size = 1; + chip->jackpoll_interval = msecs_to_jiffies(CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS); + mutex_init(&chip->open_mutex); + INIT_LIST_HEAD(&chip->pcm_list); + + /* + * HD-audio controllers appear pretty inaccurate about the update-IRQ timing. + * The IRQ is issued before actually the data is processed. So use stream + * link position by default instead of dma position buffer. + */ + chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib; + + err = azx_bus_init(chip, NULL); + if (err < 0) { + dev_err(hda->dev, "failed to init bus, err = %d\n", err); + return err; + } + + /* RIRBSTS.RINTFL cannot be cleared, cause interrupt storm */ + chip->bus.core.polling_mode = 1; + chip->bus.core.not_use_interrupts = 1; + + chip->bus.core.aligned_mmio = 1; + chip->bus.core.dma_stop_delay = 100; + chip->bus.core.addr_offset = (dma_addr_t)CIX_IPBLOQ_SKY1_ADDR_HOST_TO_HDAC_OFFSET; + + chip->bus.jackpoll_in_suspend = 1; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + dev_err(card->dev, "failed to create device, err = %d\n", err); + return err; + } + + return 0; +} + +static int cix_ipbloq_hda_probe(struct platform_device *pdev) +{ + const unsigned int driver_flags = AZX_DCAPS_PM_RUNTIME; + struct cix_ipbloq_hda *hda; + struct snd_card *card; + struct azx *chip; + int err; + + hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); + if (!hda) + return -ENOMEM; + hda->dev = &pdev->dev; + + hda->reset = devm_reset_control_get(hda->dev, NULL); + if (IS_ERR(hda->reset)) + return dev_err_probe(hda->dev, PTR_ERR(hda->reset), + "failed to get reset, err = %ld\n", PTR_ERR(hda->reset)); + + hda->clocks[hda->nclocks++].id = "ipg"; + hda->clocks[hda->nclocks++].id = "per"; + err = devm_clk_bulk_get(hda->dev, hda->nclocks, hda->clocks); + if (err < 0) + return dev_err_probe(hda->dev, err, "failed to get clk, err = %d\n", err); + + dma_set_mask_and_coherent(hda->dev, DMA_BIT_MASK(32)); + + err = of_reserved_mem_device_init(hda->dev); + if (err < 0 && err != -ENODEV) { + dev_err(hda->dev, + "failed to init reserved mem for DMA, err = %d\n", err); + return err; + } + + err = snd_card_new(hda->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err < 0) + return dev_err_probe(hda->dev, err, "failed to crate card, err = %d\n", err); + + err = cix_ipbloq_hda_create(hda, card, driver_flags); + if (err < 0) + goto out_free_card; + + chip = &hda->chip; + card->private_data = chip; + dev_set_drvdata(hda->dev, card); + + pm_runtime_enable(hda->dev); + if (!azx_has_pm_runtime(chip)) + pm_runtime_forbid(hda->dev); + + err = pm_runtime_resume_and_get(hda->dev); + if (err < 0) { + dev_err(hda->dev, "runtime resume and get failed, err = %d\n", err); + goto out_free_device; + } + + err = cix_ipbloq_hda_init(hda, chip, pdev); + if (err < 0) + goto out_free_device; + + err = cix_ipbloq_hda_probe_codec(hda); + if (err < 0) + goto out_free_device; + + pm_runtime_put(hda->dev); + + return 0; + +out_free_device: + snd_device_free(card, chip); +out_free_card: + snd_card_free(card); + + return err; +} + +static void cix_ipbloq_hda_remove(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip = card->private_data; + + snd_device_free(card, chip); + snd_card_free(card); + + pm_runtime_disable(&pdev->dev); +} + +static void cix_ipbloq_hda_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +static int cix_ipbloq_hda_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + int rc; + + rc = pm_runtime_force_suspend(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + + return 0; +} + +static int cix_ipbloq_hda_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + int rc; + + rc = pm_runtime_force_resume(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + +static int cix_ipbloq_hda_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + + if (chip && chip->running) { + azx_stop_chip(chip); + azx_enter_link_reset(chip); + } + + clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); + + return 0; +} + +static int cix_ipbloq_hda_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + int rc; + + rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); + if (rc) { + dev_err(dev, "failed to enable clk bulk, rc: %d\n", rc); + return rc; + } + + rc = reset_control_assert(hda->reset); + if (rc) { + dev_err(dev, "failed to assert reset, rc: %d\n", rc); + return rc; + } + + rc = reset_control_deassert(hda->reset); + if (rc) { + dev_err(dev, "failed to deassert reset, rc: %d\n", rc); + return rc; + } + + if (chip && chip->running) + azx_init_chip(chip, 1); + + return 0; +} + +static const struct dev_pm_ops cix_ipbloq_hda_pm = { + SET_SYSTEM_SLEEP_PM_OPS(cix_ipbloq_hda_suspend, + cix_ipbloq_hda_resume) + SET_RUNTIME_PM_OPS(cix_ipbloq_hda_runtime_suspend, + cix_ipbloq_hda_runtime_resume, NULL) +}; + +static const struct of_device_id cix_ipbloq_hda_match[] = { + { .compatible = "cix,sky1-ipbloq-hda" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cix_ipbloq_hda_match); + +static struct platform_driver cix_ipbloq_hda_driver = { + .driver = { + .name = "cix-ipbloq-hda", + .pm = &cix_ipbloq_hda_pm, + .of_match_table = cix_ipbloq_hda_match, + }, + .probe = cix_ipbloq_hda_probe, + .remove = cix_ipbloq_hda_remove, + .shutdown = cix_ipbloq_hda_shutdown, +}; +module_platform_driver(cix_ipbloq_hda_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CIX IPBLOQ HDA bus driver"); +MODULE_AUTHOR("Joakim Zhang "); From 74ac7558ea7630113f0477cff1c718f5b1f54ec8 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 3 Dec 2025 23:34:10 +0100 Subject: [PATCH 30/55] ALSA: Do not build obsolete API ALSA 0.9.0-rc3 is from 2002, 23 years old. Signed-off-by: David Heidelberg Reviewed-by: Janusz Krzysztofik Reviewed-by: Christophe Leroy (CS GROUP) Acked-by: Christophe Leroy (CS GROUP) Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251203-old-alsa-v1-1-ac80704f52c3@ixit.cz --- arch/arm/configs/am200epdkit_defconfig | 1 - arch/arm/configs/lpc32xx_defconfig | 1 - arch/arm/configs/omap1_defconfig | 1 - arch/arm/configs/tegra_defconfig | 1 - arch/mips/configs/gcw0_defconfig | 1 - arch/mips/configs/loongson1_defconfig | 1 - arch/mips/configs/qi_lb60_defconfig | 1 - arch/mips/configs/rbtx49xx_defconfig | 1 - arch/mips/configs/rs90_defconfig | 1 - arch/powerpc/configs/85xx-hw.config | 1 - arch/powerpc/configs/86xx-hw.config | 1 - arch/powerpc/configs/mpc5200_defconfig | 1 - arch/powerpc/configs/ppc6xx_defconfig | 1 - arch/sh/configs/edosk7760_defconfig | 1 - arch/sh/configs/se7724_defconfig | 1 - arch/sh/configs/sh7785lcr_32bit_defconfig | 1 - sound/core/Kconfig | 2 +- 17 files changed, 1 insertion(+), 17 deletions(-) diff --git a/arch/arm/configs/am200epdkit_defconfig b/arch/arm/configs/am200epdkit_defconfig index 134a559aba3dd5..2367b1685c1cf8 100644 --- a/arch/arm/configs/am200epdkit_defconfig +++ b/arch/arm/configs/am200epdkit_defconfig @@ -68,7 +68,6 @@ CONFIG_SOUND=m CONFIG_SND=m CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_PXA2XX_AC97=m CONFIG_USB_GADGET=y diff --git a/arch/arm/configs/lpc32xx_defconfig b/arch/arm/configs/lpc32xx_defconfig index 9afccd76446b6b..9fc5ec12e5aa9f 100644 --- a/arch/arm/configs/lpc32xx_defconfig +++ b/arch/arm/configs/lpc32xx_defconfig @@ -113,7 +113,6 @@ CONFIG_LOGO=y # CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_DEBUG=y CONFIG_SND_DEBUG_VERBOSE=y diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index 24c54bf1e2433e..6c5c5652475d27 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -148,7 +148,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_DUMMY=y CONFIG_SND_USB_AUDIO=y diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index ab477ca13f8996..f27bd26ce4438a 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -219,7 +219,6 @@ CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_DRIVERS is not set CONFIG_SND_HDA_TEGRA=y CONFIG_SND_HDA_INPUT_BEEP=y diff --git a/arch/mips/configs/gcw0_defconfig b/arch/mips/configs/gcw0_defconfig index 8b7ad877e07ae1..0a138b2ef5c588 100644 --- a/arch/mips/configs/gcw0_defconfig +++ b/arch/mips/configs/gcw0_defconfig @@ -80,7 +80,6 @@ CONFIG_LOGO=y # CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_PROC_FS is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_SPI is not set diff --git a/arch/mips/configs/loongson1_defconfig b/arch/mips/configs/loongson1_defconfig index 81acae6f61c8b1..17c9c038f75239 100644 --- a/arch/mips/configs/loongson1_defconfig +++ b/arch/mips/configs/loongson1_defconfig @@ -117,7 +117,6 @@ CONFIG_WATCHDOG_SYSFS=y CONFIG_LOONGSON1_WDT=y CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_MIPS is not set # CONFIG_SND_USB is not set diff --git a/arch/mips/configs/qi_lb60_defconfig b/arch/mips/configs/qi_lb60_defconfig index 5f5b0254d75e78..a1bb0792f6eb1e 100644 --- a/arch/mips/configs/qi_lb60_defconfig +++ b/arch/mips/configs/qi_lb60_defconfig @@ -81,7 +81,6 @@ CONFIG_LOGO=y # CONFIG_LOGO_LINUX_CLUT224 is not set CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_SPI is not set diff --git a/arch/mips/configs/rbtx49xx_defconfig b/arch/mips/configs/rbtx49xx_defconfig index 03a7bbe28a532d..49c709d663bebc 100644 --- a/arch/mips/configs/rbtx49xx_defconfig +++ b/arch/mips/configs/rbtx49xx_defconfig @@ -53,7 +53,6 @@ CONFIG_TXX9_WDT=m # CONFIG_VGA_ARB is not set CONFIG_SOUND=m CONFIG_SND=m -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_PCI is not set diff --git a/arch/mips/configs/rs90_defconfig b/arch/mips/configs/rs90_defconfig index a53dd66e9b8644..8382d535e6dc10 100644 --- a/arch/mips/configs/rs90_defconfig +++ b/arch/mips/configs/rs90_defconfig @@ -105,7 +105,6 @@ CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y # CONFIG_SND_PCM_TIMER is not set -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_PROC_FS is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_MIPS is not set diff --git a/arch/powerpc/configs/85xx-hw.config b/arch/powerpc/configs/85xx-hw.config index 8aff8321739778..2b19c20a9a2c48 100644 --- a/arch/powerpc/configs/85xx-hw.config +++ b/arch/powerpc/configs/85xx-hw.config @@ -117,7 +117,6 @@ CONFIG_SND_INTEL8X0=y CONFIG_SND_POWERPC_SOC=y # CONFIG_SND_PPC is not set CONFIG_SND_SOC=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_USB is not set CONFIG_SND=y CONFIG_SOUND=y diff --git a/arch/powerpc/configs/86xx-hw.config b/arch/powerpc/configs/86xx-hw.config index e7bd265fae5a4a..07f30ab881e59d 100644 --- a/arch/powerpc/configs/86xx-hw.config +++ b/arch/powerpc/configs/86xx-hw.config @@ -80,7 +80,6 @@ CONFIG_SERIO_LIBPS2=y CONFIG_SND_INTEL8X0=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y -# CONFIG_SND_SUPPORT_OLD_API is not set CONFIG_SND=y CONFIG_SOUND=y CONFIG_ULI526X=y diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig index c0fe5e76604a04..617650cea56a92 100644 --- a/arch/powerpc/configs/mpc5200_defconfig +++ b/arch/powerpc/configs/mpc5200_defconfig @@ -75,7 +75,6 @@ CONFIG_FB_SM501=m CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_PCI is not set # CONFIG_SND_PPC is not set diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index b082c1fae13c94..787d707f64a428 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -726,7 +726,6 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m CONFIG_SND_DYNAMIC_MINORS=y -# CONFIG_SND_SUPPORT_OLD_API is not set CONFIG_SND_VERBOSE_PRINTK=y CONFIG_SND_DEBUG=y CONFIG_SND_DEBUG_VERBOSE=y diff --git a/arch/sh/configs/edosk7760_defconfig b/arch/sh/configs/edosk7760_defconfig index 98f4611ba553e3..bc830f1edc8d19 100644 --- a/arch/sh/configs/edosk7760_defconfig +++ b/arch/sh/configs/edosk7760_defconfig @@ -79,7 +79,6 @@ CONFIG_FB_TILEBLITTING=y CONFIG_FB_SH_MOBILE_LCDC=m CONFIG_SOUND=y CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_VERBOSE_PRINTK=y CONFIG_SND_SOC=y diff --git a/arch/sh/configs/se7724_defconfig b/arch/sh/configs/se7724_defconfig index d572655f842d63..3968133d3a2b8a 100644 --- a/arch/sh/configs/se7724_defconfig +++ b/arch/sh/configs/se7724_defconfig @@ -83,7 +83,6 @@ CONFIG_LOGO=y # CONFIG_LOGO_SUPERH_VGA16 is not set CONFIG_SOUND=y CONFIG_SND=m -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_DRIVERS is not set # CONFIG_SND_SPI is not set # CONFIG_SND_SUPERH is not set diff --git a/arch/sh/configs/sh7785lcr_32bit_defconfig b/arch/sh/configs/sh7785lcr_32bit_defconfig index 17d2471d8e515f..87d9c7a4ca3cbb 100644 --- a/arch/sh/configs/sh7785lcr_32bit_defconfig +++ b/arch/sh/configs/sh7785lcr_32bit_defconfig @@ -93,7 +93,6 @@ CONFIG_SND_PCM_OSS=y CONFIG_SND_SEQUENCER_OSS=y CONFIG_SND_HRTIMER=y CONFIG_SND_DYNAMIC_MINORS=y -# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_VERBOSE_PRINTK=y CONFIG_SND_DEBUG=y diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 48db44fa56feb1..4e7bc370ffd7f2 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -155,7 +155,7 @@ config SND_MAX_CARDS config SND_SUPPORT_OLD_API bool "Support old ALSA API" - default y + default n help Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3 or older). From 18223eececd66365c12275f09042e6fcb2ac5748 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:19 +0100 Subject: [PATCH 31/55] of: base: Add of_property_read_u8_index Add support for of_property_read_u8_index(), simillar to others u16 and u32 variants. Having this helper makes the code more tidy in isome cases, specially when we are parsing multiple of these into data structures. Signed-off-by: Srinivas Kandagatla Reviewed-by: Rob Herring (Arm) Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/of/property.c | 33 +++++++++++++++++++++++++++++++++ include/linux/of.h | 9 +++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index c1feb631e3831d..4e3524227720a5 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -147,6 +147,39 @@ static void *of_find_property_value_of_size(const struct device_node *np, return prop->value; } +/** + * of_property_read_u8_index - Find and read a u8 from a multi-value property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the u8 in the list of values + * @out_value: pointer to return value, modified only if no error. + * + * Search for a property in a device node and read nth 8-bit value from + * it. + * + * Return: 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u8 value can be decoded. + */ +int of_property_read_u8_index(const struct device_node *np, + const char *propname, + u32 index, u8 *out_value) +{ + const u8 *val = of_find_property_value_of_size(np, propname, + ((index + 1) * sizeof(*out_value)), + 0, NULL); + + if (IS_ERR(val)) + return PTR_ERR(val); + + *out_value = val[index]; + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u8_index); + /** * of_property_read_u16_index - Find and read a u16 from a multi-value property. * diff --git a/include/linux/of.h b/include/linux/of.h index 121a288ca92df7..57fb598b72d3db 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -316,6 +316,9 @@ extern struct property *of_find_property(const struct device_node *np, extern bool of_property_read_bool(const struct device_node *np, const char *propname); extern int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size); +extern int of_property_read_u8_index(const struct device_node *np, + const char *propname, + u32 index, u8 *out_value); extern int of_property_read_u16_index(const struct device_node *np, const char *propname, u32 index, u16 *out_value); @@ -646,6 +649,12 @@ static inline int of_property_count_elems_of_size(const struct device_node *np, return -ENOSYS; } +static inline int of_property_read_u8_index(const struct device_node *np, + const char *propname, u32 index, u8 *out_value) +{ + return -ENOSYS; +} + static inline int of_property_read_u16_index(const struct device_node *np, const char *propname, u32 index, u16 *out_value) { From 6504fe8cd21f624fcf21d3b60e64cf10d213cf75 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:20 +0100 Subject: [PATCH 32/55] soundwire: qcom: remove unused rd_fifo_depth remove read fifo depth field parsing logic, as rd_fifo_depth is never used in the driver. Cleaning this up would benefit when adding new variant support which includes adding variant fields for rd_fifo_depth. ex: Glymur has this rd_fifo_depth register fields changed from v2.x Signed-off-by: Srinivas Kandagatla Reviewed-by: Konrad Dybcio Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 5b3078220189be..f3ad53ec6e76cf 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -209,7 +209,6 @@ struct qcom_swrm_ctrl { int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val); u32 slave_status; u32 wr_fifo_depth; - u32 rd_fifo_depth; bool clock_stop_not_supported; }; @@ -898,7 +897,6 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) swrm_wait_for_frame_gen_enabled(ctrl); ctrl->slave_status = 0; ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); - ctrl->rd_fifo_depth = FIELD_GET(SWRM_COMP_PARAMS_RD_FIFO_DEPTH, val); ctrl->wr_fifo_depth = FIELD_GET(SWRM_COMP_PARAMS_WR_FIFO_DEPTH, val); return 0; From 8114a05cdc20d3d8eb1b8639da33c193b53fe073 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:21 +0100 Subject: [PATCH 33/55] dt-bindings: soundwire: qcom: deprecate qcom,din/out-ports Number of input and output ports can be dynamically read from the controller registers, getting this value from Device Tree is redundant and potentially lead to bugs. Mark these two properties as deprecated in device tree bindings. Signed-off-by: Srinivas Kandagatla Reviewed-by: Krzysztof Kozlowski Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/soundwire/qcom,soundwire.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml index 95d947fda6a705..7ea491f7e82872 100644 --- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml +++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml @@ -73,10 +73,12 @@ properties: qcom,din-ports: $ref: /schemas/types.yaml#/definitions/uint32 description: count of data in ports + deprecated: true qcom,dout-ports: $ref: /schemas/types.yaml#/definitions/uint32 description: count of data out ports + deprecated: true qcom,ports-word-length: $ref: /schemas/types.yaml#/definitions/uint8-array @@ -223,8 +225,6 @@ required: - '#sound-dai-cells' - '#address-cells' - '#size-cells' - - qcom,dout-ports - - qcom,din-ports - qcom,ports-offset1 - qcom,ports-offset2 @@ -257,9 +257,6 @@ examples: clocks = <&lpass_rx_macro>; clock-names = "iface"; - qcom,din-ports = <0>; - qcom,dout-ports = <5>; - resets = <&lpass_audiocc LPASS_AUDIO_SWR_RX_CGCR>; reset-names = "swr_audio_cgcr"; From 9e53a66a2f2fd2c379fa1bd782e5cee37b0f75d3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:22 +0100 Subject: [PATCH 34/55] soundwire: qcom: deprecate qcom,din/out-ports Number of input and output ports can be dynamically read from the controller registers, getting this value from Device Tree is redundant and potentially lead to bugs. Remove the code parsing this property along with marking this as deprecated in device tree bindings. Signed-off-by: Srinivas Kandagatla Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-5-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 141 +++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 79 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index f3ad53ec6e76cf..9e6dc43c1112a0 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -128,7 +128,6 @@ #define MAX_FREQ_NUM 1 #define TIMEOUT_MS 100 #define QCOM_SWRM_MAX_RD_LEN 0x1 -#define QCOM_SDW_MAX_PORTS 14 #define DEFAULT_CLK_FREQ 9600000 #define SWRM_MAX_DAIS 0xF #define SWR_INVALID_PARAM 0xFF @@ -195,6 +194,7 @@ struct qcom_swrm_ctrl { int wake_irq; int num_din_ports; int num_dout_ports; + int nports; int cols_index; int rows_index; unsigned long port_mask; @@ -202,7 +202,7 @@ struct qcom_swrm_ctrl { u8 rcmd_id; u8 wcmd_id; /* Port numbers are 1 - 14 */ - struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS + 1]; + struct qcom_swrm_port_config *pconfig; struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS]; enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val); @@ -1153,7 +1153,6 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, struct snd_pcm_hw_params *params, int direction) { - struct sdw_port_config pconfig[QCOM_SDW_MAX_PORTS]; struct sdw_stream_config sconfig; struct sdw_master_runtime *m_rt; struct sdw_slave_runtime *s_rt; @@ -1162,6 +1161,10 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, unsigned long *port_mask; int maxport, pn, nports = 0, ret = 0; unsigned int m_port; + struct sdw_port_config *pconfig __free(kfree) = kcalloc(ctrl->nports, + sizeof(*pconfig), GFP_KERNEL); + if (!pconfig) + return -ENOMEM; if (direction == SNDRV_PCM_STREAM_CAPTURE) sconfig.direction = SDW_DATA_DIR_TX; @@ -1186,8 +1189,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, continue; port_mask = &ctrl->port_mask; - maxport = ctrl->num_dout_ports + ctrl->num_din_ports; - + maxport = ctrl->nports; list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { slave = s_rt->slave; @@ -1347,17 +1349,8 @@ static int qcom_swrm_register_dais(struct qcom_swrm_ctrl *ctrl) static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) { struct device_node *np = ctrl->dev->of_node; - u8 off1[QCOM_SDW_MAX_PORTS]; - u8 off2[QCOM_SDW_MAX_PORTS]; - u16 si[QCOM_SDW_MAX_PORTS]; - u8 bp_mode[QCOM_SDW_MAX_PORTS] = { 0, }; - u8 hstart[QCOM_SDW_MAX_PORTS]; - u8 hstop[QCOM_SDW_MAX_PORTS]; - u8 word_length[QCOM_SDW_MAX_PORTS]; - u8 blk_group_count[QCOM_SDW_MAX_PORTS]; - u8 lane_control[QCOM_SDW_MAX_PORTS]; - int i, ret, nports, val; - bool si_16 = false; + struct qcom_swrm_port_config *pcfg; + int i, ret, val; ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); @@ -1365,88 +1358,78 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) ctrl->num_din_ports = FIELD_GET(SWRM_COMP_PARAMS_DIN_PORTS_MASK, val); ret = of_property_read_u32(np, "qcom,din-ports", &val); - if (ret) - return ret; - - if (val > ctrl->num_din_ports) - return -EINVAL; + if (!ret) { /* only if present */ + if (val != ctrl->num_din_ports) { + dev_err(ctrl->dev, "din-ports (%d) mismatch with controller (%d)", + val, ctrl->num_din_ports); + } - ctrl->num_din_ports = val; + ctrl->num_din_ports = val; + } ret = of_property_read_u32(np, "qcom,dout-ports", &val); - if (ret) - return ret; + if (!ret) { /* only if present */ + if (val != ctrl->num_dout_ports) { + dev_err(ctrl->dev, "dout-ports (%d) mismatch with controller (%d)", + val, ctrl->num_dout_ports); + } - if (val > ctrl->num_dout_ports) - return -EINVAL; + ctrl->num_dout_ports = val; + } - ctrl->num_dout_ports = val; + ctrl->nports = ctrl->num_dout_ports + ctrl->num_din_ports; - nports = ctrl->num_dout_ports + ctrl->num_din_ports; - if (nports > QCOM_SDW_MAX_PORTS) - return -EINVAL; + ctrl->pconfig = devm_kcalloc(ctrl->dev, ctrl->nports + 1, + sizeof(*ctrl->pconfig), GFP_KERNEL); + if (!ctrl->pconfig) + return -ENOMEM; - /* Valid port numbers are from 1-14, so mask out port 0 explicitly */ set_bit(0, &ctrl->port_mask); + /* Valid port numbers are from 1, so mask out port 0 explicitly */ + for (i = 0; i < ctrl->nports; i++) { + pcfg = &ctrl->pconfig[i + 1]; - ret = of_property_read_u8_array(np, "qcom,ports-offset1", - off1, nports); - if (ret) - return ret; - - ret = of_property_read_u8_array(np, "qcom,ports-offset2", - off2, nports); - if (ret) - return ret; - - ret = of_property_read_u8_array(np, "qcom,ports-sinterval-low", - (u8 *)si, nports); - if (ret) { - ret = of_property_read_u16_array(np, "qcom,ports-sinterval", - si, nports); + ret = of_property_read_u8_index(np, "qcom,ports-offset1", i, &pcfg->off1); if (ret) return ret; - si_16 = true; - } - ret = of_property_read_u8_array(np, "qcom,ports-block-pack-mode", - bp_mode, nports); - if (ret) { - if (ctrl->version <= SWRM_VERSION_1_3_0) - memset(bp_mode, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - else + ret = of_property_read_u8_index(np, "qcom,ports-offset2", i, &pcfg->off2); + if (ret) return ret; - } - memset(hstart, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - of_property_read_u8_array(np, "qcom,ports-hstart", hstart, nports); + ret = of_property_read_u8_index(np, "qcom,ports-sinterval-low", i, (u8 *)&pcfg->si); + if (ret) { + ret = of_property_read_u16_index(np, "qcom,ports-sinterval", i, &pcfg->si); + if (ret) + return ret; + } + + ret = of_property_read_u8_index(np, "qcom,ports-block-pack-mode", + i, &pcfg->bp_mode); + if (ret) { + if (ctrl->version <= SWRM_VERSION_1_3_0) + pcfg->bp_mode = SWR_INVALID_PARAM; + else + return ret; + } - memset(hstop, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - of_property_read_u8_array(np, "qcom,ports-hstop", hstop, nports); + /* Optional properties */ + pcfg->hstart = SWR_INVALID_PARAM; + pcfg->hstop = SWR_INVALID_PARAM; + pcfg->word_length = SWR_INVALID_PARAM; + pcfg->blk_group_count = SWR_INVALID_PARAM; + pcfg->lane_control = SWR_INVALID_PARAM; - memset(word_length, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - of_property_read_u8_array(np, "qcom,ports-word-length", word_length, nports); + of_property_read_u8_index(np, "qcom,ports-hstart", i, &pcfg->hstart); - memset(blk_group_count, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - of_property_read_u8_array(np, "qcom,ports-block-group-count", blk_group_count, nports); + of_property_read_u8_index(np, "qcom,ports-hstop", i, &pcfg->hstop); - memset(lane_control, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); - of_property_read_u8_array(np, "qcom,ports-lane-control", lane_control, nports); + of_property_read_u8_index(np, "qcom,ports-word-length", i, &pcfg->word_length); - for (i = 0; i < nports; i++) { - /* Valid port number range is from 1-14 */ - if (si_16) - ctrl->pconfig[i + 1].si = si[i]; - else - ctrl->pconfig[i + 1].si = ((u8 *)si)[i]; - ctrl->pconfig[i + 1].off1 = off1[i]; - ctrl->pconfig[i + 1].off2 = off2[i]; - ctrl->pconfig[i + 1].bp_mode = bp_mode[i]; - ctrl->pconfig[i + 1].hstart = hstart[i]; - ctrl->pconfig[i + 1].hstop = hstop[i]; - ctrl->pconfig[i + 1].word_length = word_length[i]; - ctrl->pconfig[i + 1].blk_group_count = blk_group_count[i]; - ctrl->pconfig[i + 1].lane_control = lane_control[i]; + of_property_read_u8_index(np, "qcom,ports-block-group-count", + i, &pcfg->blk_group_count); + + of_property_read_u8_index(np, "qcom,ports-lane-control", i, &pcfg->lane_control); } return 0; From 6ed85ea1b17b0d2d1f345ded22891c8ca17a9ac8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:23 +0100 Subject: [PATCH 35/55] soundwire: qcom: prepare for v3.x cleanup the register layout structs to prepare for adding new 3.x controller support. Signed-off-by: Srinivas Kandagatla Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-6-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 77 +++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 9e6dc43c1112a0..5baf831f49abab 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -99,14 +99,15 @@ #define SWRM_MCP_SLV_STATUS 0x1090 #define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0) #define SWRM_MCP_SLV_STATUS_SZ 2 -#define SWRM_DP_PORT_CTRL_BANK(n, m) (0x1124 + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DP_PORT_CTRL_2_BANK(n, m) (0x1128 + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DP_BLOCK_CTRL_1(n) (0x112C + 0x100 * (n - 1)) -#define SWRM_DP_BLOCK_CTRL2_BANK(n, m) (0x1130 + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DP_PORT_HCTRL_BANK(n, m) (0x1134 + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (0x1138 + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DP_SAMPLECTRL2_BANK(n, m) (0x113C + 0x100 * (n - 1) + 0x40 * m) -#define SWRM_DIN_DPn_PCM_PORT_CTRL(n) (0x1054 + 0x100 * (n - 1)) + +#define SWRM_DPn_PORT_CTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DPn_PORT_CTRL_2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DPn_BLOCK_CTRL_1(offset, n) (offset + 0x100 * (n - 1)) +#define SWRM_DPn_BLOCK_CTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DPn_PORT_HCTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DPn_BLOCK_CTRL3_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DPn_SAMPLECTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) + #define SWR_V1_3_MSTR_MAX_REG_ADDR 0x1740 #define SWR_V2_0_MSTR_MAX_REG_ADDR 0x50ac @@ -171,6 +172,13 @@ enum { SWRM_REG_CMD_FIFO_RD_CMD, SWRM_REG_CMD_FIFO_STATUS, SWRM_REG_CMD_FIFO_RD_FIFO_ADDR, + SWRM_OFFSET_DP_PORT_CTRL_BANK, + SWRM_OFFSET_DP_PORT_CTRL_2_BANK, + SWRM_OFFSET_DP_BLOCK_CTRL_1, + SWRM_OFFSET_DP_BLOCK_CTRL2_BANK, + SWRM_OFFSET_DP_PORT_HCTRL_BANK, + SWRM_OFFSET_DP_BLOCK_CTRL3_BANK, + SWRM_OFFSET_DP_SAMPLECTRL2_BANK, }; struct qcom_swrm_ctrl { @@ -230,6 +238,13 @@ static const unsigned int swrm_v1_3_reg_layout[] = { [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V1_3_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V1_3_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR, + [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, + [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, + [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, + [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, + [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, + [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, + [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c, }; static const struct qcom_swrm_data swrm_v1_3_data = { @@ -264,6 +279,13 @@ static const unsigned int swrm_v2_0_reg_layout[] = { [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, + [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, + [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, + [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, + [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, + [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, + [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, + [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c, }; static const struct qcom_swrm_data swrm_v2_0_data = { @@ -964,10 +986,10 @@ static int qcom_swrm_port_params(struct sdw_bus *bus, unsigned int bank) { struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + u32 offset = ctrl->reg_layout[SWRM_OFFSET_DP_BLOCK_CTRL_1]; - return ctrl->reg_write(ctrl, SWRM_DP_BLOCK_CTRL_1(p_params->num), - p_params->bps - 1); - + return ctrl->reg_write(ctrl, SWRM_DPn_BLOCK_CTRL_1(offset, p_params->num), + p_params->bps - 1); } static int qcom_swrm_transport_params(struct sdw_bus *bus, @@ -977,9 +999,11 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); struct qcom_swrm_port_config *pcfg; u32 value; - int reg = SWRM_DP_PORT_CTRL_BANK((params->port_num), bank); + int reg, offset = ctrl->reg_layout[SWRM_OFFSET_DP_PORT_CTRL_BANK]; int ret; + reg = SWRM_DPn_PORT_CTRL_BANK(offset, params->port_num, bank); + pcfg = &ctrl->pconfig[params->port_num]; value = pcfg->off1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT; @@ -991,15 +1015,19 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, goto err; if (pcfg->si > 0xff) { + offset = ctrl->reg_layout[SWRM_OFFSET_DP_SAMPLECTRL2_BANK]; value = (pcfg->si >> 8) & 0xff; - reg = SWRM_DP_SAMPLECTRL2_BANK(params->port_num, bank); + reg = SWRM_DPn_SAMPLECTRL2_BANK(offset, params->port_num, bank); + ret = ctrl->reg_write(ctrl, reg, value); if (ret) goto err; } if (pcfg->lane_control != SWR_INVALID_PARAM) { - reg = SWRM_DP_PORT_CTRL_2_BANK(params->port_num, bank); + offset = ctrl->reg_layout[SWRM_OFFSET_DP_PORT_CTRL_2_BANK]; + reg = SWRM_DPn_PORT_CTRL_2_BANK(offset, params->port_num, bank); + value = pcfg->lane_control; ret = ctrl->reg_write(ctrl, reg, value); if (ret) @@ -1007,20 +1035,23 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, } if (pcfg->blk_group_count != SWR_INVALID_PARAM) { - reg = SWRM_DP_BLOCK_CTRL2_BANK(params->port_num, bank); + offset = ctrl->reg_layout[SWRM_OFFSET_DP_BLOCK_CTRL2_BANK]; + + reg = SWRM_DPn_BLOCK_CTRL2_BANK(offset, params->port_num, bank); + value = pcfg->blk_group_count; ret = ctrl->reg_write(ctrl, reg, value); if (ret) goto err; } - if (pcfg->hstart != SWR_INVALID_PARAM - && pcfg->hstop != SWR_INVALID_PARAM) { - reg = SWRM_DP_PORT_HCTRL_BANK(params->port_num, bank); + offset = ctrl->reg_layout[SWRM_OFFSET_DP_PORT_HCTRL_BANK]; + reg = SWRM_DPn_PORT_HCTRL_BANK(offset, params->port_num, bank); + + if (pcfg->hstart != SWR_INVALID_PARAM && pcfg->hstop != SWR_INVALID_PARAM) { value = (pcfg->hstop << 4) | pcfg->hstart; ret = ctrl->reg_write(ctrl, reg, value); } else { - reg = SWRM_DP_PORT_HCTRL_BANK(params->port_num, bank); value = (SWR_HSTOP_MAX_VAL << 4) | SWR_HSTART_MIN_VAL; ret = ctrl->reg_write(ctrl, reg, value); } @@ -1029,7 +1060,8 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, goto err; if (pcfg->bp_mode != SWR_INVALID_PARAM) { - reg = SWRM_DP_BLOCK_CTRL3_BANK(params->port_num, bank); + offset = ctrl->reg_layout[SWRM_OFFSET_DP_BLOCK_CTRL3_BANK]; + reg = SWRM_DPn_BLOCK_CTRL3_BANK(offset, params->port_num, bank); ret = ctrl->reg_write(ctrl, reg, pcfg->bp_mode); } @@ -1041,9 +1073,12 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, struct sdw_enable_ch *enable_ch, unsigned int bank) { - u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank); + u32 reg; struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); u32 val; + u32 offset = ctrl->reg_layout[SWRM_OFFSET_DP_PORT_CTRL_BANK]; + + reg = SWRM_DPn_PORT_CTRL_BANK(offset, enable_ch->port_num, bank); ctrl->reg_read(ctrl, reg, &val); From 66eca4b7d2d2d7b35625bea47f2ebdac2eab3615 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:24 +0100 Subject: [PATCH 36/55] dt-bindings: soundwire: qcom: Document v3.1.0 version of IP block Add bindings for Soundwire controllers v3.1.0, which is available on Qualcomm Glymur SoC. Signed-off-by: Srinivas Kandagatla Reviewed-by: Krzysztof Kozlowski Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-7-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml index 7ea491f7e82872..003023729fb8ca 100644 --- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml +++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml @@ -23,6 +23,7 @@ properties: - qcom,soundwire-v1.6.0 - qcom,soundwire-v1.7.0 - qcom,soundwire-v2.0.0 + - qcom,soundwire-v3.1.0 - items: - enum: - qcom,soundwire-v2.1.0 From b2bfe0fa1f85acd928eaa407a89411c01a4f87a8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 12 Sep 2025 09:32:25 +0100 Subject: [PATCH 37/55] soundwire: qcom: adding support for v3.1.0 Add support for controller version v3.1.0, which has changes in register layout and some register fields compared to v2.0. Signed-off-by: Srinivas Kandagatla Tested-by: Alexey Klimov # sm8550 Link: https://patch.msgid.link/20250912083225.228778-8-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 5baf831f49abab..17afc5aa8b44e0 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -31,6 +31,7 @@ #define SWRM_VERSION_1_5_1 0x01050001 #define SWRM_VERSION_1_7_0 0x01070000 #define SWRM_VERSION_2_0_0 0x02000000 +#define SWRM_VERSION_3_1_0 0x03010000 #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) @@ -40,6 +41,9 @@ #define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15) #define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) +#define SWRM_V3_COMP_PARAMS_WR_FIFO_DEPTH GENMASK(17, 10) +#define SWRM_V3_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(23, 18) + #define SWRM_COMP_MASTER_ID 0x104 #define SWRM_V1_3_INTERRUPT_STATUS 0x200 #define SWRM_V2_0_INTERRUPT_STATUS 0x5000 @@ -296,6 +300,32 @@ static const struct qcom_swrm_data swrm_v2_0_data = { .reg_layout = swrm_v2_0_reg_layout, }; +static const unsigned int swrm_v3_0_reg_layout[] = { + [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, + [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, + [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ + [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, + [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, + [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, + [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, + [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, + [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, + [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1224, + [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1228, + [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x122c, + [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1230, + [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1234, + [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1238, + [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x123c, +}; + +static const struct qcom_swrm_data swrm_v3_0_data = { + .default_rows = 50, + .default_cols = 16, + .sw_clk_gate_required = true, + .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v3_0_reg_layout, +}; #define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, @@ -919,7 +949,11 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) swrm_wait_for_frame_gen_enabled(ctrl); ctrl->slave_status = 0; ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); - ctrl->wr_fifo_depth = FIELD_GET(SWRM_COMP_PARAMS_WR_FIFO_DEPTH, val); + + if (ctrl->version >= SWRM_VERSION_3_1_0) + ctrl->wr_fifo_depth = FIELD_GET(SWRM_V3_COMP_PARAMS_WR_FIFO_DEPTH, val); + else + ctrl->wr_fifo_depth = FIELD_GET(SWRM_COMP_PARAMS_WR_FIFO_DEPTH, val); return 0; } @@ -1785,6 +1819,7 @@ static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.6.0", .data = &swrm_v1_6_data }, { .compatible = "qcom,soundwire-v1.7.0", .data = &swrm_v1_5_data }, { .compatible = "qcom,soundwire-v2.0.0", .data = &swrm_v2_0_data }, + { .compatible = "qcom,soundwire-v3.1.0", .data = &swrm_v3_0_data }, {/* sentinel */}, }; From 4be4ac36f6ca5d3854049581be3aca20345bbe01 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:44 +0800 Subject: [PATCH 38/55] soundwire: cadence_master: make frame index trace more readable The existing code uses i as the frame index which is 0 based. And "bad frame %d/%d", i, num_frames where num_frames is 1 based. Change the frame index to i + 1 to align with num_frames and more readable. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 21bb491d026b44..c59a98b408b3d0 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2495,14 +2495,14 @@ int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer, ret = check_frame_start(header, counter); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d start header %x\n", - __func__, i, num_frames, header); + __func__, i + 1, num_frames, header); return ret; } ret = check_frame_end(footer); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d end footer %x\n", - __func__, i, num_frames, footer); + __func__, i + 1, num_frames, footer); return ret; } @@ -2573,7 +2573,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf ret = check_frame_start(header, counter); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d start header %x\n", - __func__, i, num_frames, header); + __func__, i + 1, num_frames, header); return ret; } @@ -2588,7 +2588,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf if (crc != expected_crc) { dev_err(dev, "%s: bad frame %d/%d crc %#x expected %#x\n", - __func__, i, num_frames, crc, expected_crc); + __func__, i + 1, num_frames, crc, expected_crc); return -EIO; } @@ -2599,7 +2599,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf ret = check_frame_end(footer); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d end footer %x\n", - __func__, i, num_frames, footer); + __func__, i + 1, num_frames, footer); return ret; } From 43b2a39183c738095a5f0b3f00fd2e57e5a48149 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:45 +0800 Subject: [PATCH 39/55] soundwire: only compute BPT stream in sdw_compute_dp0_port_params DP0 is only for BPT stream. We should not compute and update the port params of the normal audio streams. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index c18f0c16f92973..530ac66ac6fad2 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -124,6 +124,9 @@ static void sdw_compute_dp0_port_params(struct sdw_bus *bus) struct sdw_master_runtime *m_rt; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + /* DP0 is for BPT only */ + if (m_rt->stream->type != SDW_STREAM_BPT) + continue; sdw_compute_dp0_master_ports(m_rt); sdw_compute_dp0_slave_ports(m_rt); } From 5a838e010c64b794ac990e9b19bfb0bf7083a1f2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:46 +0800 Subject: [PATCH 40/55] soundwire: cadence_master: set data_per_frame as frame capability data_per_frame will be used for preparing the TX buffer and we may add some fake frames to ensure the data in the buffer will be flushed. So that it should indicate the frame capability even if the required data bytes are less than the frame capability. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index c59a98b408b3d0..17834141090948 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2114,9 +2114,6 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ if (!actual_bpt_bytes) return -EINVAL; - if (data_bytes < actual_bpt_bytes) - actual_bpt_bytes = data_bytes; - /* * the caller may want to set the number of bytes per frame, * allow when possible @@ -2126,6 +2123,9 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ *data_per_frame = actual_bpt_bytes; + if (data_bytes < actual_bpt_bytes) + actual_bpt_bytes = data_bytes; + if (command == 0) { /* * for writes we need to send all the data_bytes per frame, From 57b3a7b27e0b72df4ccee89719de12719aa9d547 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:47 +0800 Subject: [PATCH 41/55] soundwire: cadence: export sdw_cdns_bpt_find_bandwidth Currently, we calculate the required bandwidth after the PDI buffer size is calculated. However as we need to add some fake frame to align the data block size, the final PDI size and the frame number will change. Besides, we need the required bandwidth to decide the DMA channel number and the channel number will be used to calculate the data block size. Therefore, we calculate the required bandwidth and export a helper for the caller to get the required bandwidth. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 30 ++++++++++++++++++++++++++++++ drivers/soundwire/cadence_master.h | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 17834141090948..57671f9b3e9cda 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2094,6 +2094,36 @@ static unsigned int sdw_cdns_read_pdi1_buffer_size(unsigned int actual_data_size return total * 2; } +int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */ + int row, int col, int frame_rate, + unsigned int *tx_dma_bandwidth, + unsigned int *rx_dma_bandwidth) +{ + unsigned int bpt_bits = row * (col - 1); + unsigned int bpt_bytes = bpt_bits >> 3; + unsigned int pdi0_buffer_size; + unsigned int pdi1_buffer_size; + unsigned int data_per_frame; + + data_per_frame = sdw_cdns_bra_actual_data_size(bpt_bytes); + if (!data_per_frame) + return -EINVAL; + + if (command == 0) { + pdi0_buffer_size = sdw_cdns_write_pdi0_buffer_size(data_per_frame); + pdi1_buffer_size = SDW_CDNS_WRITE_PDI1_BUFFER_SIZE; + } else { + pdi0_buffer_size = SDW_CDNS_READ_PDI0_BUFFER_SIZE; + pdi1_buffer_size = sdw_cdns_read_pdi1_buffer_size(data_per_frame); + } + + *tx_dma_bandwidth = pdi0_buffer_size * 8 * frame_rate; + *rx_dma_bandwidth = pdi1_buffer_size * 8 * frame_rate; + + return 0; +} +EXPORT_SYMBOL(sdw_cdns_bpt_find_bandwidth); + int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 9373426c7f630b..6830d7f2d7726a 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -209,6 +209,11 @@ void sdw_cdns_config_update(struct sdw_cdns *cdns); int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns); /* SoundWire BPT/BRA helpers to format data */ +int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */ + int row, int col, int frame_rate, + unsigned int *tx_dma_bandwidth, + unsigned int *rx_dma_bandwidth); + int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, From 167efc6dfd621494c6a7e47115dc829dcc0e502c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:48 +0800 Subject: [PATCH 42/55] ASoC: SOF: Intel: export hda_sdw_bpt_get_buf_size_aligment The dma buffer need to be a multiple of data block size and the fifo size. Export a function to return the LCM of data block size and the fifo size. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- include/sound/hda-sdw-bpt.h | 7 +++++++ sound/soc/sof/intel/hda-sdw-bpt.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sound/hda-sdw-bpt.h b/include/sound/hda-sdw-bpt.h index f649549b75d52d..9b654c31829ad6 100644 --- a/include/sound/hda-sdw-bpt.h +++ b/include/sound/hda-sdw-bpt.h @@ -30,6 +30,8 @@ int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, struct snd_dma_buffer *dmab_rx_bdl); + +unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth); #else static inline int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, @@ -64,6 +66,11 @@ static inline int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream * WARN_ONCE(1, "SoundWire BPT is disabled"); return -EOPNOTSUPP; } + +static inline unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth) +{ + return 0; +} #endif #endif /* __HDA_SDW_BPT_H */ diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index ff5abccf0d88b6..e45dd051ab8c26 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -10,6 +10,7 @@ * Hardware interface for SoundWire BPT support with HDA DMA */ +#include #include #include #include @@ -236,6 +237,18 @@ static int hda_sdw_bpt_dma_disable(struct device *dev, struct hdac_ext_stream *s return ret; } +#define FIFO_ALIGNMENT 64 + +unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth) +{ + unsigned int num_channels = DIV_ROUND_UP(dma_bandwidth, BPT_FREQUENCY * 32); + unsigned int data_block = num_channels * 4; + unsigned int alignment = lcm(data_block, FIFO_ALIGNMENT); + + return alignment; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_get_buf_size_alignment, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); + int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes, u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream, From 9468bc0e1b95b6c737a79ae8aaeb87c16caeb3af Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Oct 2025 11:14:49 +0800 Subject: [PATCH 43/55] soundwire: cadence_master: add fake_size parameter to sdw_cdns_prepare_read_dma_buffer We may need to add few fake frames to fit the aligned read dma buffer size. Add a fake_size parameter to allow the caller to set the fake data size. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251014031450.3781789-7-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 39 +++++++++++++++++++++++++++++- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel_ace2x.c | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 57671f9b3e9cda..4e94da28d8ad8c 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2393,7 +2393,7 @@ EXPORT_SYMBOL(sdw_cdns_prepare_write_dma_buffer); int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size, int data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes) + int *dma_buffer_total_bytes, unsigned int fake_size) { int total_dma_data_written = 0; u8 *p_dma_buffer = dma_buffer; @@ -2445,6 +2445,43 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_si if (ret < 0) return ret; + counter++; + + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; + } + + /* Add fake frame */ + header[0] &= ~GENMASK(7, 6); /* Set inactive flag in BPT/BRA frame heade */ + while (fake_size >= data_per_frame) { + header[1] = data_per_frame; + ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, + dma_buffer_size, &dma_data_written, + counter); + if (ret < 0) + return ret; + + counter++; + + fake_size -= data_per_frame; + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; + } + + if (fake_size) { + header[1] = fake_size; + ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, + dma_buffer_size, &dma_data_written, + counter); + if (ret < 0) + return ret; + + counter++; + + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; total_dma_data_written += dma_data_written; } diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 6830d7f2d7726a..a269a87486fc6d 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -226,7 +226,7 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size, int data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes); + int *dma_buffer_total_bytes, unsigned int fake_size); int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size, int num_frames); diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 5d08364ad6d141..22118b053d56ab 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -177,7 +177,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, - pdi0_buffer_size, &tx_total_bytes); + pdi0_buffer_size, &tx_total_bytes, 0); } if (!ret) From 8931f5bce4f159a0dd438c093255d88cb8e00516 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 30 Oct 2025 15:02:53 +0800 Subject: [PATCH 44/55] soundwire: intel_ace2x: add fake frame to BRA read command Intel DMA buffer size need to be a multiple of data block size. Find the minimal fake data size and extra buffer size to meet the requirement. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251030070253.1216871-8-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_ace2x.c | 61 ++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 22118b053d56ab..e11a0cf771936a 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -44,6 +44,8 @@ static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_r return ret; } +#define READ_PDI1_MIN_SIZE 12 + static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { @@ -53,15 +55,23 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_stream_runtime *stream; struct sdw_stream_config sconfig; struct sdw_port_config *pconfig; + unsigned int pdi0_buf_size_pre_frame; + unsigned int pdi1_buf_size_pre_frame; unsigned int pdi0_buffer_size; unsigned int tx_dma_bandwidth; unsigned int pdi1_buffer_size; unsigned int rx_dma_bandwidth; + unsigned int fake_num_frames; unsigned int data_per_frame; unsigned int tx_total_bytes; struct sdw_cdns_pdi *pdi0; struct sdw_cdns_pdi *pdi1; + unsigned int rx_alignment; + unsigned int tx_alignment; unsigned int num_frames; + unsigned int fake_size; + unsigned int tx_pad; + unsigned int rx_pad; int command; int ret1; int ret; @@ -138,6 +148,13 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1; + ret = sdw_cdns_bpt_find_bandwidth(command, cdns->bus.params.row, + cdns->bus.params.col, + prop->default_frame_rate, + &tx_dma_bandwidth, &rx_dma_bandwidth); + if (ret < 0) + goto deprepare_stream; + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame, &pdi0_buffer_size, &pdi1_buffer_size, &num_frames); @@ -148,10 +165,43 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size; sdw->bpt_ctx.num_frames = num_frames; sdw->bpt_ctx.data_per_frame = data_per_frame; - tx_dma_bandwidth = div_u64((u64)pdi0_buffer_size * 8 * (u64)prop->default_frame_rate, - num_frames); - rx_dma_bandwidth = div_u64((u64)pdi1_buffer_size * 8 * (u64)prop->default_frame_rate, - num_frames); + + rx_alignment = hda_sdw_bpt_get_buf_size_alignment(rx_dma_bandwidth); + tx_alignment = hda_sdw_bpt_get_buf_size_alignment(tx_dma_bandwidth); + + if (command) { /* read */ + /* Get buffer size of a full frame */ + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, + cdns->bus.params.col, + data_per_frame, SDW_BPT_MSG_MAX_BYTES, + &data_per_frame, &pdi0_buf_size_pre_frame, + &pdi1_buf_size_pre_frame, &fake_num_frames); + if (ret < 0) + goto deprepare_stream; + + /* find fake pdi1 buffer size */ + rx_pad = rx_alignment - (pdi1_buffer_size % rx_alignment); + while (rx_pad <= READ_PDI1_MIN_SIZE) + rx_pad += rx_alignment; + + pdi1_buffer_size += rx_pad; + /* It is fine if we request more than enough byte to read */ + fake_num_frames = DIV_ROUND_UP(rx_pad, pdi1_buf_size_pre_frame); + fake_size = fake_num_frames * data_per_frame; + + /* find fake pdi0 buffer size */ + pdi0_buffer_size += (fake_num_frames * pdi0_buf_size_pre_frame); + tx_pad = tx_alignment - (pdi0_buffer_size % tx_alignment); + pdi0_buffer_size += tx_pad; + } else { /* write */ + /* + * For the write command, the rx data block is 4, and the rx buffer size of a frame + * is 8. So the rx buffer size (pdi0_buffer_size) is always a multiple of rx + * alignment. + */ + tx_pad = tx_alignment - (pdi0_buffer_size % tx_alignment); + pdi0_buffer_size += tx_pad; + } dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", msg->len, num_frames, data_per_frame); @@ -177,7 +227,8 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, - pdi0_buffer_size, &tx_total_bytes, 0); + pdi0_buffer_size, &tx_total_bytes, + fake_size); } if (!ret) From fdfa1960eee7591995cf877e9caf9cf5794ab91f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 21 Oct 2025 17:43:52 +0800 Subject: [PATCH 45/55] soundwire: introduce BPT section Currently we send a BRA message with a start address with continuous registers in a BPT stream. However, a codec may need to write different register sections shortly. Introduce a register section in struct sdw_btp_msg which contain register start address, length, and buffer. This commit uses only 1 section for each BPT message. And we need to add up all BPT section length and check if it reach maximum BPT bytes. No function changes. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Tested-by: Shuming Fan Link: https://patch.msgid.link/20251021094355.132943-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 10 ++++++++-- drivers/soundwire/bus.h | 22 ++++++++++++++++------ drivers/soundwire/debugfs.c | 14 +++++++++++--- drivers/soundwire/intel_ace2x.c | 22 +++++++++++++--------- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 55c1db81653400..fb68738dfb9b84 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -2052,8 +2052,14 @@ EXPORT_SYMBOL(sdw_clear_slave_status); int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { - if (msg->len > SDW_BPT_MSG_MAX_BYTES) { - dev_err(bus->dev, "Invalid BPT message length %d\n", msg->len); + int len = 0; + int i; + + for (i = 0; i < msg->sections; i++) + len += msg->sec[i].len; + + if (len > SDW_BPT_MSG_MAX_BYTES) { + dev_err(bus->dev, "Invalid BPT message length %d\n", len); return -EINVAL; } diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 02651fbb683aca..8115c64dd48e2f 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,21 +73,31 @@ struct sdw_msg { }; /** - * struct sdw_btp_msg - Message structure + * struct sdw_btp_section - Message section structure * @addr: Start Register address accessed in the Slave * @len: number of bytes to transfer. More than 64Kb can be transferred * but a practical limit of SDW_BPT_MSG_MAX_BYTES is enforced. - * @dev_num: Slave device number - * @flags: transfer flags, indicate if xfer is read or write - * @buf: message data buffer (filled by host for write, filled + * @buf: section data buffer (filled by host for write, filled * by Peripheral hardware for reads) */ -struct sdw_bpt_msg { +struct sdw_bpt_section { u32 addr; u32 len; + u8 *buf; +}; + +/** + * struct sdw_btp_msg - Message structure + * @sec: Pointer to array of sections + * @sections: Number of sections in the array + * @dev_num: Slave device number + * @flags: transfer flags, indicate if xfer is read or write + */ +struct sdw_bpt_msg { + struct sdw_bpt_section *sec; + int sections; u8 dev_num; u8 flags; - u8 *buf; }; #define SDW_DOUBLE_RATE_FACTOR 2 diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index 1e0f9318b61656..6068011dd0d989 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -222,15 +222,23 @@ DEFINE_DEBUGFS_ATTRIBUTE(set_num_bytes_fops, NULL, static int do_bpt_sequence(struct sdw_slave *slave, bool write, u8 *buffer) { struct sdw_bpt_msg msg = {0}; + struct sdw_bpt_section *sec; - msg.addr = start_addr; - msg.len = num_bytes; + sec = kcalloc(1, sizeof(*sec), GFP_KERNEL); + if (!sec) + return -ENOMEM; + msg.sections = 1; + + sec[0].addr = start_addr; + sec[0].len = num_bytes; + + msg.sec = sec; msg.dev_num = slave->dev_num; if (write) msg.flags = SDW_MSG_FLAG_WRITE; else msg.flags = SDW_MSG_FLAG_READ; - msg.buf = buffer; + sec[0].buf = buffer; return sdw_bpt_send_sync(slave->bus, slave, &msg); } diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index e11a0cf771936a..a0f708a7cdff00 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -156,8 +156,9 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * goto deprepare_stream; ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, - msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame, - &pdi0_buffer_size, &pdi1_buffer_size, &num_frames); + msg->sec[0].len, SDW_BPT_MSG_MAX_BYTES, + &data_per_frame, &pdi0_buffer_size, &pdi1_buffer_size, + &num_frames); if (ret < 0) goto deprepare_stream; @@ -204,7 +205,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * } dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", - msg->len, num_frames, data_per_frame); + msg->sec[0].len, num_frames, data_per_frame); dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n", pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth); @@ -219,12 +220,14 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * } if (!command) { - ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->addr, msg->buf, - msg->len, data_per_frame, + ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->sec[0].addr, + msg->sec[0].buf, + msg->sec[0].len, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes); } else { - ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len, + ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->sec[0].addr, + msg->sec[0].len, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes, @@ -305,9 +308,9 @@ static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *s struct sdw_cdns *cdns = &sdw->cdns; int ret; - if (msg->len < INTEL_BPT_MSG_BYTE_MIN) { + if (msg->sec[0].len < INTEL_BPT_MSG_BYTE_MIN) { dev_err(cdns->dev, "BPT message length %d is less than the minimum bytes %d\n", - msg->len, INTEL_BPT_MSG_BYTE_MIN); + msg->sec[0].len, INTEL_BPT_MSG_BYTE_MIN); return -EINVAL; } @@ -367,7 +370,8 @@ static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave, } else { ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, sdw->bpt_ctx.pdi1_buffer_size, - msg->buf, msg->len, sdw->bpt_ctx.num_frames, + msg->sec[0].buf, msg->sec[0].len, + sdw->bpt_ctx.num_frames, sdw->bpt_ctx.data_per_frame); if (ret < 0) dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret); From fe8a9cf75c1efc659dbb5f53d744e6f4e8552dda Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 21 Oct 2025 17:43:53 +0800 Subject: [PATCH 46/55] soundwire: pass sdw_bpt_section to cdns BPT helpers We can get start_register, data_size, and buffer data from the new sdw_bpt_section parameter. Also, handle all register sections in the cdns BRA helpers. No function changes as section number is 1. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Tested-by: Shuming Fan Link: https://patch.msgid.link/20251021094355.132943-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 186 +++++++++++++++++------------ drivers/soundwire/cadence_master.h | 12 +- drivers/soundwire/intel_ace2x.c | 11 +- 3 files changed, 123 insertions(+), 86 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4e94da28d8ad8c..a106e5e482c8e3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2324,17 +2324,20 @@ static int sdw_cdns_prepare_read_pd0_buffer(u8 *header, unsigned int header_size #define CDNS_BPT_ROLLING_COUNTER_START 1 -int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size, - int data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes) +int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec, + int data_per_frame, u8 *dma_buffer, + int dma_buffer_size, int *dma_buffer_total_bytes) { int total_dma_data_written = 0; u8 *p_dma_buffer = dma_buffer; u8 header[SDW_CDNS_BRA_HDR]; + unsigned int start_register; + unsigned int section_size; int dma_data_written; - u8 *p_data = data; + u8 *p_data; u8 counter; int ret; + int i; counter = CDNS_BPT_ROLLING_COUNTER_START; @@ -2342,47 +2345,57 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, header[0] |= GENMASK(7, 6); /* header is active */ header[0] |= (dev_num << 2); - while (data_size >= data_per_frame) { - header[1] = data_per_frame; - header[2] = start_register >> 24 & 0xFF; - header[3] = start_register >> 16 & 0xFF; - header[4] = start_register >> 8 & 0xFF; - header[5] = start_register >> 0 & 0xFF; - - ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR, - p_data, data_per_frame, - p_dma_buffer, dma_buffer_size, - &dma_data_written, counter); - if (ret < 0) - return ret; + for (i = 0; i < num_sec; i++) { + start_register = sec[i].addr; + section_size = sec[i].len; + p_data = sec[i].buf; - counter++; + while (section_size >= data_per_frame) { + header[1] = data_per_frame; + header[2] = start_register >> 24 & 0xFF; + header[3] = start_register >> 16 & 0xFF; + header[4] = start_register >> 8 & 0xFF; + header[5] = start_register >> 0 & 0xFF; - p_data += data_per_frame; - data_size -= data_per_frame; + ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR, + p_data, data_per_frame, + p_dma_buffer, dma_buffer_size, + &dma_data_written, counter); + if (ret < 0) + return ret; - p_dma_buffer += dma_data_written; - dma_buffer_size -= dma_data_written; - total_dma_data_written += dma_data_written; + counter++; - start_register += data_per_frame; - } + p_data += data_per_frame; + section_size -= data_per_frame; - if (data_size) { - header[1] = data_size; - header[2] = start_register >> 24 & 0xFF; - header[3] = start_register >> 16 & 0xFF; - header[4] = start_register >> 8 & 0xFF; - header[5] = start_register >> 0 & 0xFF; + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; - ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR, - p_data, data_size, - p_dma_buffer, dma_buffer_size, - &dma_data_written, counter); - if (ret < 0) - return ret; + start_register += data_per_frame; + } - total_dma_data_written += dma_data_written; + if (section_size) { + header[1] = section_size; + header[2] = start_register >> 24 & 0xFF; + header[3] = start_register >> 16 & 0xFF; + header[4] = start_register >> 8 & 0xFF; + header[5] = start_register >> 0 & 0xFF; + + ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR, + p_data, section_size, + p_dma_buffer, dma_buffer_size, + &dma_data_written, counter); + if (ret < 0) + return ret; + + counter++; + + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; + } } *dma_buffer_total_bytes = total_dma_data_written; @@ -2391,16 +2404,19 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, } EXPORT_SYMBOL(sdw_cdns_prepare_write_dma_buffer); -int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size, +int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec, int data_per_frame, u8 *dma_buffer, int dma_buffer_size, int *dma_buffer_total_bytes, unsigned int fake_size) { int total_dma_data_written = 0; u8 *p_dma_buffer = dma_buffer; u8 header[SDW_CDNS_BRA_HDR]; + unsigned int start_register; + unsigned int data_size; int dma_data_written; u8 counter; int ret; + int i; counter = CDNS_BPT_ROLLING_COUNTER_START; @@ -2408,48 +2424,52 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_si header[0] |= GENMASK(7, 6); /* header is active */ header[0] |= (dev_num << 2); - while (data_size >= data_per_frame) { - header[1] = data_per_frame; - header[2] = start_register >> 24 & 0xFF; - header[3] = start_register >> 16 & 0xFF; - header[4] = start_register >> 8 & 0xFF; - header[5] = start_register >> 0 & 0xFF; + for (i = 0; i < num_sec; i++) { + start_register = sec[i].addr; + data_size = sec[i].len; + while (data_size >= data_per_frame) { + header[1] = data_per_frame; + header[2] = start_register >> 24 & 0xFF; + header[3] = start_register >> 16 & 0xFF; + header[4] = start_register >> 8 & 0xFF; + header[5] = start_register >> 0 & 0xFF; - ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, - dma_buffer_size, &dma_data_written, - counter); - if (ret < 0) - return ret; + ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, + p_dma_buffer, dma_buffer_size, + &dma_data_written, counter); + if (ret < 0) + return ret; - counter++; + counter++; - data_size -= data_per_frame; + data_size -= data_per_frame; - p_dma_buffer += dma_data_written; - dma_buffer_size -= dma_data_written; - total_dma_data_written += dma_data_written; + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; - start_register += data_per_frame; - } + start_register += data_per_frame; + } - if (data_size) { - header[1] = data_size; - header[2] = start_register >> 24 & 0xFF; - header[3] = start_register >> 16 & 0xFF; - header[4] = start_register >> 8 & 0xFF; - header[5] = start_register >> 0 & 0xFF; + if (data_size) { + header[1] = data_size; + header[2] = start_register >> 24 & 0xFF; + header[3] = start_register >> 16 & 0xFF; + header[4] = start_register >> 8 & 0xFF; + header[5] = start_register >> 0 & 0xFF; - ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, - dma_buffer_size, &dma_data_written, - counter); - if (ret < 0) - return ret; + ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, + p_dma_buffer, dma_buffer_size, + &dma_data_written, counter); + if (ret < 0) + return ret; - counter++; + counter++; - p_dma_buffer += dma_data_written; - dma_buffer_size -= dma_data_written; - total_dma_data_written += dma_data_written; + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; + } } /* Add fake frame */ @@ -2616,9 +2636,12 @@ static u8 extract_read_data(u32 *data, int num_bytes, u8 *buffer) } int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size, - u8 *buffer, int buffer_size, int num_frames, int data_per_frame) + struct sdw_bpt_section *sec, int num_sec, int num_frames, + int data_per_frame) { int total_num_bytes = 0; + int buffer_size = 0; + int sec_index; u32 *p_data; u8 *p_buf; int counter; @@ -2632,7 +2655,10 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf counter = CDNS_BPT_ROLLING_COUNTER_START; p_data = (u32 *)dma_buffer; - p_buf = buffer; + + sec_index = 0; + p_buf = sec[sec_index].buf; + buffer_size = sec[sec_index].len; for (i = 0; i < num_frames; i++) { header = *p_data++; @@ -2672,6 +2698,18 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf counter++; counter &= GENMASK(3, 0); + + if (buffer_size == total_num_bytes && (i + 1) < num_frames) { + sec_index++; + if (sec_index >= num_sec) { + dev_err(dev, "%s: incorrect section index %d i %d\n", + __func__, sec_index, i); + return -EINVAL; + } + p_buf = sec[sec_index].buf; + buffer_size = sec[sec_index].len; + total_num_bytes = 0; + } } return 0; } diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index a269a87486fc6d..668f807cff4b23 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ /* Copyright(c) 2015-17 Intel Corporation. */ #include +#include "bus.h" #ifndef __SDW_CADENCE_H #define __SDW_CADENCE_H @@ -220,11 +221,11 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ unsigned int *data_per_frame, unsigned int *pdi0_buffer_size, unsigned int *pdi1_buffer_size, unsigned int *num_frames); -int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size, - int data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes); +int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec, + int data_per_frame, u8 *dma_buffer, + int dma_buffer_size, int *dma_buffer_total_bytes); -int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size, +int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec, int data_per_frame, u8 *dma_buffer, int dma_buffer_size, int *dma_buffer_total_bytes, unsigned int fake_size); @@ -232,5 +233,6 @@ int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size, int num_frames); int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size, - u8 *buffer, int buffer_size, int num_frames, int data_per_frame); + struct sdw_bpt_section *sec, int num_sec, int num_frames, + int data_per_frame); #endif /* __SDW_CADENCE_H */ diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index a0f708a7cdff00..300ede6bc7f132 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -220,14 +220,12 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * } if (!command) { - ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->sec[0].addr, - msg->sec[0].buf, - msg->sec[0].len, data_per_frame, + ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->sec, 1, + data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes); } else { - ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->sec[0].addr, - msg->sec[0].len, + ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->sec, 1, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes, @@ -370,8 +368,7 @@ static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave, } else { ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, sdw->bpt_ctx.pdi1_buffer_size, - msg->sec[0].buf, msg->sec[0].len, - sdw->bpt_ctx.num_frames, + msg->sec, 1, sdw->bpt_ctx.num_frames, sdw->bpt_ctx.data_per_frame); if (ret < 0) dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret); From 188d194be2bfe03afcc02c90d9d905b46a17f3ef Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 21 Oct 2025 17:43:54 +0800 Subject: [PATCH 47/55] soundwire: intel_ace2x: handle multi BPT sections Calculate required PDI buffer and pass the section number to the cdns BPT helpers. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Tested-by: Shuming Fan Link: https://patch.msgid.link/20251021094355.132943-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_ace2x.c | 46 ++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 300ede6bc7f132..1ed0251d259236 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -57,6 +57,8 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_port_config *pconfig; unsigned int pdi0_buf_size_pre_frame; unsigned int pdi1_buf_size_pre_frame; + unsigned int pdi0_buffer_size_; + unsigned int pdi1_buffer_size_; unsigned int pdi0_buffer_size; unsigned int tx_dma_bandwidth; unsigned int pdi1_buffer_size; @@ -68,6 +70,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_cdns_pdi *pdi1; unsigned int rx_alignment; unsigned int tx_alignment; + unsigned int num_frames_; unsigned int num_frames; unsigned int fake_size; unsigned int tx_pad; @@ -76,6 +79,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * int ret1; int ret; int dir; + int len; int i; stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT); @@ -155,12 +159,25 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * if (ret < 0) goto deprepare_stream; - ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, - msg->sec[0].len, SDW_BPT_MSG_MAX_BYTES, - &data_per_frame, &pdi0_buffer_size, &pdi1_buffer_size, - &num_frames); - if (ret < 0) - goto deprepare_stream; + len = 0; + pdi0_buffer_size = 0; + pdi1_buffer_size = 0; + num_frames = 0; + /* Add up pdi buffer size and frame numbers of each BPT sections */ + for (i = 0; i < msg->sections; i++) { + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, + cdns->bus.params.col, + msg->sec[i].len, SDW_BPT_MSG_MAX_BYTES, + &data_per_frame, &pdi0_buffer_size_, + &pdi1_buffer_size_, &num_frames_); + if (ret < 0) + goto deprepare_stream; + + len += msg->sec[i].len; + pdi0_buffer_size += pdi0_buffer_size_; + pdi1_buffer_size += pdi1_buffer_size_; + num_frames += num_frames_; + } sdw->bpt_ctx.pdi0_buffer_size = pdi0_buffer_size; sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size; @@ -205,7 +222,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * } dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", - msg->sec[0].len, num_frames, data_per_frame); + len, num_frames, data_per_frame); dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n", pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth); @@ -220,12 +237,12 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * } if (!command) { - ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->sec, 1, + ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->sec, msg->sections, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes); } else { - ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->sec, 1, + ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->sec, msg->sections, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, pdi0_buffer_size, &tx_total_bytes, @@ -304,11 +321,16 @@ static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *s struct sdw_bpt_msg *msg) { struct sdw_cdns *cdns = &sdw->cdns; + int len = 0; int ret; + int i; + + for (i = 0; i < msg->sections; i++) + len += msg->sec[i].len; - if (msg->sec[0].len < INTEL_BPT_MSG_BYTE_MIN) { + if (len < INTEL_BPT_MSG_BYTE_MIN) { dev_err(cdns->dev, "BPT message length %d is less than the minimum bytes %d\n", - msg->sec[0].len, INTEL_BPT_MSG_BYTE_MIN); + len, INTEL_BPT_MSG_BYTE_MIN); return -EINVAL; } @@ -368,7 +390,7 @@ static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave, } else { ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, sdw->bpt_ctx.pdi1_buffer_size, - msg->sec, 1, sdw->bpt_ctx.num_frames, + msg->sec, msg->sections, sdw->bpt_ctx.num_frames, sdw->bpt_ctx.data_per_frame); if (ret < 0) dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret); From 9a97857db0c5655b8932f86b5d18bb959079b0ee Mon Sep 17 00:00:00 2001 From: Andres J Rosa Date: Wed, 3 Dec 2025 10:25:01 -0600 Subject: [PATCH 48/55] ALSA: uapi: Fix typo in asound.h comment Fix 'level-shit' to 'level-shift' in struct snd_cea_861_aud_if comment. Fixes: 7ba1c40b536e ("ALSA: Add definitions for CEA-861 Audio InfoFrames") Signed-off-by: Andres J Rosa Link: https://patch.msgid.link/20251203162509.1822-1-andyrosa@gmail.com Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 5a049eeaeccea5..d3ce75ba938a84 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -60,7 +60,7 @@ struct snd_cea_861_aud_if { unsigned char db2_sf_ss; /* sample frequency and size */ unsigned char db3; /* not used, all zeros */ unsigned char db4_ca; /* channel allocation code */ - unsigned char db5_dminh_lsv; /* downmix inhibit & level-shit values */ + unsigned char db5_dminh_lsv; /* downmix inhibit & level-shift values */ }; /**************************************************************************** From b0ff70e9d4fe46cece25eb97b9b9b0166624af95 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Sun, 7 Dec 2025 03:38:12 +0800 Subject: [PATCH 49/55] ASoC: cs35l41: Always return 0 when a subsystem ID is found When trying to get the system name in the _HID path, after successfully retrieving the subsystem ID the return value isn't set to 0 but instead still kept at -ENODATA, leading to a false negative: [ 12.382507] cs35l41 spi-VLV1776:00: Subsystem ID: VLV1776 [ 12.382521] cs35l41 spi-VLV1776:00: probe with driver cs35l41 failed with error -61 Always return 0 when a subsystem ID is found to mitigate these false negatives. Link: https://github.com/CachyOS/CachyOS-Handheld/issues/83 Fixes: 46c8b4d2a693 ("ASoC: cs35l41: Fallback to reading Subsystem ID property if not ACPI") Cc: stable@vger.kernel.org # 6.18 Signed-off-by: Eric Naim Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20251206193813.56955-1-dnaim@cachyos.org Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 3a8a8dd065b772..ee56dfceedeb0a 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1188,13 +1188,14 @@ static int cs35l41_get_system_name(struct cs35l41_private *cs35l41) } } -err: if (sub) { cs35l41->dsp.system_name = sub; dev_info(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name); - } else - dev_warn(cs35l41->dev, "Subsystem ID not found\n"); + return 0; + } +err: + dev_warn(cs35l41->dev, "Subsystem ID not found\n"); return ret; } From 298e753880b6ea99ac30df34959a7a03b0878eed Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 9 Dec 2025 13:16:41 +0800 Subject: [PATCH 50/55] ALSA: firewire-motu: add bounds check in put_user loop for DSP events In the DSP event handling code, a put_user() loop copies event data. When the user buffer size is not aligned to 4 bytes, it could overwrite beyond the buffer boundary. Fix by adding a bounds check before put_user(). Suggested-by: Takashi Iwai Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB788112C72AF8A1C8C448B4B8AFA3A@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/firewire/motu/motu-hwdep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 6675b23aad69ef..89dc436a065299 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -75,7 +75,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, while (consumed < count && snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { ptr = (u32 __user *)(buf + consumed); - if (put_user(ev, ptr)) + if (consumed + sizeof(ev) > count || put_user(ev, ptr)) return -EFAULT; consumed += sizeof(ev); } From a061deb0b4d56a9213bc05c1abe626fdd303ee52 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Dec 2025 10:46:57 +0000 Subject: [PATCH 51/55] ASoC: cs35l56: Fix incorrect select SND_SOC_CS35L56_CAL_SYSFS_COMMON KConfig SND_SOC_CS35L56_CAL_SET_CTRL must select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON. It incorrectly selected SND_SOC_CS35L56_CAL_SYSFS_COMMON. Fixes: 32172cf3cb54 ("ASoC: cs35l56: Allow restoring factory calibration through ALSA control") Reported-by: Randy Dunlap Link: https://lore.kernel.org/lkml/22b92ddf-6321-41b5-8073-f9c7064d3432@infradead.org/T/#u Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251209104657.485541-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c245a0b09cc6a3..6161ce318dd027 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -917,7 +917,7 @@ config SND_SOC_CS35L56_CAL_DEBUGFS config SND_SOC_CS35L56_CAL_SET_CTRL bool "CS35L56 ALSA control to restore factory calibration" default N - select SND_SOC_CS35L56_CAL_SYSFS_COMMON + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON help Allow restoring factory calibration data through an ALSA control. This is only needed on platforms without UEFI or From f34836a8ddf9216ff919927cddb705022bf30aab Mon Sep 17 00:00:00 2001 From: Hemalatha Pinnamreddy Date: Wed, 3 Dec 2025 17:31:34 +0530 Subject: [PATCH 52/55] ASoC: amd: acp: update tdm channels for specific DAI TDM channel updates were applied to all DAIs, causing configurations to overwrite for unrelated streams. The logic is modified to update channels only for targeted DAI. This prevents corruption of other DAI settings and resolves audio issues observed during system suspend and resume cycles. Fixes: 12229b7e50cf ("ASoC: amd: acp: Add TDM support for acp i2s stream") Signed-off-by: Hemalatha Pinnamreddy Signed-off-by: Raghavendra Prasad Mallela Link: https://patch.msgid.link/20251203120136.2591395-1-raghavendraprasad.mallela@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 4ba0a66981ea9d..283a674c7e2c32 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -157,6 +157,8 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas spin_lock_irq(&chip->acp_lock); list_for_each_entry(stream, &chip->stream_list, list) { + if (dai->id != stream->dai_id) + continue; switch (chip->acp_rev) { case ACP_RN_PCI_ID: case ACP_RMB_PCI_ID: From 161a0c617ab172bbcda7ce61803addeb2124dbff Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Dec 2025 14:15:51 +0100 Subject: [PATCH 53/55] ALSA: hda: intel-dsp-config: Prefer legacy driver as fallback When config table entries don't match with the device to be probed, currently we fall back to SND_INTEL_DSP_DRIVER_ANY, which means to allow any drivers to bind with it. This was set so with the assumption (or hope) that all controller drivers should cover the devices generally, but in practice, this caused a problem as reported recently. Namely, when a specific kconfig for SOF isn't set for the modern Intel chips like Alderlake, a wrong driver (AVS) got probed and failed. This is because we have entries like: #if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE) /* Alder Lake / Raptor Lake */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S, }, .... #endif so this entry is effective only when CONFIG_SND_SOC_SOF_ALDERLAKE is set. If not set, there is no matching entry, hence it returns SND_INTEL_DSP_DRIVER_ANY as fallback. OTOH, if the kconfig is set, it explicitly falls back to SND_INTEL_DSP_DRIVER_LEGACY when no DMIC or SoundWire is found -- that was the working scenario. That being said, the current setup may be broken for modern Intel chips that are supposed to work with either SOF or legacy driver when the corresponding kconfig were missing. For addressing the problem above, this patch changes the fallback driver to the legacy driver, i.e. return SND_INTEL_DSP_DRIVER_LEGACY type as much as possible. When CONFIG_SND_HDA_INTEL is also disabled, the fallback is set to SND_INTEL_DSP_DRIVER_ANY type, just to be sure. Reported-by: Askar Safin Closes: https://lore.kernel.org/all/20251014034156.4480-1-safinaskar@gmail.com/ Tested-by: Askar Safin Reviewed-by: Peter Ujfalusi Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251210131553.184404-1-tiwai@suse.de --- sound/hda/core/intel-dsp-config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c index c401c06584213e..0c25e87408de2a 100644 --- a/sound/hda/core/intel-dsp-config.c +++ b/sound/hda/core/intel-dsp-config.c @@ -718,7 +718,8 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) /* find the configuration for the specific device */ cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); if (!cfg) - return SND_INTEL_DSP_DRIVER_ANY; + return IS_ENABLED(CONFIG_SND_HDA_INTEL) ? + SND_INTEL_DSP_DRIVER_LEGACY : SND_INTEL_DSP_DRIVER_ANY; if (cfg->flags & FLAG_SOF) { if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && From e33a6abdb744e3a015dd53e997c690081a8b985d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 11 Dec 2025 10:50:03 +0900 Subject: [PATCH 54/55] ALSA: hda: cix-ipbloq: Use modern PM ops When building without CONFIG_PM_SLEEP, there are several warnings (or errors with CONFIG_WERROR=y / W=e) from the cix-ipbloq driver: sound/hda/controllers/cix-ipbloq.c:378:12: error: 'cix_ipbloq_hda_runtime_resume' defined but not used [-Werror=unused-function] 378 | static int cix_ipbloq_hda_runtime_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/hda/controllers/cix-ipbloq.c:362:12: error: 'cix_ipbloq_hda_runtime_suspend' defined but not used [-Werror=unused-function] 362 | static int cix_ipbloq_hda_runtime_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/hda/controllers/cix-ipbloq.c:349:12: error: 'cix_ipbloq_hda_resume' defined but not used [-Werror=unused-function] 349 | static int cix_ipbloq_hda_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~ sound/hda/controllers/cix-ipbloq.c:336:12: error: 'cix_ipbloq_hda_suspend' defined but not used [-Werror=unused-function] 336 | static int cix_ipbloq_hda_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~ When CONFIG_PM and CONFIG_PM_SLEEP are unset, SET_SYSTEM_SLEEP_PM_OPS() and SET_RUNTIME_PM_OPS() evaluate to nothing, so these functions appear unused to the compiler in this configuration. Use the modern SYSTEM_SLEEP_PM_OPS and RUNTIME_PM_OPS macros to resolve these warnings, which is what they are intended to do. Additionally, wrap &cix_ipbloq_hda_pm in pm_ptr() to ensure the compiler can drop the entire structure when CONFIG_PM is unset. Fixes: d91e9bd10125 ("ALSA: hda: add CIX IPBLOQ HDA controller support") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20251211-hda-cix-ipbloq-modern-pm-ops-v1-1-c7a5580af021@kernel.org Signed-off-by: Takashi Iwai --- sound/hda/controllers/cix-ipbloq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/hda/controllers/cix-ipbloq.c b/sound/hda/controllers/cix-ipbloq.c index cc9153692ef53e..99f9f48e91d4b4 100644 --- a/sound/hda/controllers/cix-ipbloq.c +++ b/sound/hda/controllers/cix-ipbloq.c @@ -407,10 +407,10 @@ static int cix_ipbloq_hda_runtime_resume(struct device *dev) } static const struct dev_pm_ops cix_ipbloq_hda_pm = { - SET_SYSTEM_SLEEP_PM_OPS(cix_ipbloq_hda_suspend, - cix_ipbloq_hda_resume) - SET_RUNTIME_PM_OPS(cix_ipbloq_hda_runtime_suspend, - cix_ipbloq_hda_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cix_ipbloq_hda_suspend, + cix_ipbloq_hda_resume) + RUNTIME_PM_OPS(cix_ipbloq_hda_runtime_suspend, + cix_ipbloq_hda_runtime_resume, NULL) }; static const struct of_device_id cix_ipbloq_hda_match[] = { @@ -422,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cix_ipbloq_hda_match); static struct platform_driver cix_ipbloq_hda_driver = { .driver = { .name = "cix-ipbloq-hda", - .pm = &cix_ipbloq_hda_pm, + .pm = pm_ptr(&cix_ipbloq_hda_pm), .of_match_table = cix_ipbloq_hda_match, }, .probe = cix_ipbloq_hda_probe, From fd324768eb2c132bb111ba76675b4c72406251d9 Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Thu, 11 Dec 2025 17:24:26 +0800 Subject: [PATCH 55/55] ALSA: hda/tas2781: Add new quirk for HP new project Add new vendor_id and subsystem_id in quirk for HP new project (NexusX). Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20251211092427.1648-1-baojun.xu@ti.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 9510aa2f1bda5d..171a71457ec3b9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6770,6 +6770,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e9e, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),