From 41212ff3e4ed475d9f91d26b5324075087702987 Mon Sep 17 00:00:00 2001 From: Fu Hao Date: Mon, 15 Jun 2026 20:22:40 +0800 Subject: [PATCH 001/791] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio Add the new PCI ID 0x1d94 0x14a9 for Hygon family 18h model 5h HDA controller. Signed-off-by: Fu Hao Link: https://patch.msgid.link/8b38f2941375553e4246167736c6acb5a541e833.1781523812.git.fuhao@open-hieco.net Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 4b03c64e72ab..ed7662fce680 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -100,6 +100,8 @@ enum { #define ATIHDMI_NUM_CAPTURE 0 #define ATIHDMI_NUM_PLAYBACK 8 +/* Hygon HD Audio controller */ +#define PCI_DEVICE_ID_HYGON_18H_M05H_HDA 0x14a9 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -241,6 +243,7 @@ enum { AZX_DRIVER_ZHAOXIN, AZX_DRIVER_ZHAOXINHDMI, AZX_DRIVER_LOONGSON, + AZX_DRIVER_HYGON, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -360,6 +363,7 @@ static const char * const driver_short_names[] = { [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin", [AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI", [AZX_DRIVER_LOONGSON] = "HDA Loongson", + [AZX_DRIVER_HYGON] = "HDA Hygon", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; @@ -2836,6 +2840,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, + /* Hygon HDAudio */ + { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_HDA), + .driver_data = AZX_DRIVER_HYGON | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); From e096c24e7f97fee6b9a026ab3b57eb776708d5bb Mon Sep 17 00:00:00 2001 From: Fu Hao Date: Mon, 15 Jun 2026 20:23:25 +0800 Subject: [PATCH 002/791] ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h On Hygon family 18h model 5h controller, some registers such as GCTL, SD_CTL and SD_CTL_3B should be accessed in dword, or the writing will fail. Signed-off-by: Fu Hao Link: https://patch.msgid.link/dd4a0b6743751dfb495a371c135ac052191e99f6.1781523812.git.fuhao@open-hieco.net Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 4 ++++ sound/hda/core/controller.c | 10 +++++++-- sound/hda/core/stream.c | 40 +++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index ed7662fce680..192f5c044b7a 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -1930,6 +1930,10 @@ static int azx_first_init(struct azx *chip) if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI) bus->polling_mode = 1; + if (chip->driver_type == AZX_DRIVER_HYGON && + chip->pci->device == PCI_DEVICE_ID_HYGON_18H_M05H_HDA) + bus->access_sdnctl_in_dword = 1; + bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio"); if (IS_ERR(bus->remap_addr)) return PTR_ERR(bus->remap_addr); diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index 69e11d62bbfa..6312ad7af71d 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -511,7 +511,10 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) { unsigned long timeout; - snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); + if (bus->access_sdnctl_in_dword) + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); + else + snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); timeout = jiffies + msecs_to_jiffies(100); while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) @@ -576,7 +579,10 @@ static void azx_int_disable(struct hdac_bus *bus) /* disable interrupts in stream descriptor */ list_for_each_entry(azx_dev, &bus->stream_list, list) - snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_INT_MASK, 0); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); /* disable SIE for all streams & disable controller CIE and GIE */ snd_hdac_chip_writel(bus, INTCTL, 0); diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c index b471a038b314..0091c4ef6486 100644 --- a/sound/hda/core/stream.c +++ b/sound/hda/core/stream.c @@ -146,8 +146,12 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev) stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream); else stripe_ctl = 0; - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, - stripe_ctl); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); } /* set DMA start and interrupt mask */ if (bus->access_sdnctl_in_dword) @@ -166,11 +170,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start); */ static void snd_hdac_stream_clear(struct hdac_stream *azx_dev) { - snd_hdac_stream_updateb(azx_dev, SD_CTL, - SD_CTL_DMA_START | SD_INT_MASK, 0); - snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ - if (azx_dev->stripe) - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + struct hdac_bus *bus = azx_dev->bus; + + if (bus->access_sdnctl_in_dword) { + snd_hdac_stream_updatel(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + if (azx_dev->stripe) + snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + } else { + snd_hdac_stream_updateb(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + if (azx_dev->stripe) + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + } + azx_dev->running = false; } @@ -225,12 +240,16 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) { unsigned char val; int dma_run_state; + struct hdac_bus *bus = azx_dev->bus; snd_hdac_stream_clear(azx_dev); dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; - snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); /* wait for hardware to report that the stream entered reset */ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300); @@ -238,7 +257,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) if (azx_dev->bus->dma_stop_delay && dma_run_state) udelay(azx_dev->bus->dma_stop_delay); - snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); /* wait for hardware to report that the stream is out of reset */ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300); From 3213bf9885e3356fff12ac18b12d4c2fb4687734 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Fri, 19 Jun 2026 01:00:26 +0800 Subject: [PATCH 003/791] ALSA: usb-audio: Release components on probe errors usb_audio_probe() can create USB-audio component resources before the first interface is recorded in chip->num_interfaces. If a later probe step fails, the error path drops chip->active and frees the card directly when no interfaces have been registered. Normal disconnect first releases USB-audio components in a fixed order before the card is freed. The first-interface probe error path skipped that sequence, so partially initialized PCM, endpoint, MIDI, media, or mixer resources could be left for the card private_free path without their disconnect handling having run. Move the existing normal-disconnect component release sequence into a helper and call it from the zero-interface probe error path before snd_card_free(). Keep the normal disconnect ordering unchanged: PCM streams, endpoint resources, MIDI 1.0 resources, MIDI 2.0 resources, media device cleanup, then mixer resources. Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260618170026.192212-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/card.c | 70 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 6a3b576fb067..b36f513dccb9 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -905,6 +905,40 @@ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) return 0; } +static void usb_audio_disconnect_components(struct snd_usb_audio *chip) +{ + struct snd_usb_stream *as; + struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; + struct list_head *p; + + /* release the pcm resources */ + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); + } + /* release the endpoint resources */ + list_for_each_entry(ep, &chip->ep_list, list) { + snd_usb_endpoint_release(ep); + } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } + snd_usb_midi_v2_disconnect_all(chip); + /* + * Nice to check quirk && quirk->shares_media_device and + * then call the snd_media_device_delete(). Don't have + * access to the quirk here. snd_media_device_delete() + * accesses mixer_list + */ + snd_media_device_delete(chip); + + /* release mixer resources */ + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); + } +} + /* * probe the active usb device * @@ -1077,8 +1111,10 @@ static int usb_audio_probe(struct usb_interface *intf, * decrement before memory is possibly returned. */ atomic_dec(&chip->active); - if (!chip->num_interfaces) + if (!chip->num_interfaces) { + usb_audio_disconnect_components(chip); snd_card_free(chip->card); + } } return err; } @@ -1091,48 +1127,18 @@ static bool __usb_audio_disconnect(struct usb_interface *intf, struct snd_usb_audio *chip, struct snd_card *card) { - struct list_head *p; - guard(mutex)(®ister_mutex); if (platform_ops && platform_ops->disconnect_cb) platform_ops->disconnect_cb(chip); if (atomic_inc_return(&chip->shutdown) == 1) { - struct snd_usb_stream *as; - struct snd_usb_endpoint *ep; - struct usb_mixer_interface *mixer; - /* wait until all pending tasks done; * they are protected by snd_usb_lock_shutdown() */ snd_refcount_sync(&chip->usage_count); snd_card_disconnect(card); - /* release the pcm resources */ - list_for_each_entry(as, &chip->pcm_list, list) { - snd_usb_stream_disconnect(as); - } - /* release the endpoint resources */ - list_for_each_entry(ep, &chip->ep_list, list) { - snd_usb_endpoint_release(ep); - } - /* release the midi resources */ - list_for_each(p, &chip->midi_list) { - snd_usbmidi_disconnect(p); - } - snd_usb_midi_v2_disconnect_all(chip); - /* - * Nice to check quirk && quirk->shares_media_device and - * then call the snd_media_device_delete(). Don't have - * access to the quirk here. snd_media_device_delete() - * accesses mixer_list - */ - snd_media_device_delete(chip); - - /* release mixer resources */ - list_for_each_entry(mixer, &chip->mixer_list, list) { - snd_usb_mixer_disconnect(mixer); - } + usb_audio_disconnect_components(chip); } if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) From f199040982eac619bf9b3231af284dbf2e74f179 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Jun 2026 17:00:34 +0200 Subject: [PATCH 004/791] ALSA: pcm: Notify disconnected state at mmap data fault, too When a device gets disconnected, the driver waits until the all opened file descriptors are closed before releasing the device. Although PCM core emits an error to any further syscalls (except for close), some applications don't notice the disconnected state because they mainly access only via mmap, and it may block the device release unnecessarily too long. This patch is an attempt to improve the behavior by adding the notification of the PCM disconnected state at mmap data faulting. When the device is disconnected, it now returns VM_FAULT_SIGBUS, so that the application can notice the invalid PCM state. Reported-and-tested-by: Ai Chao Link: https://lore.kernel.org/20260615132657.2097213-1-aichao@kylinos.cn> Link: https://patch.msgid.link/20260619150035.1560937-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7dc0060617f1..d4e04b5088c5 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3907,6 +3907,8 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) if (substream == NULL) return VM_FAULT_SIGBUS; runtime = substream->runtime; + if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED) + return VM_FAULT_SIGBUS; offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) From 961d9f98da0d80e9c7895ffa98ce4c1c38ea48fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20H=C3=B6ner?= Date: Tue, 23 Jun 2026 21:14:36 +0200 Subject: [PATCH 005/791] ALSA: hda/realtek: Enable internal speakers on Razer Blade 16 (2025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Razer Blade 16 (2025) (Realtek ALC298, PCI SSID 1a58:300e) the internal speakers are driven through an external smart amplifier whose crossover, voicing and protection live in a DSP reached over a vendor coef mailbox on NID 0x20. Both speaker pins are fed from one DAC through a mixer and the amplifier does the crossover. The BIOS leaves the tweeter pin (NID 0x14) disabled (default config 0x411111f0), so the tweeters are not exposed and the amplifier is never programmed for this machine under Linux. Add a fixup for the machine that: - re-exposes the tweeter pin (NID 0x14) as an internal speaker in the same association (2) as the woofer (NID 0x17), so the controls come out as "Speaker"/"Bass Speaker", with auto-mute on headphone insertion; - pins every output to a fixed converter via preferred_dacs so the routing does not depend on the generic parser's DAC-allocation heuristics: both speaker pins share DAC 0x03 (the only converter that drives the woofer on this machine) through the mixer, while the headphone (NID 0x21) keeps DAC 0x02. Without a fixed assignment the parser brings up only one speaker pin and leaves the other silent; - wakes the external amp, programs its DSP over the NID-0x20 mailbox at init (boot and resume) and parks it; it is woken again for playback and parked afterwards from a pcm_playback_hook, like the existing Samsung and LG Gram amplifier fixups. The DSP sequence was obtained by capturing the Windows driver's HD-audio traffic via QEMU/VFIO passthrough of the controller and reducing it to the NID-0x20 writes this machine needs; everything else the parser rebuilds. The captured tables are large, so the programming and the fixup live in a separate file under sound/hda/codecs/helpers/ that the codec driver includes. Signed-off-by: Christopher Höner Link: https://patch.msgid.link/20260623191436.6605-1-christopher-hoener@web.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/helpers/razer_blade16_2025.c | 820 ++++++++++++++++++ sound/hda/codecs/realtek/alc269.c | 21 + 2 files changed, 841 insertions(+) create mode 100644 sound/hda/codecs/helpers/razer_blade16_2025.c diff --git a/sound/hda/codecs/helpers/razer_blade16_2025.c b/sound/hda/codecs/helpers/razer_blade16_2025.c new file mode 100644 index 000000000000..1f2e06d8fc13 --- /dev/null +++ b/sound/hda/codecs/helpers/razer_blade16_2025.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Razer Blade 16 (2025) external smart-amplifier programming and fixup, + * to be included from the codec driver. + * + * The internal speakers are driven by an external smart amplifier whose + * crossover, voicing and protection live in a DSP reached over a vendor + * coef mailbox on NID 0x20. These tables hold the programming captured + * from the Windows driver, reduced to the NID-0x20 traffic this machine + * needs (everything else the HDA parser rebuilds). + * + * Each entry is either a plain codec coef write or a mailbox write: + * strobe == 0: coef[addr] = lo + * strobe != 0: amp[addr] = (hi << 16) | lo, with the bank selected on + * port 0x89 and the write committed by the strobe opcode. + */ + +struct alc298_razer_blade16_2025_op { + u16 bank; /* mailbox bank (port 0x89); unused for a plain coef */ + u16 addr; /* mailbox register, or plain coef index */ + u16 hi; /* mailbox high word; unused for a plain coef */ + u16 lo; /* mailbox low word, or plain coef value */ + u16 strobe; /* mailbox commit opcode; 0 marks a plain coef write */ +}; + +/* program the external DSP at init; the amp is woken/parked separately */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_init[] = { + { 0x0000, 0xf102, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xf103, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc000, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xea00, 0x0000, 0x0047, 0xb031 }, + { 0x0000, 0xf20d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xf212, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xc001, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc003, 0x0000, 0x0022, 0xb031 }, + { 0x0000, 0xc004, 0x0000, 0x0044, 0xb031 }, + { 0x0000, 0xc005, 0x0000, 0x0044, 0xb031 }, + { 0x0000, 0xc007, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xc00e, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xf223, 0x0000, 0x007f, 0xb031 }, + { 0x0000, 0xf224, 0x0000, 0x00db, 0xb031 }, + { 0x0000, 0xf225, 0x0000, 0x00ee, 0xb031 }, + { 0x0000, 0xf226, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xf227, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xf21a, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xf242, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc120, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc125, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xc321, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc200, 0x0000, 0x00d8, 0xb031 }, + { 0x0000, 0xc201, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xc202, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xc400, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc401, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xc402, 0x0000, 0x00e0, 0xb031 }, + { 0x0000, 0xc403, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc404, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xc406, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc407, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc408, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xc300, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc125, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdf00, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xdf5f, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xdf60, 0x0000, 0x00a7, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x0084, 0xb031 }, + { 0x0000, 0xc206, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xf10a, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f4, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00e0, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f4, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0065, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f7, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f7, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xe706, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xe707, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xe806, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xe807, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xce04, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xce05, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce06, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xce07, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xcf04, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xcf05, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xcf06, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xcf07, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xc130, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xe000, 0x0000, 0x00a8, 0xb031 }, + { 0x4100, 0x1888, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xea00, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xed00, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xcc2c, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc2d, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xcc2e, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xcc2f, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcd2c, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd2d, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xcd2e, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xcd2f, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcc24, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc25, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xcc26, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xcc27, 0x0000, 0x0085, 0xb031 }, + { 0x0000, 0xcd24, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd25, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xcd26, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xcd27, 0x0000, 0x0085, 0xb031 }, + { 0x0000, 0xcc20, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc21, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc22, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcd20, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd21, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd22, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcc16, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xcc17, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd16, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xcd17, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc29, 0x0000, 0x005d, 0xb031 }, + { 0x0000, 0xcc2a, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xcd29, 0x0000, 0x005d, 0xb031 }, + { 0x0000, 0xcd2a, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xcc31, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xcc32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc33, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc34, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd31, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xcd32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd33, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd34, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc36, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xcc37, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc38, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc39, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd36, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xcd37, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd38, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd39, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc09, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc0a, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xcc0b, 0x0000, 0x007c, 0xb031 }, + { 0x0000, 0xcc0c, 0x0000, 0x005b, 0xb031 }, + { 0x0000, 0xcd09, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd0a, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xcd0b, 0x0000, 0x007c, 0xb031 }, + { 0x0000, 0xcd0c, 0x0000, 0x005b, 0xb031 }, + { 0x0000, 0xcc0e, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc0f, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xcc10, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xcc11, 0x0000, 0x002d, 0xb031 }, + { 0x0000, 0xcd0e, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd0f, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xcd10, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xcd11, 0x0000, 0x002d, 0xb031 }, + { 0x0000, 0xccd6, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccd7, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdd6, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcdd7, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xccd8, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccd9, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdd8, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcdd9, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xccda, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccdb, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdda, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcddb, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xc320, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xc321, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xe604, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xdd00, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xdc19, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdc1a, 0x0000, 0x006a, 0xb031 }, + { 0x0000, 0xdc1b, 0x0000, 0x00aa, 0xb031 }, + { 0x0000, 0xdc1c, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xdc1d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdc1e, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xdc1f, 0x0000, 0x0062, 0xb031 }, + { 0x0000, 0xdc20, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xde19, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xde1a, 0x0000, 0x006a, 0xb031 }, + { 0x0000, 0xde1b, 0x0000, 0x00aa, 0xb031 }, + { 0x0000, 0xde1c, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xde1d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xde1e, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xde1f, 0x0000, 0x0062, 0xb031 }, + { 0x0000, 0xde20, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xdb32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdd32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdb33, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xdd33, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xdb34, 0x0000, 0x001a, 0xb031 }, + { 0x0000, 0xdd34, 0x0000, 0x001a, 0xb031 }, + { 0x0000, 0xdb15, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdd15, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdb17, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xdd17, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xdb94, 0x0000, 0x0070, 0xb031 }, + { 0x0000, 0xdd94, 0x0000, 0x0070, 0xb031 }, + { 0x0000, 0xdb19, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdd19, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x001c, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xdb04, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xdb05, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdd04, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xdd05, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdbbb, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xdbbc, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xdbbd, 0x0000, 0x00f3, 0xb031 }, + { 0x0000, 0xdbbe, 0x0000, 0x00cf, 0xb031 }, + { 0x0000, 0xddbb, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xddbc, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xddbd, 0x0000, 0x00f3, 0xb031 }, + { 0x0000, 0xddbe, 0x0000, 0x00cf, 0xb031 }, + { 0x0000, 0xdb01, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xdd01, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xdb08, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdd08, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdc52, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xde52, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xdf0a, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdf0b, 0x0000, 0x007f, 0xb031 }, + { 0x0000, 0xc851, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc951, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdf01, 0x0000, 0x0073, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xebcb, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb10, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb11, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xeb12, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xeb13, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xeb14, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb15, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xeb16, 0x0000, 0x0069, 0xb031 }, + { 0x0000, 0xeb21, 0x0000, 0x0011, 0xb031 }, + { 0x0000, 0xeb22, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xeb23, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb24, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb25, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xeb26, 0x0000, 0x0055, 0xb031 }, + { 0x0000, 0xeb27, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb28, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb29, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xeb2a, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xeb2b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xeb30, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb31, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xeb32, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xeb33, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb34, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb35, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xeb36, 0x0000, 0x0024, 0xb031 }, + { 0x0000, 0xeb37, 0x0000, 0x0068, 0xb031 }, + { 0x0000, 0xeb40, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb41, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb42, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xeb43, 0x0000, 0x00e8, 0xb031 }, + { 0x0000, 0xeb44, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb45, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xeb46, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xeb47, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb48, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb49, 0x0000, 0x00c5, 0xb031 }, + { 0x0000, 0xeb4a, 0x0000, 0x0032, 0xb031 }, + { 0x0000, 0xeb4b, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb50, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb51, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xeb52, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xeb53, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb54, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb55, 0x0000, 0x007d, 0xb031 }, + { 0x0000, 0xeb56, 0x0000, 0x00d9, 0xb031 }, + { 0x0000, 0xeb57, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xeb60, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xeb61, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xeb62, 0x0000, 0x00b5, 0xb031 }, + { 0x0000, 0xeb63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb64, 0x0000, 0x00fa, 0xb031 }, + { 0x0000, 0xeb65, 0x0000, 0x0086, 0xb031 }, + { 0x0000, 0xeb66, 0x0000, 0x00a8, 0xb031 }, + { 0x0000, 0xeb67, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb68, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xeb69, 0x0000, 0x009a, 0xb031 }, + { 0x0000, 0xeb6a, 0x0000, 0x002f, 0xb031 }, + { 0x0000, 0xeb6b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xebb6, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeb70, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb71, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xeb72, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xeb73, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb74, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xeb75, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xeb76, 0x0000, 0x002c, 0xb031 }, + { 0x0000, 0xeb77, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xeb80, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb81, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb82, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xeb83, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb84, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb85, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xeb86, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xeb87, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb88, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb89, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb8a, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xeb8b, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xebb8, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeb90, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xeb91, 0x0000, 0x008b, 0xb031 }, + { 0x0000, 0xeb92, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xeb93, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xeb94, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xeb95, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xeb96, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xeb97, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xeba0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeba1, 0x0000, 0x0091, 0xb031 }, + { 0x0000, 0xeba2, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xeba3, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeba4, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xeba5, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeba6, 0x0000, 0x0093, 0xb031 }, + { 0x0000, 0xeba7, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xeba8, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xeba9, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xebaa, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xebab, 0x0000, 0x006c, 0xb031 }, + { 0x0000, 0xebc3, 0x0000, 0x00ff, 0xb031 }, + { 0x0000, 0xebc4, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xeb00, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xeb01, 0x0000, 0x00a9, 0xb031 }, + { 0x0000, 0xeb02, 0x0000, 0x00df, 0xb031 }, + { 0x0000, 0xeb03, 0x0000, 0x007b, 0xb031 }, + { 0x0000, 0xeb05, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xeb06, 0x0000, 0x005a, 0xb031 }, + { 0x0000, 0xeb07, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xebc0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xd0cb, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xd0b0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd010, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd011, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd012, 0x0000, 0x0092, 0xb031 }, + { 0x0000, 0xd013, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd014, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd015, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xd016, 0x0000, 0x0072, 0xb031 }, + { 0x0000, 0xd017, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xd020, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd021, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd022, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd023, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd024, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd025, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd026, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xd027, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xd028, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd029, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd02a, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd02b, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c3, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c4, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd0c0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xce6a, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xce63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce64, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xdb04, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xdb05, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xcc00, 0x0000, 0x00c1, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca62, 0x0000, 0x0077, 0xb031 }, + { 0x0000, 0xca65, 0x0000, 0x0082, 0xb031 }, + { 0x0000, 0xca68, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xca75, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xca7b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc860, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc861, 0x0000, 0x00e5, 0xb031 }, + { 0x0000, 0xc862, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xc863, 0x0000, 0x00c6, 0xb031 }, + { 0x0000, 0xc864, 0x0000, 0x00ba, 0xb031 }, + { 0x0000, 0xc865, 0x0000, 0x0061, 0xb031 }, + { 0x0000, 0xc867, 0x0000, 0x0017, 0xb031 }, + { 0x0000, 0xc868, 0x0000, 0x00ed, 0xb031 }, + { 0x0000, 0xc869, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc86a, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xc86b, 0x0000, 0x00e2, 0xb031 }, + { 0x0000, 0xc875, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xc87b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xc836, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc8a0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc833, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc834, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc83f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc83e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc83a, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xc83b, 0x0000, 0x0089, 0xb031 }, + { 0x0000, 0xc830, 0x0000, 0x0013, 0xb031 }, + { 0x0000, 0xc826, 0x0000, 0x00d1, 0xb031 }, + { 0x0000, 0xc890, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc823, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc824, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xc82f, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xc82e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc820, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc816, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc880, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xc813, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc814, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc81f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc81e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc81a, 0x0000, 0x0066, 0xb031 }, + { 0x0000, 0xc81b, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc810, 0x0000, 0x000d, 0xb031 }, + { 0x0000, 0xc846, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc8d0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc844, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc84f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc84e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc84a, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc84b, 0x0000, 0x0056, 0xb031 }, + { 0x0000, 0xc800, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xc851, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc500, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc501, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc502, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc503, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc504, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc505, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc506, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc507, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc510, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc511, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc512, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc513, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc514, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc515, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc516, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc517, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc518, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc541, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd250, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xd251, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd264, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xecb0, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec10, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec11, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xec12, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xec13, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xec14, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec15, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xec16, 0x0000, 0x0069, 0xb031 }, + { 0x0000, 0xec21, 0x0000, 0x0011, 0xb031 }, + { 0x0000, 0xec22, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xec23, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xec24, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec25, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xec26, 0x0000, 0x0055, 0xb031 }, + { 0x0000, 0xec27, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec28, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec29, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xec2a, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xec2b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xecb2, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec30, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec31, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xec32, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xec33, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec34, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec35, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xec36, 0x0000, 0x0024, 0xb031 }, + { 0x0000, 0xec37, 0x0000, 0x0068, 0xb031 }, + { 0x0000, 0xec40, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec41, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec42, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xec43, 0x0000, 0x00e8, 0xb031 }, + { 0x0000, 0xec44, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec45, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xec46, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xec47, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec48, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec49, 0x0000, 0x00c5, 0xb031 }, + { 0x0000, 0xec4a, 0x0000, 0x0032, 0xb031 }, + { 0x0000, 0xec4b, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xecb4, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec50, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec51, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xec52, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xec53, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec54, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec55, 0x0000, 0x007d, 0xb031 }, + { 0x0000, 0xec56, 0x0000, 0x00d9, 0xb031 }, + { 0x0000, 0xec57, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec60, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xec61, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xec62, 0x0000, 0x00b5, 0xb031 }, + { 0x0000, 0xec63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec64, 0x0000, 0x00fa, 0xb031 }, + { 0x0000, 0xec65, 0x0000, 0x0086, 0xb031 }, + { 0x0000, 0xec66, 0x0000, 0x00a8, 0xb031 }, + { 0x0000, 0xec67, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec68, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xec69, 0x0000, 0x009a, 0xb031 }, + { 0x0000, 0xec6a, 0x0000, 0x002f, 0xb031 }, + { 0x0000, 0xec6b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xecb6, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xec70, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec71, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xec72, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xec73, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec74, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xec75, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xec76, 0x0000, 0x002c, 0xb031 }, + { 0x0000, 0xec77, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xec80, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec81, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec82, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xec83, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec84, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec85, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xec86, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xec87, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec88, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec89, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec8a, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xec8b, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xecb8, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xec90, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xec91, 0x0000, 0x008b, 0xb031 }, + { 0x0000, 0xec92, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xec93, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xec94, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xec95, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xec96, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xec97, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xeca0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeca1, 0x0000, 0x0091, 0xb031 }, + { 0x0000, 0xeca2, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xeca3, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeca4, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xeca5, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeca6, 0x0000, 0x0093, 0xb031 }, + { 0x0000, 0xeca7, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xeca8, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xeca9, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xecaa, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xecab, 0x0000, 0x006c, 0xb031 }, + { 0x0000, 0xebc3, 0x0000, 0x00ff, 0xb031 }, + { 0x0000, 0xebc4, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xec00, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xec01, 0x0000, 0x00a9, 0xb031 }, + { 0x0000, 0xec02, 0x0000, 0x00df, 0xb031 }, + { 0x0000, 0xec03, 0x0000, 0x007b, 0xb031 }, + { 0x0000, 0xec05, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xec06, 0x0000, 0x005a, 0xb031 }, + { 0x0000, 0xec07, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xebc0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xd1b0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd110, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd111, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd112, 0x0000, 0x0092, 0xb031 }, + { 0x0000, 0xd113, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd114, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd115, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xd116, 0x0000, 0x0072, 0xb031 }, + { 0x0000, 0xd117, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xd120, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd121, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd122, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd123, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd124, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd125, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd126, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xd127, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xd128, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd129, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd12a, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd12b, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c3, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c4, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd0c0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xce63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce64, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xdd04, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xdd05, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca82, 0x0000, 0x0077, 0xb031 }, + { 0x0000, 0xca85, 0x0000, 0x0082, 0xb031 }, + { 0x0000, 0xca88, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xca95, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xca9b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc960, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc961, 0x0000, 0x00e5, 0xb031 }, + { 0x0000, 0xc962, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xc963, 0x0000, 0x00c6, 0xb031 }, + { 0x0000, 0xc964, 0x0000, 0x00ba, 0xb031 }, + { 0x0000, 0xc965, 0x0000, 0x0061, 0xb031 }, + { 0x0000, 0xc967, 0x0000, 0x0017, 0xb031 }, + { 0x0000, 0xc968, 0x0000, 0x00ed, 0xb031 }, + { 0x0000, 0xc969, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc96a, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xc96b, 0x0000, 0x00e2, 0xb031 }, + { 0x0000, 0xc975, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xc97b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xc936, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc9a0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc933, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc934, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc93f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc93e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc93a, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xc93b, 0x0000, 0x0089, 0xb031 }, + { 0x0000, 0xc930, 0x0000, 0x0013, 0xb031 }, + { 0x0000, 0xc926, 0x0000, 0x00d1, 0xb031 }, + { 0x0000, 0xc990, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc923, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc924, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xc92f, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xc92e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc920, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc916, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc980, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xc913, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc914, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc91f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc91e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc91a, 0x0000, 0x0066, 0xb031 }, + { 0x0000, 0xc91b, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc910, 0x0000, 0x000d, 0xb031 }, + { 0x0000, 0xc946, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc9d0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc944, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc94f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc94e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc94a, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc94b, 0x0000, 0x0056, 0xb031 }, + { 0x0000, 0xc951, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc600, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc601, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc602, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc603, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc604, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc605, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc606, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc607, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc610, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc611, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc612, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc613, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc614, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc615, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc616, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc617, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc618, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc541, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xc802, 0x0000, 0x00d2, 0xb031 }, + { 0x0000, 0xc902, 0x0000, 0x00d2, 0xb031 }, + { 0x0000, 0xc800, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd250, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xd252, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd264, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdf00, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xdbb5, 0x0041, 0xd27f, 0xb037 }, + { 0x0000, 0xddb5, 0x0040, 0x6e3b, 0xb037 }, + { 0x0000, 0xdb93, 0x0000, 0x009e, 0xb033 }, + { 0x0000, 0xdd93, 0x0000, 0x009e, 0xb033 }, + { 0x0000, 0xdb12, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xdd12, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xdb08, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdd08, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0x0010, 0x0000, 0x0f21, 0x0000 }, +}; + +/* wake the amp (ea00=0x43, c121=0x0b, f109=0xe0) */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_wake[] = { + { 0x0000, 0xea00, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00e0, 0xb031 }, +}; + +/* park the amp (ea00=0x47, c121=0x0a, f109=0xa0) */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_sleep[] = { + { 0x0000, 0xea00, 0x0000, 0x0047, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00a0, 0xb031 }, +}; + +/* + * Razer Blade 16 (2025): Realtek ALC298 driving an external smart amp. + * Crossover, voicing and speaker protection live in an external DSP + * reached over a vendor-coef mailbox on NID 0x20; the captured DSP + * programming is in the tables above. NID 0x14, the BIOS-disabled + * tweeter pin, is re-exposed via a pin-config override so the parser + * drives it as a normal internal speaker (shared volume/mute, auto-mute + * on headphone insertion). + * + * alc298_razer_blade16_2025_apply() runs one op-table: a plain coef write + * (strobe == 0) goes through alc_write_coef_idx(), while a mailbox op + * selects a bank and streams four PROC_COEF words (addr/hi/lo/commit) + * into a single coef index (0x23) on NID 0x20 to reach the external DSP + * -- that one-index/many-words form is why it cannot be expressed as an + * alc_process_coef_fw() table (one value per index). + */ +static void alc298_razer_blade16_2025_apply(struct hda_codec *codec, + const struct alc298_razer_blade16_2025_op *seq, + int num) +{ + int i; + + for (i = 0; i < num; i++) { + if (!seq[i].strobe) { + alc_write_coef_idx(codec, seq[i].addr, seq[i].lo); + continue; + } + alc_write_coef_idx(codec, 0x89, seq[i].bank); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x23); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].addr); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].hi); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].lo); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].strobe); + } +} + +static void alc298_razer_blade16_2025_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + /* power the external amp only while a stream is running */ + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_wake, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_wake)); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_sleep, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_sleep)); + break; + } +} + +static void alc298_fixup_razer_blade16_2025(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* + * Pin every output to a fixed converter so the routing does not + * rely on the generic parser's DAC-allocation heuristics: without + * a fixed assignment the parser brings up only one speaker pin and + * leaves the other silent, and the allocation could change between + * kernel versions. Both speaker pins share DAC 0x03 through mixer + * 0x0d: that is the only converter that drives the woofer (NID + * 0x17) on this machine -- feeding it from DAC 0x02 or the digital + * converter 0x06 leaves the woofer silent. Both pins carry the + * same stereo stream and the external amp does the crossover. + * Headphone NID 0x21 keeps DAC 0x02. + */ + static const hda_nid_t preferred_pairs[] = { + 0x21, 0x02, + 0x14, 0x03, + 0x17, 0x03, + 0 + }; + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.preferred_dacs = preferred_pairs; + break; + case HDA_FIXUP_ACT_PROBE: + spec->gen.pcm_playback_hook = alc298_razer_blade16_2025_pcm_hook; + break; + case HDA_FIXUP_ACT_INIT: + /* + * Wake the amp, program its DSP at boot and on resume, then + * park it. It is woken again for playback and parked + * afterwards from the pcm_playback_hook. + */ + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_wake, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_wake)); + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_init, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_init)); + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_sleep, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_sleep)); + break; + } +} diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d9e2384fc0ba..21ab891fcb47 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3357,6 +3357,9 @@ static void alc287_fixup_acer_micmute_led(struct hda_codec *codec, /* for alc285_fixup_ideapad_s740_coef() */ #include "../helpers/ideapad_s740.c" +/* for alc298_fixup_razer_blade16_2025() */ +#include "../helpers/razer_blade16_2025.c" + static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), @@ -4041,6 +4044,8 @@ enum { ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, ALC298_FIXUP_LG_GRAM_STYLE_14, + ALC298_FIXUP_RAZER_BLADE16_2025_PINS, + ALC298_FIXUP_RAZER_BLADE16_2025, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -4206,6 +4211,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, } static const struct hda_fixup alc269_fixups[] = { + [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170121 }, /* tweeter as internal speaker, seq 1 */ + { 0x17, 0x90170120 }, /* woofer as internal speaker, seq 0 */ + { } + }, + .chained = true, + .chain_id = ALC298_FIXUP_RAZER_BLADE16_2025, + }, + [ALC298_FIXUP_RAZER_BLADE16_2025] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_razer_blade16_2025, + }, [ALC269_FIXUP_GPIO2] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_gpio2, @@ -7862,6 +7881,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1a58, 0x300e, "Razer Blade 16 (2025)", + ALC298_FIXUP_RAZER_BLADE16_2025_PINS), SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), From 39edd6d5f6e751e3675bd4e436f168cdd4d03983 Mon Sep 17 00:00:00 2001 From: Federico Beffa Date: Thu, 25 Jun 2026 13:46:28 +0200 Subject: [PATCH 006/791] ALSA: usb-audio: Add support for Pioneer DJ DJM-S11 The Pioneer DJ DJM-S11, a professional 2-port DJ mixer, is currently not recognized as an audio interface and therefore not usable on Linux. This patch enables its full audio functionality. Like other mixers in the Pioneer DJ DJM family, the DJM-S11 uses vendor-specific USB class descriptors (0xff) and requires custom mixer quirks to expose the hardware's input capture routing and volume controls. Add the USB vendor and product ID (2b73:0037) to quirks-table.h, mapping the control interface (Interface 0) to trigger the standard mixer quirks, and establishing the audio formats for the playback and capture streams. Control interface 3 is likely related to the touchscreen which is capable to display waveforms, etc. This functionality is not supported. In mixer_quirks.c, add the DSP routing options for the DJM-S11. This creates the ALSA kcontrols for selecting the capture sources (Phono, Line, Post-fader, etc.) and initializes the default DSP state during the device probe. Note that the DJM-S11's capture endpoint clock is strictly slaved to its playback endpoint. Capturing audio (e.g., timecode vinyl DVS input) physically requires an active playback stream on the output channels to feed the master word clock back to the capture stage. Userspace audio servers (such as PipeWire or JACK) should be configured in full-duplex mode or have automatic node suspension disabled to maintain the clock line and prevent I/O errors during capture. Signed-off-by: Federico Beffa Link: https://patch.msgid.link/20260625115340.823184-1-beffa@fbengineering.ch Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 20 ++++++++++++ sound/usb/quirks-table.h | 67 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 10792d26fa94..fb3bd360e5a9 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3920,6 +3920,7 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_450_IDX 0x5 #define SND_DJM_A9_IDX 0x6 #define SND_DJM_V10_IDX 0x7 +#define SND_DJM_S11_IDX 0x8 #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ .name = _name, \ @@ -4260,6 +4261,21 @@ static const struct snd_djm_ctl snd_djm_ctls_v10[] = { // playback channels are fixed and controlled by hardware knobs on the mixer }; +// DJM-S11 +static const u16 snd_djm_opts_s11_cap1[] = { + 0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d }; +static const u16 snd_djm_opts_s11_cap2[] = { + 0x0200, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d }; +static const u16 snd_djm_opts_s11_cap3[] = { + 0x0307, 0x0308, 0x0309, 0x030a, 0x030d, 0x0311, 0x0312 }; + +static const struct snd_djm_ctl snd_djm_ctls_s11[] = { + SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL), + SND_DJM_CTL("Input 1 Capture Switch", s11_cap1, 1, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Input 2 Capture Switch", s11_cap2, 1, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Input 3 Capture Switch", s11_cap3, 3, SND_DJM_WINDEX_CAP) +}; + static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2), [SND_DJM_750_IDX] = SND_DJM_DEVICE(750), @@ -4269,6 +4285,7 @@ static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_450_IDX] = SND_DJM_DEVICE(450), [SND_DJM_A9_IDX] = SND_DJM_DEVICE(a9), [SND_DJM_V10_IDX] = SND_DJM_DEVICE(v10), + [SND_DJM_S11_IDX] = SND_DJM_DEVICE(s11), }; static int snd_djm_controls_info(struct snd_kcontrol *kctl, @@ -4573,6 +4590,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2b73, 0x0034): /* Pioneer DJ DJM-V10 */ err = snd_djm_controls_create(mixer, SND_DJM_V10_IDX); break; + case USB_ID(0x2b73, 0x0037): /* Pioneer DJ DJM-S11 */ + err = snd_djm_controls_create(mixer, SND_DJM_S11_IDX); + break; case USB_ID(0x03f0, 0x0269): /* HP TB Dock G2 */ err = hp_dock_mixer_create(mixer); break; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 71444c2898b4..938908671d93 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3310,6 +3310,73 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * Pioneer DJ / AlphaTheta DJM-S11 + */ + USB_DEVICE(0x2b73, 0x0037), + QUIRK_DRIVER_INFO { + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_STANDARD_MIXER(0) + }, + { + QUIRK_DATA_AUDIOFORMAT(1) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 14, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 48000 }, + .clock = 1, + .fmt_type = UAC_FORMAT_TYPE_I + } + }, + { + QUIRK_DATA_AUDIOFORMAT(2) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 10, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC | + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 48000 }, + .clock = 1, + .fmt_type = UAC_FORMAT_TYPE_I + } + }, + { + /* Audio Control (unknown purpose) */ + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + /* HID */ + .ifnum = 5, + .type = QUIRK_IGNORE_INTERFACE + }, + QUIRK_COMPOSITE_END + } + } +}, /* * MacroSilicon MS2100/MS2106 based AV capture cards From c0933934d5d96fc641e8d0025f9099e554a7b47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Fri, 26 Jun 2026 11:47:22 -0400 Subject: [PATCH 007/791] ALSA: hda: Force resume if acomp notified during system suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if an HDMI cable is connected while the system is suspended, the HDMI audio jack status stays off after resume. This is due to the jack state not being synced by the HDA HDMI codec device's runtime resume, as that never happens if the device was runtime suspended before the system suspended, or by the acomp notification triggered from the DRM side if that happens before the HDA HDMI codec device has resumed. To fix this, if snd_hda_hdmi_acomp_pin_eld_notify() gets called before the HDA HDMI codec device has resumed, mark it to be forcefully runtime resumed at the next PM complete time. Do this using a separate acomp_requested_resume flag that can be temporarily set without overwriting forced_resume for drivers that always want to force resume. Assisted-by: Copilot:claude-sonnet-4.6 Signed-off-by: Nícolas F. R. A. Prado Link: https://patch.msgid.link/20260626-hda-force-resume-eld-notify-v1-1-a92cb01393e0@collabora.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 1 + sound/hda/codecs/hdmi/hdmi.c | 4 +++- sound/hda/common/codec.c | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 17945ab5e6e2..c05b6d44c491 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -256,6 +256,7 @@ struct hda_codec { unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ + unsigned int acomp_requested_resume:1; /* resume requested by acomp */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ unsigned int eld_jack_detect:1; /* Machine jack-detection by ELD */ diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 1f4d646724ed..0b6816018a42 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2233,8 +2233,10 @@ void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) { + codec->acomp_requested_resume = 1; return; + } snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); } diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index ef533770179b..b9ded149ea11 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -2967,8 +2967,11 @@ static void hda_codec_pm_complete(struct device *dev) dev->power.power_state = PMSG_RESUME; if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || - hda_codec_need_resume(codec) || codec->forced_resume)) + hda_codec_need_resume(codec) || codec->forced_resume || + codec->acomp_requested_resume)) { + codec->acomp_requested_resume = 0; pm_request_resume(dev); + } } static int hda_codec_pm_suspend(struct device *dev) From 11e828cd6b0f283ebe9dc6b4cffd38e3321e7725 Mon Sep 17 00:00:00 2001 From: Mert Seftali Date: Sun, 14 Jun 2026 14:40:19 +0200 Subject: [PATCH 008/791] ASoC: SOF: ipc4-topology: Return error for invalid number of formats When the number of input or output formats is zero, sof_ipc4_widget_setup_comp_src() and sof_ipc4_widget_setup_comp_asrc() print an error and jump to the cleanup label. At that point 'ret' is still 0, because the earlier sof_ipc4_get_audio_fmt() call succeeded, so the function returns success and the caller never finds out that the widget setup actually failed. Set ret to -EINVAL before the goto so the error gets reported. Fixes: 21a5adffad46 ("ASoC: SOF: ipc4-topology: Validate the number of in/out formats for src/asrc") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202606111431.Uky3T0tF-lkp@intel.com/ Signed-off-by: Mert Seftali Link: https://patch.msgid.link/20260614124019.19259-1-mertsftl@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 95ad5266b0c6..8ac7dde32f77 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1127,6 +1127,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", src->available_fmt.num_input_formats, src->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } @@ -1179,6 +1180,7 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", asrc->available_fmt.num_input_formats, asrc->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } From 6ed013a581d00f84bbd0c3c60f7a108bef93fc4e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 12 Jun 2026 09:01:12 +0700 Subject: [PATCH 009/791] ASoC: meson: Use dev_err_probe() for device reset failures device_reset() may return -EPROBE_DEFER. Switch to dev_err_probe() so probe failures are reported consistently and deferred probing is handled properly. This matches the existing pattern used in aiu_probe(). No functional change intended. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260612020113.9557-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/meson/g12a-toacodec.c | 2 +- sound/soc/meson/g12a-tohdmitx.c | 2 +- sound/soc/meson/t9015.c | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c index a95375b53f0a..21941ee552c5 100644 --- a/sound/soc/meson/g12a-toacodec.c +++ b/sound/soc/meson/g12a-toacodec.c @@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index d541ca4acfaf..967109ca2b57 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index da1a93946d67..f0b55aee5241 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -265,10 +265,8 @@ static int t9015_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = device_reset(dev); - if (ret) { - dev_err(dev, "reset failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { From ce3733792d38ade8df8ce288a96ff0c6ad858bf3 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:44:56 +0800 Subject: [PATCH 010/791] ASoC: sma1303: remove fault-check sysfs group on remove sma1303_i2c_probe() creates a sysfs group that exposes the fault-check controls. The check_fault_status store callback can queue check_fault_work. sma1303_i2c_remove() only cancels the delayed work. It does not remove the sysfs group, so the controls can remain published after remove while their callbacks still use the driver data and can queue the work again. Remove the sysfs group before cancelling the delayed work. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260615064456.28615-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/sma1303.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index c7aaf98ef71e..38677badfc18 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1778,6 +1778,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) struct sma1303_priv *sma1303 = (struct sma1303_priv *) i2c_get_clientdata(client); + if (sma1303->attr_grp) + sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); cancel_delayed_work_sync(&sma1303->check_fault_work); } From ae375f8d063d71e598c0d7e14fc056154c84d7a7 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:45:48 +0800 Subject: [PATCH 011/791] ASoC: tlv320aic26: remove keyclick sysfs file aic26_probe() creates the keyclick sysfs file on the component device. The file callback uses the ASoC component pointer stored in the driver private data. There is no matching remove callback, so the sysfs file can remain after the component is removed while its backing component state is gone. Add a component remove callback that removes the keyclick file. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260615064549.34110-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic26.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index e5dfb3d752a3..84e954311c1b 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) return 0; } +static void aic26_remove(struct snd_soc_component *component) +{ + device_remove_file(component->dev, &dev_attr_keyclick); +} + static const struct snd_soc_component_driver aic26_soc_component_dev = { .probe = aic26_probe, + .remove = aic26_remove, .controls = aic26_snd_controls, .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, From e9f08e779976bbfef7d168c70350083878db7e2e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Jun 2026 21:44:39 +0800 Subject: [PATCH 012/791] ASoC: SOF: add Intel UAOL sof_ipc_dai_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type will be used for Intel USB Audio Offload Link (UAOL) DAI. Signed-off-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260615134439.1044872-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 36809f712723..0b6a6ba6489a 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -90,6 +90,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ }; /* general purpose DAI configuration */ From 012dfa0c45ecaadd48549938a9f35d1a329a2c5d Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Mon, 15 Jun 2026 20:57:50 +0200 Subject: [PATCH 013/791] ASoC: dt-bindings: mtk-btcvsd-snd: Convert to DT Schema Convert the mtk-btcvsd-snd.txt DT binding to DT Schema format. Signed-off-by: Luca Leonardo Scorcia Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260615185810.11804-1-l.scorcia@gmail.com Signed-off-by: Mark Brown --- .../sound/mediatek,mtk-btcvsd-snd.yaml | 59 +++++++++++++++++++ .../bindings/sound/mtk-btcvsd-snd.txt | 24 -------- 2 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml delete mode 100644 Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt diff --git a/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml new file mode 100644 index 000000000000..1b7451655476 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mtk-btcvsd-snd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek ALSA BT SCO CVSD/MSBC + +maintainers: + - Luca Leonardo Scorcia + +properties: + compatible: + const: mediatek,mtk-btcvsd-snd + + reg: + items: + - description: PKV region + - description: SRAM_BANK2 region + + interrupts: + items: + - description: BT-SCO interrupt + + mediatek,infracfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the infracfg controller + + mediatek,offset: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of register offsets and masks + items: + - description: infra_misc_offset + - description: infra_conn_bt_cvsd_mask + - description: cvsd_mcu_read_offset + - description: cvsd_mcu_write_offset + - description: cvsd_packet_indicator_offset + +required: + - compatible + - reg + - interrupts + - mediatek,infracfg + - mediatek,offset + +additionalProperties: false + +examples: + - | + #include + + mtk-btcvsd-snd@18000000 { + compatible = "mediatek,mtk-btcvsd-snd"; + reg = <0x18000000 0x1000>, + <0x18080000 0x8000>; + interrupts = ; + mediatek,infracfg = <&infrasys>; + mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; + }; diff --git a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt deleted file mode 100644 index 679e44839b48..000000000000 --- a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt +++ /dev/null @@ -1,24 +0,0 @@ -Mediatek ALSA BT SCO CVSD/MSBC Driver - -Required properties: -- compatible = "mediatek,mtk-btcvsd-snd"; -- reg: register location and size of PKV and SRAM_BANK2 -- interrupts: should contain BTSCO interrupt -- mediatek,infracfg: the phandles of INFRASYS -- mediatek,offset: Array contains of register offset and mask - infra_misc_offset, - infra_conn_bt_cvsd_mask, - cvsd_mcu_read_offset, - cvsd_mcu_write_offset, - cvsd_packet_indicator_offset - -Example: - - mtk-btcvsd-snd@18000000 { - compatible = "mediatek,mtk-btcvsd-snd"; - reg=<0 0x18000000 0 0x1000>, - <0 0x18080000 0 0x8000>; - interrupts = ; - mediatek,infracfg = <&infrasys>; - mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; - }; From a33e6fa400d174e7970d4efc30dd2e10911281ab Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 17 Jun 2026 09:33:45 +0200 Subject: [PATCH 014/791] ASoC: SOF: topology: Use more common error handling code in sof_link_load() Use an additional label so that a bit of exception handling can be better reused at the end of this function implementation. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://patch.msgid.link/40b93192-68d2-4de1-845b-9c9ba994a75b@web.de Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 42a2d90bb705..f709935593ef 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1943,8 +1943,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ private->array, le32_to_cpu(private->size)); if (ret < 0) { dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); - kfree(slink); - return ret; + goto free_slink; } token_list = tplg_ops ? tplg_ops->token_list : NULL; @@ -2013,8 +2012,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ /* allocate memory for tuples array */ slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); if (!slink->tuples) { - kfree(slink); - return -ENOMEM; + ret = -ENOMEM; + goto free_slink; } if (token_list[SOF_DAI_LINK_TOKENS].tokens) { @@ -2070,6 +2069,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ err: kfree(slink->tuples); +free_slink: kfree(slink); return ret; From 612ccf42acd14bb2685fa60c3495ca13e63e8989 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 20:23:25 +0800 Subject: [PATCH 015/791] ASoC: rt700-sdw: always drain jack work on remove rt700_sdw_remove() drains jack_detect_work and jack_btn_check_work only when rt700->hw_init is true. That state bit is cleared by rt700_update_status() when the SoundWire slave becomes UNATTACHED, but a jack work item can already have been queued by rt700_interrupt_callback() or rt700_jack_init() while the device was initialized. Do not use hw_init as the remove-time guard for draining these work objects. The delayed works are initialized during rt700_init(), so remove can cancel them unconditionally and pair the object lifetime with the codec-private data lifetime instead of a mutable hardware state bit. This issue was found by our static analysis tool and then confirmed by manual review of the SoundWire status, interrupt and remove paths. The remove path should drain work based on whether the work object exists, not on a runtime hardware state bit that can change after the work was queued. A QEMU PoC queued jack_detect_work, simulated SDW_SLAVE_UNATTACHED, and then entered remove. DEBUG_OBJECTS reported an active timer/work object associated with the rt700 jack work path after remove skipped the cancel. This is sent as an RFC because the practical trigger depends on SoundWire core remove ordering after an UNATTACHED status update. If remove cannot run after hw_init has been cleared while jack work is still pending, this is a defensive lifecycle cleanup rather than a reachable race on current systems. Fixes: 737ee8bdf682 ("ASoC: rt700-sdw: use cancel_work_sync() in .remove as well as .suspend") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619122325.2504287-1-runyu.xiao@seu.edu.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 6bc636c86f42..83f7d52cb598 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -459,10 +459,8 @@ static void rt700_sdw_remove(struct sdw_slave *slave) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - if (rt700->hw_init) { - cancel_delayed_work_sync(&rt700->jack_detect_work); - cancel_delayed_work_sync(&rt700->jack_btn_check_work); - } + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); pm_runtime_disable(&slave->dev); } From 3a89ddcf0c3d9a068631e8c24d5c9e81d1e6512a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 17:48:22 +0800 Subject: [PATCH 016/791] ASoC: fsl: mpc5200-i2s: Free DMA resources on probe failure mpc5200_audio_dma_create() creates the DMA resources before registering the component. If snd_soc_register_component() fails, the function returns directly and leaves the DMA resources allocated. Call mpc5200_audio_dma_destroy() before returning from this error path. Fixes: f515b67381de ("ASoC: fsl: mpc5200 combine psc_dma platform data") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622094822.926166-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_i2s.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9ad44eeed6ad..7831136f4f12 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -170,6 +170,7 @@ static int psc_i2s_of_probe(struct platform_device *op) psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); + mpc5200_audio_dma_destroy(op); return rc; } From 016f29997ebd29d6ab59c8162ce0e7f73bd1e517 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 17:16:20 +0800 Subject: [PATCH 017/791] AsoC: intel: sst: fix PCI device reference leak on probe failure intel_sst_probe() takes a reference to the PCI device with pci_dev_get(). If sst_platform_get_resources() fails afterwards, the probe error path cleans up the driver context but does not drop the PCI device reference. Add a pci_dev_put() error path for failures after pci_dev_get(). Fixes: f533a035e4da ("ASoC: Intel: mrfld - create separate module for pci part") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622091620.897478-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 22ae2d22f121..44bb11c69490 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -130,13 +130,15 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->pci = pci_dev_get(pci); ret = sst_platform_get_resources(sst_drv_ctx); if (ret < 0) - goto do_free_drv_ctx; + goto do_put_pci; pci_set_drvdata(pci, sst_drv_ctx); sst_configure_runtime_pm(sst_drv_ctx); return ret; +do_put_pci: + pci_dev_put(sst_drv_ctx->pci); do_free_drv_ctx: sst_context_cleanup(sst_drv_ctx); dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); From 6ad4892c4f5cb437a928a02f5b7d37d496aa9268 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 22:56:45 +0800 Subject: [PATCH 018/791] ASoC: hdac_hda: Fix hlink refcount leak on component registration failure hdac_hda_dev_probe() gets the HDA link with snd_hdac_ext_bus_link_get() before registering the ASoC component. If component registration fails, the function returns without dropping the link reference. Always call snd_hdac_ext_bus_link_put() after the registration attempt so the reference taken during probe is balanced on both success and failure. Fixes: 6bae5ea94989 ("ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622145645.1184986-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 680e341aa7f1..1ab5f8a26e03 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -642,10 +642,8 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev) &hdac_hda_codec, hdac_hda_dais, ARRAY_SIZE(hdac_hda_dais)); - if (ret < 0) { + if (ret < 0) dev_err(&hdev->dev, "%s: failed to register HDA codec %d\n", __func__, ret); - return ret; - } snd_hdac_ext_bus_link_put(hdev->bus, hlink); From 2e5bf7f48bf170f8e615cea408289440c3328926 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 01:07:54 +0000 Subject: [PATCH 019/791] ASoC: codecs: max98090: add missing describe "data" for max98090_set_jack() "data" is missing. Add it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87zf0ids7p.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index da416329b038..bccce322ccc3 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) * * @component: MAX98090 component * @jack: jack to report detection events on + * @data: can be used if codec driver need extra data for configuring jack * * Enable microphone detection via IRQ on the MAX98090. If GPIOs are * being used to bring out signals to the processor then only platform From 785903b7770f19ea05b8a76081dff691c56fa771 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 23:11:27 +0800 Subject: [PATCH 020/791] ASoC: hdac_hdmi: Fix resource cleanup on probe failure hdac_hdmi_dev_probe() gets the HDA link before allocating and initializing the HDMI codec private data. Several later error paths return directly without dropping the link reference, leaving the hlink refcount unbalanced. Release the link reference on probe failures. Also turn display power off if the failure happens after it has been enabled. This issue was dicussed in: https://lore.kernel.org/all/s5h1s0esk8o.wl-tiwai@suse.de/ I think the paths fixed here are probe failure paths: the device has not been fully initialized or bound, and runtime PM suspend cannot be relied on to balance the reference taken during probe. Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622151127.1198196-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 3220f9226e0b..38073a70fa61 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1866,8 +1866,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) snd_hdac_ext_bus_link_get(hdev->bus, hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); - if (hdmi_priv == NULL) + if (hdmi_priv == NULL) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENOMEM; + } snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; @@ -1876,8 +1878,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; hdmi_priv->hdev = hdev; - if (!hdac_id) + if (!hdac_id) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENODEV; + } if (hdac_id->driver_data) hdmi_priv->drv_data = @@ -1902,6 +1906,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) if (ret < 0) { dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + snd_hdac_display_power(hdev->bus, hdev->addr, false); return ret; } snd_hdac_refresh_widgets(hdev); From 3359ba93d01a23b2e4249e9e44ccfe48eb9c5d71 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 18 Jun 2026 10:38:18 +0800 Subject: [PATCH 021/791] ASoC: fsl_audmix: rework runtime PM handling in probe After pm_runtime_enable() the AUDMIX block is powered off and stays suspended until the first runtime resume. Register writes issued between probe() and the first resume (e.g. from DAPM or ALSA control paths) target unpowered hardware and cause a system hang. Fix this by calling pm_runtime_resume_and_get() immediately after pm_runtime_enable() to power the hardware up and enable its clocks. Release the reference afterwards with pm_runtime_put() to allow the runtime PM framework to suspend the device and switch the regmap to cache-only mode when idle. When CONFIG_PM is disabled or runtime PM is not enabled, pm_runtime_* calls are stubs that do not power up the hardware. Handle this case explicitly by calling fsl_audmix_runtime_resume() directly so the hardware is always initialised and its clocks are enabled, ensuring register accesses succeed regardless of PM configuration. Fixes: be1df61cf06ef ("ASoC: fsl: Add Audio Mixer CPU DAI driver") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260618023818.31618-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f819f33ec46b..2885cc10b02d 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -457,6 +457,9 @@ static const struct of_device_id fsl_audmix_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_audmix_ids); +static int fsl_audmix_runtime_resume(struct device *dev); +static int fsl_audmix_runtime_suspend(struct device *dev); + static int fsl_audmix_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -488,13 +491,25 @@ static int fsl_audmix_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = fsl_audmix_runtime_resume(dev); + if (ret) + goto err_disable_pm; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err_pm_get_sync; + + /* To enable regmap cache only when runtime PM enabled */ + pm_runtime_put(dev); ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, fsl_audmix_dai, ARRAY_SIZE(fsl_audmix_dai)); if (ret) { dev_err(dev, "failed to register ASoC DAI\n"); - goto err_disable_pm; + goto err_pm_get_sync; } /* @@ -506,12 +521,15 @@ static int fsl_audmix_probe(struct platform_device *pdev) if (IS_ERR(priv->pdev)) { ret = PTR_ERR(priv->pdev); dev_err(dev, "failed to register platform: %d\n", ret); - goto err_disable_pm; + goto err_pm_get_sync; } } return 0; +err_pm_get_sync: + if (!pm_runtime_status_suspended(dev)) + fsl_audmix_runtime_suspend(dev); err_disable_pm: pm_runtime_disable(dev); return ret; @@ -522,6 +540,8 @@ static void fsl_audmix_remove(struct platform_device *pdev) struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + fsl_audmix_runtime_suspend(&pdev->dev); if (priv->pdev) platform_device_unregister(priv->pdev); From d7e261b7ad1bc96a2ee249c6694691244c099acf Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:14 +0700 Subject: [PATCH 022/791] ASoC: fsl_asrc: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260615093824.115751-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 5fda9b647c70..0b28bcfa47fe 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc = pair->asrc; struct device *dev = &asrc->pdev->dev; - unsigned long lock_flags; int i, ret = 0; - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { if (asrc->pair[i] != NULL) @@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) pair->index = index; } - spin_unlock_irqrestore(&asrc->lock, lock_flags); - return ret; } @@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) { struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; - unsigned long lock_flags; /* Make sure the pair is disabled */ regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEi_MASK(index), 0); - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); asrc->channel_avail += pair->channels; asrc->pair[index] = NULL; pair->error = 0; - - spin_unlock_irqrestore(&asrc->lock, lock_flags); } /** From 58712476dee21cc3d3cb3b0b85a8ca3a86d480fe Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:15 +0700 Subject: [PATCH 023/791] ASoC: fsl_audmix: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f819f33ec46b..a27370862ee8 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); - unsigned long lock_flags; /* Capture stream shall not be handled */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms |= BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms |= BIT(dai->driver->id); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms &= ~BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms &= ~BIT(dai->driver->id); break; default: return -EINVAL; From 48d84310be60578d4413139661b433b1d33aca1d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:16 +0700 Subject: [PATCH 024/791] ASoC: fsl_easrc: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 114a6c0b6b73..edfd943197a0 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; struct device *dev; - unsigned long lock_flags; int ret; if (!easrc) @@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) if (ret) return ret; - spin_lock_irqsave(&easrc->lock, lock_flags); - ret = fsl_easrc_config_slot(easrc, ctx->index); - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + ret = fsl_easrc_config_slot(easrc, ctx->index); if (ret) return ret; @@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *easrc = ctx->asrc; struct device *dev; - unsigned long lock_flags; int ret = 0; int i; dev = &easrc->pdev->dev; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { if (easrc->pair[i]) @@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) easrc->channel_avail -= channels; } - spin_unlock_irqrestore(&easrc->lock, lock_flags); - return ret; } @@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) */ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) { - unsigned long lock_flags; struct fsl_asrc *easrc; if (!ctx) @@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) easrc = ctx->asrc; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); fsl_easrc_release_slot(easrc, ctx->index); easrc->channel_avail += ctx->channels; easrc->pair[ctx->index] = NULL; - - spin_unlock_irqrestore(&easrc->lock, lock_flags); } /* @@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; - unsigned long lock_flags; regcache_cache_only(easrc->regmap, true); clk_disable_unprepare(easrc->mem_clk); - spin_lock_irqsave(&easrc->lock, lock_flags); - easrc_priv->firmware_loaded = 0; - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + easrc_priv->firmware_loaded = 0; return 0; } @@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) struct fsl_easrc_priv *easrc_priv = easrc->private; struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; - unsigned long lock_flags; int ret; int i; @@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) regcache_mark_dirty(easrc->regmap); regcache_sync(easrc->regmap); - spin_lock_irqsave(&easrc->lock, lock_flags); - if (easrc_priv->firmware_loaded) { - spin_unlock_irqrestore(&easrc->lock, lock_flags); - goto skip_load; + scoped_guard(spinlock_irqsave, &easrc->lock) { + if (easrc_priv->firmware_loaded) + return 0; + easrc_priv->firmware_loaded = 1; } - easrc_priv->firmware_loaded = 1; - spin_unlock_irqrestore(&easrc->lock, lock_flags); ret = fsl_easrc_get_firmware(easrc); if (ret) { @@ -2377,9 +2364,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) goto disable_mem_clk; } -skip_load: - return 0; - disable_mem_clk: clk_disable_unprepare(easrc->mem_clk); return ret; From cb87bdabd341e399a6fb18e5d9a7c0c669191719 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:17 +0700 Subject: [PATCH 025/791] ASoC: fsl_esai: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index cde0b0c6c1ef..4a530a6c33f0 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) { struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); bool tx = true, rx = false, enabled[2]; - unsigned long lock_flags; u32 tfcr, rfcr; - spin_lock_irqsave(&esai_priv->lock, lock_flags); + guard(spinlock_irqsave)(&esai_priv->lock); /* Save the registers */ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); @@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) fsl_esai_trigger_start(esai_priv, tx); if (enabled[rx]) fsl_esai_trigger_start(esai_priv, rx); - - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); } static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; esai_priv->channels[tx] = substream->runtime->channels; @@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_start(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_start(esai_priv, tx); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_stop(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_stop(esai_priv, tx); break; default: return -EINVAL; From a1b865cff274b8dc35f520f63ca4ef711b442869 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:18 +0700 Subject: [PATCH 026/791] ASoC: fsl_spdif: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1b9be85b34c2..ad1206ed9882 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; memcpy(&ucontrol->value.iec958.subcode[0], &ctrl->subcode[idx], SPDIF_UBITS_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } @@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; memcpy(&ucontrol->value.bytes.data[0], &ctrl->qsub[idx], SPDIF_QSUB_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } From 4d748e7082ec11764863bd184f6d58cd43584f54 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:19 +0700 Subject: [PATCH 027/791] ASoC: fsl_ssi: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index b2e1da1781ae..dc022976c982 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, if (reg > 0x7f) return; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 write clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return; } lreg = reg << 12; @@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, udelay(100); clk_disable_unprepare(fsl_ac97_data->clk); - -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); } static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, @@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, unsigned int lreg; int ret; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return val; } lreg = (reg & 0x7f) << 12; @@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, clk_disable_unprepare(fsl_ac97_data->clk); -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); return val; } From 94b0cb1dd6d9633d17bcd3242597b0c70e5118a8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:20 +0700 Subject: [PATCH 028/791] ASoC: fsl_xcvr: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 6677d3bf36ec..41d100500534 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; int ret = 0; - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_ISR_CMDC_TX_EN); if (ret < 0) { dev_err(dai->dev, "err updating isr %d\n", ret); - goto release_lock; + return ret; } fallthrough; case FSL_XCVR_MODE_SPDIF: @@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); - goto release_lock; + return ret; } break; } @@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); if (ret < 0) { dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); if (ret < 0) { dev_err(dai->dev, "Error while setting IER0: %d\n", ret); - goto release_lock; + return ret; } /* clear DPATH RESET */ @@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } break; @@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); - goto release_lock; + return ret; } if (xcvr->soc_data->spdif_only) break; @@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) { dev_err(dai->dev, "Err updating ISR %d\n", ret); - goto release_lock; + return ret; } break; } @@ -916,8 +915,6 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, break; } -release_lock: - spin_unlock_irqrestore(&xcvr->lock, lock_flags); return ret; } @@ -1448,11 +1445,10 @@ static void reset_rx_work(struct work_struct *work) { struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); struct device *dev = &xcvr->pdev->dev; - unsigned long lock_flags; u32 ext_ctrl; dev_dbg(dev, "reset rx path\n"); - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { @@ -1469,7 +1465,6 @@ static void reset_rx_work(struct work_struct *work) FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, 0); } - spin_unlock_irqrestore(&xcvr->lock, lock_flags); } static irqreturn_t irq0_isr(int irq, void *devid) From 929d412fdb0d9cb0bd7bae1b1b64cfaf3cc24524 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:21 +0700 Subject: [PATCH 029/791] ASoC: imx-audio-rpmsg: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audio-rpmsg.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index 38aafb8954c7..b55dfbdb4502 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; struct rpmsg_info *info; struct rpmsg_msg *msg; - unsigned long flags; if (!rpmsg->rpmsg_pdev) return 0; @@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, /* TYPE C is notification from M core */ switch (r_msg->header.cmd) { case TX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[TX], flags); - msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[TX]; - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[TX]; + } info->callback[TX](info->callback_param[TX]); break; case RX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[RX], flags); - msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[1]; - spin_unlock_irqrestore(&info->lock[RX], flags); + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[1]; + } info->callback[RX](info->callback_param[RX]); break; default: From 11ce4dd4e7bef521e82694a895dbd926b25084da Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:22 +0700 Subject: [PATCH 030/791] ASoC: fsl_rpmsg: Use guard() for mutex & spin locks Clean up the code using guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-pcm-rpmsg.c | 69 +++++++++++++++-------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2a4813c6cda9..ee741f3d79bd 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, struct rpmsg_device *rpdev = info->rpdev; int ret = 0; - mutex_lock(&info->msg_lock); + guard(mutex)(&info->msg_lock); if (!rpdev) { dev_err(info->dev, "rpmsg channel not ready\n"); - mutex_unlock(&info->msg_lock); return -EINVAL; } @@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, sizeof(struct rpmsg_s_msg)); if (ret) { dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); - mutex_unlock(&info->msg_lock); return ret; } /* No receive msg for TYPE_C command */ - if (msg->s_msg.header.type == MSG_TYPE_C) { - mutex_unlock(&info->msg_lock); + if (msg->s_msg.header.type == MSG_TYPE_C) return 0; - } /* wait response from rpmsg */ ret = wait_for_completion_timeout(&info->cmd_complete, @@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, if (!ret) { dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", msg->s_msg.header.cmd); - mutex_unlock(&info->msg_lock); return -ETIMEDOUT; } @@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, info->r_msg.param.resp); - mutex_unlock(&info->msg_lock); - return 0; } @@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, struct rpmsg_msg *msg, struct rpmsg_info *info) { - unsigned long flags; int ret = 0; /* * Queue the work to workqueue. * If the queue is full, drop the message. */ - spin_lock_irqsave(&info->wq_lock, flags); + guard(spinlock_irqsave)(&info->wq_lock); if (info->work_write_index != info->work_read_index) { int index = info->work_write_index; @@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, info->msg_drop_count[substream->stream]++; ret = -EPIPE; } - spin_unlock_irqrestore(&info->wq_lock, flags); return ret; } @@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, snd_pcm_sframes_t avail; struct timer_list *timer; struct rpmsg_msg *msg; - unsigned long flags; int buffer_tail = 0; int written_num; @@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, msg->s_msg.param.buffer_tail = buffer_tail; /* The notification message is updated to latest */ - spin_lock_irqsave(&info->lock[substream->stream], flags); - memcpy(&info->notify[substream->stream], msg, - sizeof(struct rpmsg_s_msg)); - info->notify_updated[substream->stream] = true; - spin_unlock_irqrestore(&info->lock[substream->stream], flags); + scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { + memcpy(&info->notify[substream->stream], msg, + sizeof(struct rpmsg_s_msg)); + info->notify_updated[substream->stream] = true; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) avail = snd_pcm_playback_hw_avail(runtime); @@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) bool is_notification = false; struct rpmsg_info *info; struct rpmsg_msg msg; - unsigned long flags; + bool updated; work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); info = work_of_rpmsg->info; @@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) * enough data in M core side, need to let M core know * data is updated immediately. */ - spin_lock_irqsave(&info->lock[TX], flags); - if (info->notify_updated[TX]) { - memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[TX] = false; - spin_unlock_irqrestore(&info->lock[TX], flags); - info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + updated = info->notify_updated[TX]; + if (updated) { + memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[TX] = false; + } } + if (updated) + info->send_message(&msg, info); - spin_lock_irqsave(&info->lock[RX], flags); - if (info->notify_updated[RX]) { - memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[RX] = false; - spin_unlock_irqrestore(&info->lock[RX], flags); - info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[RX], flags); + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + updated = info->notify_updated[RX]; + if (updated) { + memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[RX] = false; + } } + if (updated) + info->send_message(&msg, info); + /* Skip the notification message for it has been processed above */ if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && @@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) info->send_message(&work_of_rpmsg->msg, info); /* update read index */ - spin_lock_irqsave(&info->wq_lock, flags); - info->work_read_index++; - info->work_read_index %= WORK_MAX_NUM; - spin_unlock_irqrestore(&info->wq_lock, flags); + scoped_guard(spinlock_irqsave, &info->wq_lock) { + info->work_read_index++; + info->work_read_index %= WORK_MAX_NUM; + } } static int imx_rpmsg_pcm_probe(struct platform_device *pdev) From caed9fb2e428e0866c500b8dddabf4993a3aaa30 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:23 +0700 Subject: [PATCH 031/791] ASoC: fsl: mpc5200_dma: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_dma.c | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 56e2cf2f727b..bfedb2dea0b3 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { struct psc_dma_stream *s = _psc_dma_stream; - spin_lock(&s->psc_dma->lock); - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + scoped_guard(spinlock, &s->psc_dma->lock) { + /* + * For each finished period, dequeue the completed period buffer + * and enqueue a new one in its place + */ + while (bcom_buffer_done(s->bcom_task)) { + bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current = (s->period_current+1) % s->runtime->periods; - s->period_count++; + s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; - psc_dma_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); + } } - spin_unlock(&s->psc_dma->lock); /* If the stream is active, then also inform the PCM middle layer * of the period finished event. */ @@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; - unsigned long flags; int i; switch (cmd) { @@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, /* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ - spin_lock_irqsave(&psc_dma->lock, flags); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); - for (i = 0; i < runtime->periods; i++) - if (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); - - bcom_enable(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + bcom_enable(s->bcom_task); + } out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); @@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, substream->pstr->stream, s->period_count); s->active = 0; - spin_lock_irqsave(&psc_dma->lock, flags); - bcom_disable(s->bcom_task); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + bcom_disable(s->bcom_task); + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); + } break; From 8a2d7e46317a85327f8a7ea7d33139394b2b9662 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:24 +0700 Subject: [PATCH 032/791] ASoC: fsl: mpc5200_psc_ac97: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-12-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_ac97.c | 34 +++++++++++--------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 8554fb690772..d4d9f5b6bc07 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -31,14 +31,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) int status; unsigned int val; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command send status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (rdy)\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } @@ -54,19 +53,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) if (status == 0) { pr_err("timeout on ac97 read (val) %x\n", in_be16(&psc_dma->psc_regs->sr_csr.status)); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } /* Get the data */ val = in_be32(&psc_dma->psc_regs->ac97_data); if (((val >> 24) & 0x7f) != reg) { pr_err("reg echo error on ac97 read\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } val = (val >> 8) & 0xffff; - mutex_unlock(&psc_dma->mutex); return (unsigned short) val; } @@ -75,52 +71,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, { int status; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (write)\n"); - goto out; + return; } /* Write data */ out_be32(&psc_dma->psc_regs->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8)); - - out: - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_warm_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); udelay(3); out_be32(®s->sicr, psc_dma->sicr); - - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_cold_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); - dev_dbg(psc_dma->dev, "cold reset\n"); + scoped_guard(mutex, &psc_dma->mutex) { + dev_dbg(psc_dma->dev, "cold reset\n"); - mpc5200_psc_ac97_gpio_reset(psc_dma->id); + mpc5200_psc_ac97_gpio_reset(psc_dma->id); - /* Notify the PSC that a reset has occurred */ - out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); + /* Notify the PSC that a reset has occurred */ + out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); - /* Re-enable RX and TX */ - out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - - mutex_unlock(&psc_dma->mutex); + /* Re-enable RX and TX */ + out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + } usleep_range(1000, 2000); psc_ac97_warm_reset(ac97); From f16513ffa944347fe2680a236810fa2d236bb1cc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:00 +0700 Subject: [PATCH 033/791] ASoC: Intel: avs: Use guard() for locking Clean up the code using guard() for spin & mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 8 +++---- sound/soc/intel/avs/control.c | 7 ++---- sound/soc/intel/avs/core.c | 4 ++-- sound/soc/intel/avs/debug.h | 10 +++----- sound/soc/intel/avs/ipc.c | 11 ++++----- sound/soc/intel/avs/path.c | 30 +++++++---------------- sound/soc/intel/avs/utils.c | 45 +++++++++++++---------------------- 7 files changed, 39 insertions(+), 76 deletions(-) diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index b922eeaba843..cf600e1c986e 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) { struct avs_path *path; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ list_for_each_entry(path, &adev->path_list, node) { struct avs_path_pipeline *ppl; @@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) if (cfg->copier.dma_type == INVALID_OBJECT_ID) continue; - if (!mod->gtw_attrs.lp_buffer_alloc) { - spin_unlock(&adev->path_list_lock); + if (!mod->gtw_attrs.lp_buffer_alloc) return false; - } } } } - spin_unlock(&adev->path_list_lock); return true; } diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c index a8f05de338e0..370069247a7d 100644 --- a/sound/soc/intel/avs/control.c +++ b/sound/soc/intel/avs/control.c @@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i struct avs_path_pipeline *ppl; struct avs_path_module *mod; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { @@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || guid_equal(type, &AVS_GAIN_MOD_UUID)) && - mod->template->ctl_id == id) { - spin_unlock(&adev->path_list_lock); + mod->template->ctl_id == id) return mod; - } } } } - spin_unlock(&adev->path_list_lock); return NULL; } diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 1a53856c2ffb..2afe59646896 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -14,6 +14,7 @@ // foundation of this driver // +#include #include #include #include @@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) ret = IRQ_HANDLED; - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); /* Clear RIRB interrupt. */ status = snd_hdac_chip_readb(bus, RIRBSTS); if (status & RIRB_INT_MASK) { @@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) ret = IRQ_HANDLED; } - spin_unlock_irq(&bus->reg_lock); return ret; } diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h index 94fe8729a5c1..c47fc4e8b02b 100644 --- a/sound/soc/intel/avs/debug.h +++ b/sound/soc/intel/avs/debug.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H #define __SOUND_SOC_INTEL_AVS_DEBUG_H +#include #include "messages.h" #include "registers.h" @@ -26,14 +27,9 @@ struct avs_dev; static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) { - unsigned long flags; - int ret; + guard(spinlock_irqsave)(&adev->trace_lock); - spin_lock_irqsave(&adev->trace_lock, flags); - ret = avs_dsp_op(adev, log_buffer_status, msg); - spin_unlock_irqrestore(&adev->trace_lock, flags); - - return ret; + return avs_dsp_op(adev, log_buffer_status, msg); } struct avs_apl_log_buffer_layout { diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index c0feb9edd7f6..e99cb703eb7e 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request if (!ipc->ready) return -EPERM; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, reply); @@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request /* Same treatment as on exception, just stack_dump=0. */ avs_dsp_exception_caught(adev, &msg); } - goto exit; + return ret; } ret = ipc->rx.rsp.status; @@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request memcpy(reply->data, ipc->rx.data, reply->size); } -exit: - mutex_unlock(&ipc->msg_mutex); return ret; } @@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req struct avs_ipc *ipc = adev->ipc; int ret; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, NULL); @@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", name, request->glb.primary, request->glb.ext.val, ret); - mutex_unlock(&ipc->msg_mutex); - return ret; } diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 2291f9728a54..213d6ecdd7cc 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) if (!template) return NULL; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); /* Only one variant of given path template may be instantiated at a time. */ list_for_each_entry(path, &adev->path_list, node) { - if (path->template->owner == template) { - spin_unlock(&adev->path_list_lock); + if (path->template->owner == template) return path; - } } - spin_unlock(&adev->path_list_lock); return NULL; } @@ -1305,7 +1303,7 @@ void avs_path_free(struct avs_path *path) struct avs_path *cpath, *csave; struct avs_dev *adev = path->owner; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Free all condpaths this path spawned. */ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) @@ -1314,8 +1312,6 @@ void avs_path_free(struct avs_path *path) avs_condpath_free(path->owner, cpath); avs_path_free_unlocked(path); - - mutex_unlock(&adev->path_mutex); } struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, @@ -1334,13 +1330,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, } /* Serialize path and its components creation. */ - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Satisfy needs of avs_path_find_tplg(). */ - mutex_lock(&adev->comp_list_mutex); + guard(mutex)(&adev->comp_list_mutex); path = avs_path_create_unlocked(adev, dma_id, variant); if (IS_ERR(path)) - goto exit; + return path; ret = avs_condpaths_walk_all(adev, path); if (ret) { @@ -1348,10 +1344,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, path = ERR_PTR(ret); } -exit: - mutex_unlock(&adev->comp_list_mutex); - mutex_unlock(&adev->path_mutex); - return path; } @@ -1496,15 +1488,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* If either source or sink stops, so do the attached conditional paths. */ list_for_each_entry(cpath, &path->source_list, source_node) avs_condpath_pause(adev, cpath); list_for_each_entry(cpath, &path->sink_list, sink_node) avs_condpath_pause(adev, cpath); - - mutex_unlock(&adev->path_mutex); } int avs_path_pause(struct avs_path *path) @@ -1560,7 +1550,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Run conditional paths only if source and sink are both running. */ list_for_each_entry(cpath, &path->source_list, source_node) @@ -1572,8 +1562,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t if (cpath->source->state == AVS_PPL_STATE_RUNNING && cpath->sink->state == AVS_PPL_STATE_RUNNING) avs_condpath_run(adev, cpath, trigger); - - mutex_unlock(&adev->path_mutex); } int avs_path_run(struct avs_path *path, int trigger) diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index ee36725ac731..ea14ec173855 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_entry_index(adev, uuid); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) bool ret = false; int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) ret = ida_is_empty(adev->mod_idas[idx]); - mutex_unlock(&adev->modres_mutex); return ret; } @@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) if (ret) return AVS_IPC_RET(ret); - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); ret = avs_module_ida_alloc(adev, info, purge); if (ret < 0) { dev_err(adev->dev, "initialize module idas failed: %d\n", ret); - goto exit; + return ret; } /* Refresh current information with newly received table. */ kfree(adev->mods_info); adev->mods_info = info; -exit: - mutex_unlock(&adev->modres_mutex); return ret; } void avs_module_info_free(struct avs_dev *adev) { - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); avs_module_ida_destroy(adev); kfree(adev->mods_info); adev->mods_info = NULL; - - mutex_unlock(&adev->modres_mutex); } int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) { - int ret, idx, max_id; + int idx, max_id; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx == -ENOENT) { dev_err(adev->dev, "invalid module id: %d", module_id); - ret = -EINVAL; - goto exit; + return -EINVAL; } max_id = adev->mods_info->entries[idx].instance_max_count - 1; - ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); -exit: - mutex_unlock(&adev->modres_mutex); - return ret; + + return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); } void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); - if (idx == -ENOENT) { + if (idx == -ENOENT) dev_err(adev->dev, "invalid module id: %d", module_id); - goto exit; - } - - ida_free(adev->mod_idas[idx], instance_id); -exit: - mutex_unlock(&adev->modres_mutex); + else + ida_free(adev->mod_idas[idx], instance_id); } /* From 6aaac9f6baa55cbc4c5cd6071dba11928dc15302 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:01 +0700 Subject: [PATCH 034/791] ASoC: Intel: avs: Use scoped_guard() for scoped locking Clean up the code using scoped_guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/debugfs.c | 19 +++++++------- sound/soc/intel/avs/ipc.c | 48 +++++++++++++++++------------------ sound/soc/intel/avs/loader.c | 15 ++++++----- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 701c247227bf..9ab503da3b75 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -251,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); struct avs_dev *adev = file->private_data; unsigned long resource_mask; - unsigned long flags, i; + unsigned long i; u32 num_cores; resource_mask = adev->logged_resources; num_cores = adev->hw_cfg.dsp_cores; - spin_lock_irqsave(&adev->trace_lock, flags); + scoped_guard(spinlock_irqsave, &adev->trace_lock) { + /* Gather any remaining logs. */ + for_each_set_bit(i, &resource_mask, num_cores) { + msg.log.core = i; + avs_dsp_op(adev, log_buffer_status, &msg); + } - /* Gather any remaining logs. */ - for_each_set_bit(i, &resource_mask, num_cores) { - msg.log.core = i; - avs_dsp_op(adev, log_buffer_status, &msg); + kfifo_free(&adev->trace_fifo); } - kfifo_free(&adev->trace_fifo); - - spin_unlock_irqrestore(&adev->trace_lock, flags); - module_put(adev->dev->driver->owner); return 0; } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index e99cb703eb7e..71e7997e52c2 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -100,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) unsigned int core_mask; int ret; - mutex_lock(&adev->comp_list_mutex); - /* disconnect all running streams */ - list_for_each_entry(acomp, &adev->comp_list, node) { - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_card *card; + scoped_guard(mutex, &adev->comp_list_mutex) { + /* disconnect all running streams */ + list_for_each_entry(acomp, &adev->comp_list, node) { + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_card *card; - card = acomp->base.card; - if (!card) - continue; - - for_each_card_rtds(card, rtd) { - struct snd_pcm *pcm; - int dir; - - pcm = rtd->pcm; - if (!pcm || rtd->dai_link->no_pcm) + card = acomp->base.card; + if (!card) continue; - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream; + for_each_card_rtds(card, rtd) { + struct snd_pcm *pcm; + int dir; - substream = pcm->streams[dir].substream; - if (!substream || !substream->runtime) + pcm = rtd->pcm; + if (!pcm || rtd->dai_link->no_pcm) continue; - /* No need for _irq() as we are in nonatomic context. */ - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock(substream); + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream; + + substream = pcm->streams[dir].substream; + if (!substream || !substream->runtime) + continue; + + /* No need for _irq() as we are in nonatomic context. */ + snd_pcm_stream_lock(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock(substream); + } } } } - mutex_unlock(&adev->comp_list_mutex); /* forcibly shutdown all cores */ core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 353e343b1d28..bebdc79ec88e 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) if (ret) goto reenable_gating; - mutex_lock(&adev->comp_list_mutex); - list_for_each_entry(acomp, &adev->comp_list, node) { - struct avs_tplg *tplg = acomp->tplg; + scoped_guard(mutex, &adev->comp_list_mutex) { + list_for_each_entry(acomp, &adev->comp_list, node) { + struct avs_tplg *tplg = acomp->tplg; - ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); - if (ret < 0) - break; + ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); + if (ret < 0) + break; + } } - mutex_unlock(&adev->comp_list_mutex); reenable_gating: avs_hda_l1sen_enable(adev, true); From 4889a8a73f65b8e4feb49ea434b8a41806513574 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:02 +0700 Subject: [PATCH 035/791] ASoC: intel: atom: Use __free(kfree) for stream pointer Declare 'stream' with __free(kfree) so it is automatically freed when leaving scope. This allows direct returns from error paths and removes the explicit kfree(stream) call. Set 'stream' to NULL after ownership has been transferred to runtime->private_data to prevent it from being freed on the success path. This cleanup is a preparation step for upcoming locking changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index f074af2499c8..67506a718158 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -304,7 +305,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, { int ret_val = 0; struct snd_pcm_runtime *runtime = substream->runtime; - struct sst_runtime_stream *stream; + struct sst_runtime_stream *stream __free(kfree) = NULL; stream = kzalloc_obj(*stream); if (!stream) @@ -330,7 +331,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, ret_val = power_up_sst(stream); if (ret_val < 0) - goto out_power_up; + return ret_val; /* * Make sure the period to be multiple of 1ms to align the @@ -347,12 +348,19 @@ static int sst_media_open(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); - return snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); + ret_val = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + if (ret_val < 0) + return ret_val; + + stream = NULL; + + return ret_val; + out_ops: mutex_unlock(&sst_lock); -out_power_up: - kfree(stream); + return ret_val; } From 71c0f725d35de81b06076a4d1448ea4733993a4b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:03 +0700 Subject: [PATCH 036/791] ASoC: Intel: atom: Use guard() for locking Clean up the code using guard() for spin & mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260626082904.32344-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-atom-controls.c | 29 ++++++-------------- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 25 ++++++----------- sound/soc/intel/atom/sst/sst_ipc.c | 5 ++-- sound/soc/intel/atom/sst/sst_pvt.c | 9 +++--- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 3629ceaaac17..701369349fb6 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -14,6 +14,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, void *cmd_data, u16 len) { - int ret; + guard(mutex)(&drv->lock); - mutex_lock(&drv->lock); - ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, - task_id, pipe_id, cmd_data, len); - mutex_unlock(&drv->lock); - - return ret; + return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, + task_id, pipe_id, cmd_data, len); } /* @@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, unsigned int val, mux; u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); val = 1 << ctl_no; /* search which slot/channel has this bit set - there should be only one */ for (mux = e->max; mux > 0; mux--) @@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, break; ucontrol->value.enumerated.item[0] = mux; - mutex_unlock(&drv->lock); dev_dbg(c->dev, "%s - %s map = %#x\n", is_tx ? "tx channel" : "rx slot", @@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, if (mux > e->max - 1) return -EINVAL; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); /* first clear all registers of this bit */ for (i = 0; i < e->max; i++) map[i] &= ~val; @@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, /* kctl set to 'none' and we reset the bits so send IPC */ ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, struct sst_algo_control *bc = (void *)kcontrol->private_value; dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (bc->type) { case SST_ALGO_PARAMS: memcpy(bc->params, ucontrol->value.bytes.data, bc->max); break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", bc->type); return -EINVAL; @@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, /*if pipe is enabled, need to send the algo params from here*/ if (bc->w && bc->w->power) ret = sst_send_algo_cmd(drv, bc); - mutex_unlock(&drv->lock); return ret; } @@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; struct sst_gain_value *gv = mc->gain_val; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (mc->type) { case SST_GAIN_TLV: @@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", mc->type); return -EINVAL; @@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, if (mc->w && mc->w->power) ret = sst_send_gain_cmd(drv, gv, mc->task_id, mc->pipe_id | mc->instance_id, mc->module_id, 0); - mutex_unlock(&drv->lock); return ret; } @@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, struct sst_data *drv = snd_soc_component_get_drvdata(c); struct sst_ids *ids = w->priv; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); sst_find_and_send_pipe_algo(drv, w->name, ids); sst_set_pipe_gain(ids, drv, 0); - mutex_unlock(&drv->lock); return 0; } diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 67506a718158..c757e4dcd7cf 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -33,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) return -EINVAL; if (!try_module_get(dev->dev->driver->owner)) return -ENODEV; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); if (sst) { dev_err(dev->dev, "we already have a device %s\n", sst->name); module_put(dev->dev->driver->owner); @@ -42,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) } dev_dbg(dev->dev, "registering device %s\n", dev->name); sst = dev; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_register_dsp); @@ -54,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) if (dev != sst) return -EINVAL; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); - if (!sst) { - mutex_unlock(&sst_lock); + if (!sst) return -EIO; - } module_put(sst->dev->driver->owner); dev_dbg(dev->dev, "unreg %s\n", sst->name); sst = NULL; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_unregister_dsp); @@ -104,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) void sst_set_stream_status(struct sst_runtime_stream *stream, int state) { - unsigned long flags; - spin_lock_irqsave(&stream->status_lock, flags); + guard(spinlock_irqsave)(&stream->status_lock); stream->stream_status = state; - spin_unlock_irqrestore(&stream->status_lock, flags); } static inline int sst_get_stream_status(struct sst_runtime_stream *stream) { - int state; - unsigned long flags; - - spin_lock_irqsave(&stream->status_lock, flags); - state = stream->stream_status; - spin_unlock_irqrestore(&stream->status_lock, flags); - return state; + guard(spinlock_irqsave)(&stream->status_lock); + return stream->stream_status; } static void sst_fill_alloc_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 0d5e71e8a5b5..6c19ab63aa4f 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) union interrupt_reg_mrfld isr; union interrupt_reg_mrfld imr; union ipc_header_mrfld clear_ipc; - unsigned long irq_flags; - spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); @@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) /* un mask busy interrupt */ imr.part.busy_interrupt = 0; sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); - spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); } diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 67b1ab14239f..0b0cfd70efbc 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) void sst_set_fw_state_locked( struct intel_sst_drv *sst_drv_ctx, int sst_state) { - mutex_lock(&sst_drv_ctx->sst_lock); + guard(mutex)(&sst_drv_ctx->sst_lock); sst_drv_ctx->sst_state = sst_state; - mutex_unlock(&sst_drv_ctx->sst_lock); } /* @@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) { int local; - spin_lock(&drv->block_lock); + guard(spinlock)(&drv->block_lock); /* find first zero index from lsb */ local = ffz(drv->pvt_id); dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); if (local >= SST_MAX_BLOCKS){ - spin_unlock(&drv->block_lock); dev_err(drv->dev, "PVT _ID error: no free id blocks "); return -EINVAL; } /* toggle the index */ change_bit(local, &drv->pvt_id); - spin_unlock(&drv->block_lock); + return local; } From 08d2d5e683e85a7dc33c1abfd50fdc380bfafb5a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:04 +0700 Subject: [PATCH 037/791] ASoC: Intel: atom: Use scoped_guard() for scoped locking Clean up the code using scoped_guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-atom-controls.c | 38 ++++++++++---------- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 20 ++++------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 701369349fb6..82df398237da 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -750,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) return ret; } - mutex_lock(&drv->lock); - if (enable) - timer_usage++; - else - timer_usage--; - - /* - * Send the command only if this call is the first enable or last - * disable - */ - if ((enable && (timer_usage == 1)) || - (!enable && (timer_usage == 0))) { - ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, - SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, - sizeof(cmd.header) + cmd.header.length); - if (ret && enable) { + scoped_guard(mutex, &drv->lock) { + if (enable) + timer_usage++; + else timer_usage--; - enable = false; + + /* + * Send the command only if this call is the first enable or last + * disable + */ + if ((enable && timer_usage == 1) || + (!enable && timer_usage == 0)) { + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, + SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + + cmd.header.length); + if (ret && enable) { + timer_usage--; + enable = false; + } } } - mutex_unlock(&drv->lock); if (!enable) sst->ops->power(sst->dev, false); diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index c757e4dcd7cf..9ee4d9926e06 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -304,15 +304,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, spin_lock_init(&stream->status_lock); /* get the sst ops */ - mutex_lock(&sst_lock); - if (!sst || - !try_module_get(sst->dev->driver->owner)) { - dev_err(dai->dev, "no device available to run\n"); - ret_val = -ENODEV; - goto out_ops; + scoped_guard(mutex, &sst_lock) { + if (!sst || + !try_module_get(sst->dev->driver->owner)) { + dev_err(dai->dev, "no device available to run\n"); + return -ENODEV; + } + stream->ops = sst->ops; } - stream->ops = sst->ops; - mutex_unlock(&sst_lock); stream->stream_info.str_id = 0; @@ -347,11 +346,6 @@ static int sst_media_open(struct snd_pcm_substream *substream, stream = NULL; - return ret_val; - -out_ops: - mutex_unlock(&sst_lock); - return ret_val; } From 84eb7c8e59ccbcd45b2678abac56b778c97ee270 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 22 Jun 2026 17:11:39 +0800 Subject: [PATCH 038/791] ASoC: rt1321: move DSP status checking into a helper function This patch creates the rt1320_dspfw_status helper function to check DSP status. The new function can be reused by other functions and supports checking the DSP status across different amplifiers. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260622091139.2183297-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 117 ++++++++++++++-------------------- 1 file changed, 49 insertions(+), 68 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 13493b85f3c9..8d6fe2e36fc7 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -893,6 +893,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) return 0; } +static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int fw_status_addr, fw_ready; + unsigned int dspfw_run; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + fw_status_addr = RT1320_DSPFW_STATUS_ADDR; + break; + case RT1321_DEV_ID: + fw_status_addr = RT1321_DSPFW_STATUS_ADDR; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); + fw_ready &= 0x1; + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); + dspfw_run &= 0x1; + fw_ready = (!dspfw_run && fw_ready); + } + + if (fw_ready) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + return 1; + } + + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + return 0; +} + static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) { struct device *dev = &rt1320->sdw_slave->dev; @@ -1126,30 +1162,18 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; struct rt1320_datafixpoint audfixpoint[2]; unsigned int reg_c5fb, reg_c570, reg_cd00; - unsigned int vol_reg[4], fw_ready; + unsigned int vol_reg[4]; unsigned long long l_meanr0, r_meanr0; - unsigned int fw_status_addr; int l_re[5], r_re[5]; int ret, tmp; unsigned long long factor = (1 << 27); unsigned short l_advancegain, r_advancegain; unsigned int delay_s = 7; /* delay seconds for the calibration */ + int dspfw_status; if (!rt1320->component) return; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - /* set volume 0dB */ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); @@ -1172,9 +1196,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); goto _finish_; } @@ -1383,30 +1406,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) { struct device *dev = &rt1320->sdw_slave->dev; - unsigned int factor = (1 << 22), fw_ready; + unsigned int factor = (1 << 22); int l_t0_data[38], r_t0_data[38]; - unsigned int fw_status_addr; - - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } + int dspfw_status; regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); goto _exit_; } @@ -1599,8 +1609,7 @@ struct rt1320_dspfwheader { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); struct device *dev = &rt1320->sdw_slave->dev; - unsigned int val, i, fw_offset, fw_ready; - unsigned int fw_status_addr; + unsigned int val, i, fw_offset; struct rt1320_dspfwheader *fwheader; struct rt1320_imageinfo *ptr_img; struct sdw_bpt_section sec[10]; @@ -1613,18 +1622,6 @@ struct rt1320_dspfwheader { int len_vendor, len_product, len_sku; char filename[512]; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); @@ -1654,9 +1651,7 @@ struct rt1320_dspfwheader { RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (fw_ready) { + if (rt1320_dspfw_status(rt1320)) { dev_dbg(dev, "%s, DSP FW was already\n", __func__); rt1320->fw_load_done = true; goto _exit_; @@ -2327,25 +2322,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) { struct device *dev = regmap_get_device(rt1320->regmap); - unsigned int fw_status_addr; - unsigned int fw_ready; + int dspfw_status; int ret = 0; if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) return -EINVAL; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return -EINVAL; - } - regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); @@ -2354,9 +2336,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) goto _timeout_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); goto _timeout_; } From d79828f380b337a118ef93d7f4daabb41dde24bc Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 22 Jun 2026 17:11:48 +0800 Subject: [PATCH 039/791] ASoC: rt1321: add support for rt1321 VA1/VA2 This patch adds support for the RT1321 VA1 and VA2 amplifier. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260622091149.2183334-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 699 +++++++++++++++++++++++++++++++--- sound/soc/codecs/rt1320-sdw.h | 12 + 2 files changed, 653 insertions(+), 58 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 8d6fe2e36fc7..49fe100aef6e 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -411,6 +411,402 @@ static const struct reg_sequence rt1321_blind_write[] = { { 0x41001988, 0x03 }, }; +static const struct reg_sequence rt1321_va1_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x0000d73c, 0xb7 }, + { 0x0000d73d, 0xd7 }, + { 0x0000d73e, 0x00 }, + { 0x0000d73f, 0x10 }, + { 0x1000cd56, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2df80, 0x00 }, + { 0x3fc2df81, 0x00 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x0000d541, 0x40 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000c570, 0x08 }, + { 0x0000d714, 0x17 }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c8, 0x03 }, + { 0x20009012, 0x00 }, + { 0x0000dd08, 0x17 }, + { 0x0000dd09, 0x0e }, + { 0x0000dd0a, 0x17 }, + { 0x0000dd0b, 0x0e }, + { 0x0000c570, 0x08 }, + { 0x0000d471, 0x3a }, + { 0x0000db00, 0x00 }, + { 0x0000db01, 0x00 }, + { 0x0000db02, 0x73 }, + { 0x0000db03, 0x00 }, + { 0x0000db04, 0x00 }, + { 0x0000db05, 0x00 }, + { 0x0000db06, 0x00 }, + { 0x0000db07, 0x00 }, + { 0x0000db08, 0x7f }, + { 0x0000db09, 0x00 }, + { 0x0000db1a, 0x00 }, + { 0x0000db1b, 0x00 }, + { 0x0000db19, 0x00 }, +}; + +static const struct reg_sequence rt1321_va2_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x10008000, 0x67 }, + { 0x10008001, 0x80 }, + { 0x10008002, 0x00 }, + { 0x10008003, 0x00 }, + { 0x1000cd56, 0x00 }, + { 0x0000d486, 0x43 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfa3, 0x00 }, + { 0x3fc2dfa2, 0x00 }, + { 0x3fc2dfa1, 0x00 }, + { 0x3fc2dfa0, 0x00 }, + { 0x3fc2df80, 0x10 }, + { 0x3fc2df81, 0x20 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x3fc2df84, 0x50 }, + { 0x3fc2df85, 0x19 }, + { 0x3fc2df86, 0x00 }, + { 0x3fc2df87, 0x00 }, + { 0x3fc2df88, 0x52 }, + { 0x3fc2df89, 0x23 }, + { 0x3fc2df8a, 0x00 }, + { 0x3fc2df8b, 0x00 }, + { 0x3fc2df8c, 0xe0 }, + { 0x3fc2df8d, 0x2e }, + { 0x3fc2df8e, 0x00 }, + { 0x3fc2df8f, 0x00 }, + { 0x3fc2df90, 0xe0 }, + { 0x3fc2df91, 0x2e }, + { 0x3fc2df92, 0x00 }, + { 0x3fc2df93, 0x00 }, + { 0x3fc2df94, 0x01 }, + { 0x3fc2df95, 0x08 }, + { 0x3fc2df96, 0x00 }, + { 0x3fc2df97, 0x00 }, + { 0x3fc2df40, 0x80 }, + { 0x3fc2df41, 0xbb }, + { 0x3fc2df42, 0x00 }, + { 0x3fc2df43, 0x00 }, + { 0x3fc2df44, 0xc0 }, + { 0x3fc2df45, 0x99 }, + { 0x3fc2df46, 0x01 }, + { 0x3fc2df47, 0x00 }, + { 0x3fc2df48, 0x00 }, + { 0x3fc2df49, 0x00 }, + { 0x3fc2df4a, 0x00 }, + { 0x3fc2df4b, 0x00 }, + { 0x3fc2df4c, 0x00 }, + { 0x3fc2df4d, 0x00 }, + { 0x3fc2df4e, 0x00 }, + { 0x3fc2df4f, 0x00 }, + { 0x3fc2df50, 0x01 }, + { 0x3fc2df51, 0x00 }, + { 0x3fc2df52, 0x00 }, + { 0x3fc2df53, 0x00 }, + { 0x3fc2df54, 0x01 }, + { 0x3fc2df55, 0x00 }, + { 0x3fc2df56, 0x00 }, + { 0x3fc2df57, 0x00 }, + { 0x3fc2df58, 0x00 }, + { 0x3fc2df59, 0x00 }, + { 0x3fc2df5a, 0x00 }, + { 0x3fc2df5b, 0x00 }, + { 0x3fc2df5c, 0x01 }, + { 0x3fc2df5d, 0x00 }, + { 0x3fc2df5e, 0x00 }, + { 0x3fc2df5f, 0x00 }, + { 0x3fc2df60, 0x00 }, + { 0x3fc2df61, 0x00 }, + { 0x3fc2df62, 0x00 }, + { 0x3fc2df63, 0x00 }, + { 0x3fc2df64, 0x00 }, + { 0x3fc2df65, 0x00 }, + { 0x3fc2df66, 0x00 }, + { 0x3fc2df67, 0x10 }, + { 0x3fc2df68, 0x01 }, + { 0x3fc2df69, 0x00 }, + { 0x3fc2df6a, 0x00 }, + { 0x3fc2df6b, 0x00 }, + { 0x3fc2df6c, 0x01 }, + { 0x3fc2df6d, 0x00 }, + { 0x3fc2df6e, 0x00 }, + { 0x3fc2df6f, 0x00 }, + { 0x3fc2df70, 0x04 }, + { 0x3fc2df71, 0x00 }, + { 0x3fc2df72, 0x00 }, + { 0x3fc2df73, 0x00 }, + { 0x3fc2df74, 0x01 }, + { 0x3fc2df75, 0x00 }, + { 0x3fc2df76, 0x00 }, + { 0x3fc2df77, 0x00 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000d714, 0x17 }, + { 0x0000dd0b, 0x0d }, + { 0x0000dd0a, 0xff }, + { 0x0000dd09, 0x0d }, + { 0x0000dd08, 0xff }, + { 0x0000c5fb, 0x12 }, + { 0x0000c570, 0x08 }, +}; + static const struct reg_default rt1320_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, @@ -498,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x20003000 ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x20005800 ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x20009000 ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc000c0 ... 0x3fc2dfc8: case 0x3fe00000 ... 0x3fe36fff: /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ @@ -597,15 +998,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0x1000c000 ... 0x1000dfff: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x2000300e ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x2000580e ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x2000900e ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc2ab80 ... 0x3fc2ac4c: case 0x3fc2b780: case 0x3fc2bf80 ... 0x3fc2bf83: case 0x3fc2bfc0 ... 0x3fc2bfc8: - case 0x3fc2d300 ... 0x3fc2d354: + case 0x3fc2d300 ... 0x3fc2d3cc: case 0x3fc2dfc0 ... 0x3fc2dfc8: case 0x3fe2e000 ... 0x3fe2e003: case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): @@ -1019,6 +1425,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ if (!tempbuf) return -ENOMEM; + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) + paramid += 0x0bff0000; + paramhr.moudleid = 1; paramhr.commandtype = cmdid; /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ @@ -1157,6 +1566,52 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); } +static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + int l_targetpostgain, r_targetpostgain; + unsigned long long factor = (1 << 12); + int l_pilotgain[9], r_pilotgain[9]; + const int postgain_step = 234; + int targetGain; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + targetGain = -320000; + break; + case RT1321_DEV_ID: + targetGain = -420000; + if (rt1320->version_id == RT1321_VA0) + targetGain = -320000; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); + dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); + + /* calculate pilot tone gain */ + l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; + r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; + + /* calculate post gain to meet target gain */ + l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; + r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; + l_targetpostgain = 0xfff - l_targetpostgain; + r_targetpostgain = 0xfff - r_targetpostgain; + dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); + + regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); + regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); + + return 0; +} + static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) { struct device *dev = &rt1320->sdw_slave->dev; @@ -1174,16 +1629,10 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) if (!rt1320->component) return; - /* set volume 0dB */ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); - regmap_write(rt1320->regmap, 0xdd0b, 0x0f); - regmap_write(rt1320->regmap, 0xdd0a, 0xff); - regmap_write(rt1320->regmap, 0xdd09, 0x0f); - regmap_write(rt1320->regmap, 0xdd08, 0xff); - regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); regmap_read(rt1320->regmap, 0xc570, ®_c570); regmap_read(rt1320->regmap, 0xcd00, ®_cd00); @@ -1208,8 +1657,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - if (rt1320->dev_id == RT1320_DEV_ID) - regmap_write(rt1320->regmap, 0xc5fb, 0x00); + /* fine tune pilot tone output */ + ret = rt1320_pilot_tone_output(rt1320); + if (ret < 0) { + dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); + goto _finish_; + } + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_write(rt1320->regmap, 0xc5c4, 0x12); + } + + regmap_write(rt1320->regmap, 0xc5fb, 0x00); regmap_write(rt1320->regmap, 0xc570, 0x0b); regmap_write(rt1320->regmap, 0xcd00, 0xc5); @@ -1270,6 +1730,11 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + } + /* advance gain will be set when R0 load, not here */ regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); @@ -1336,6 +1801,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) max_addr = 0x10007fff; break; case RT1321_DEV_ID: + if (rt1320->version_id == RT1321_VA2) + return; + filename = RT1321_VA_MCU_PATCH; min_addr = 0x10008000; max_addr = 0x10008fff; @@ -1494,22 +1962,48 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) request_firmware(&rae_fw, rae_filename, dev); if (rae_fw) { - /* RAE CRC clear */ - regmap_write(rt1320->regmap, 0xe80b, 0x0f); - - /* RAE stop & CRC disable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); - - while (--retry) { - regmap_read(rt1320->regmap, 0xe83f, &value); - if (value & 0x40) - break; - usleep_range(1000, 1100); - } - if (!retry && !(value & 0x40)) { - dev_err(dev, "%s: RAE is not ready to load\n", __func__); - release_firmware(rae_fw); - return -ETIMEDOUT; + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0xe80b, 0x0f); + /* RAE stop & CRC disable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); + while (--retry) { + regmap_read(rt1320->regmap, 0xe83f, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); + return -ETIMEDOUT; + } + break; + case RT1321_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0x2000300e, 0xc0); + regmap_write(rt1320->regmap, 0x2000300f, 0x0f); + /* RAE stop & Phase sync & CRC disable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); + /* check whether write state is ready */ + while (--retry) { + regmap_read(rt1320->regmap, 0x20003043, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); + return -ETIMEDOUT; + } + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; } dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); @@ -1570,18 +2064,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) goto _exit_; } - /* RAE CRC enable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); - - /* RAE update */ - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); - - /* RAE run */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); - - regmap_read(rt1320->regmap, 0xe80b, &value); - dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); + regmap_read(rt1320->regmap, 0xe80b, &value); + dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + break; + case RT1321_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); + /* Phase sync eanble */ + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + break; + } rt1320->rae_update_done = true; @@ -1620,6 +2130,8 @@ struct rt1320_dspfwheader { unsigned int hdr_size = 0; const char *dmi_vendor, *dmi_product, *dmi_sku; int len_vendor, len_product, len_sku; + unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ + unsigned char has_0x3fc00000 = 0; char filename[512]; dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -1657,9 +2169,6 @@ struct rt1320_dspfwheader { goto _exit_; } - /* change to IRAM */ - regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); - request_firmware(&fw, filename, dev); if (fw) { fwheader = (struct rt1320_dspfwheader *)fw->data; @@ -1705,9 +2214,11 @@ struct rt1320_dspfwheader { dev_fw_match = true; break; case RT1321_DEV_ID: - if (ptr_img->addr == 0x3fc00000) - if (fw_data[9] == '1') + if (ptr_img->addr == 0x3fc00000) { + if (fw_data[7] == '1') dev_fw_match = true; + has_0x3fc00000 = 1; + } break; default: dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); @@ -1717,6 +2228,22 @@ struct rt1320_dspfwheader { fw_offset += ptr_img->size; } + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { + /* + * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, + * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. + */ + if (!has_0x3fc00000) { + boot_mode = 1; + dev_fw_match = true; + } + dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); + } + + /* change to IRAM */ + if (!boot_mode) + regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); + if (dev_fw_match) { dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; @@ -1739,24 +2266,39 @@ struct rt1320_dspfwheader { goto _exit_; } - /* run RAM code */ - regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); - val |= 0x8; - regmap_write(rt1320->regmap, 0x3fc2bfc0, val); - - /* clear frame counter */ switch (rt1320->dev_id) { case RT1320_DEV_ID: + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2bfc0, val); + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); break; case RT1321_DEV_ID: + if (!boot_mode) { + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2dfc0, val); + } + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); + /* enable handshake */ + regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); + regmap_write(rt1320->regmap, 0xd470, 0xad); + /* minimum phase settings */ + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + regmap_write(rt1320->regmap, 0x20003003, 0x31); + regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); + regmap_write(rt1320->regmap, 0xc5b3, 0x01); + regmap_write(rt1320->regmap, 0xc052, 0x11); break; } @@ -1839,14 +2381,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) static void rt1321_preset(struct rt1320_sdw_priv *rt1320) { + const struct reg_sequence *blindwrite; unsigned int i, reg, val, delay; + unsigned int array_size; - for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { - reg = rt1321_blind_write[i].reg; - val = rt1321_blind_write[i].def; - delay = rt1321_blind_write[i].delay_us; + switch (rt1320->version_id) { + case RT1321_VA0: + blindwrite = rt1321_blind_write; + array_size = ARRAY_SIZE(rt1321_blind_write); + break; + case RT1321_VA1: + blindwrite = rt1321_va1_blind_write; + array_size = ARRAY_SIZE(rt1321_va1_blind_write); + break; + case RT1321_VA2: + blindwrite = rt1321_va2_blind_write; + array_size = ARRAY_SIZE(rt1321_va2_blind_write); + break; + default: + dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", + __func__, rt1320->version_id); + return; + } - if (reg == 0x3fc2dfc3) + for (i = 0; i < array_size; i++) { + reg = blindwrite[i].reg; + val = blindwrite[i].def; + delay = blindwrite[i].delay_us; + + if (reg == 0x1000cd56) rt1320_load_mcu_patch(rt1320); regmap_write(rt1320->regmap, reg, val); @@ -1888,6 +2451,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); rt1320->dev_id = (val << 8) | tmp; + + /* This is a workaround that reads the value twice to obtain the correct result. */ + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + val = (val << 8) | tmp; + + if (rt1320->dev_id == RT1321_DEV_ID) { + if (rt1320->version_id == 0x01) + rt1320->version_id = RT1321_VA2; + else if (val == RT1321_DEV_HV_VA0_ID) + rt1320->version_id = RT1321_VA0; + else if (val == RT1321_DEV_HV_VA1_ID) + rt1320->version_id = RT1321_VA1; + else + dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); + } } regmap_read(rt1320->regmap, @@ -2440,7 +3021,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!rt1320->hw_init) return 0; - ret = pm_runtime_resume(component->dev); + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -2451,6 +3032,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!ucontrol->value.integer.value[0]) rt1320->fw_load_done = false; + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); return 0; } diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index a7b573883dd0..bc0f78e03529 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -17,12 +17,17 @@ #define RT1320_DEV_ID 0x6981 #define RT1321_DEV_ID 0x7045 +#define RT1321_DEV_HV_VA0_ID 0x6997 +#define RT1321_DEV_HV_VA1_ID 0x7071 /* imp-defined registers */ #define RT1320_DEV_VERSION_ID_1 0xc404 #define RT1320_DEV_ID_1 0xc405 #define RT1320_DEV_ID_0 0xc406 +#define RT1320_HV_DEV_ID_0 0xf622 +#define RT1320_HV_DEV_ID_1 0xf623 + #define RT1320_POWER_STATE 0xc560 #define RT1321_PATCH_MAIN_VER 0x1000cffe @@ -94,6 +99,12 @@ enum rt1320_version_id { RT1320_VC, }; +enum rt1321_version_id { + RT1321_VA0, + RT1321_VA1, + RT1321_VA2, +}; + #define RT1320_VER_B_ID 0x07392238 #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" @@ -121,6 +132,7 @@ struct rt1320_datafixpoint { int invrs; }; +/* FW parameter id 1300 */ typedef struct FwPara_HwSwGain { unsigned int SwAdvGain; unsigned int SwBasGain; From 99e361b2a50424ffd923dc151fd605afa5b213ce Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Sat, 20 Jun 2026 15:50:58 +0200 Subject: [PATCH 040/791] ASoC: rt5677: Add GPIO .get_direction() callback Implement the get_direction callback for the GPIO controller to allow consumers to query the direction of GPIO pins. Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20260620-smaug-audio-v1-1-e318acdf5abd@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index ac084ca008f3..73fc008d558a 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -6,6 +6,7 @@ * Author: Oder Chiou */ +#include #include #include #include @@ -4767,6 +4768,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return rt5677_update_gpio_bits(rt5677, offset, m, v); } +static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; + unsigned int bank = offset / 5; + unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; + int ret; + + ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); + if (ret < 0) + return ret; + + return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + /* * Configures the GPIO as * 0 - floating @@ -4834,6 +4850,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) static const struct gpio_chip rt5677_template_chip = { .label = RT5677_DRV_NAME, .owner = THIS_MODULE, + .get_direction = rt5677_gpio_get_direction, .direction_output = rt5677_gpio_direction_out, .set = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, From a5edc45d9cf6e60be1cbd39f68e92685a91b13ac Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Sat, 20 Jun 2026 15:50:59 +0200 Subject: [PATCH 041/791] ASoC: rt5677: Enable standalone compilation for generic card use Add a prompt string to make the RT5677 driver user-selectable, allowing it to be built independently for use with generic sound card bindings. Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20260620-smaug-audio-v1-2-e318acdf5abd@bootlin.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 252f683be3c1..e9de333c5c8a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1888,7 +1888,7 @@ config SND_SOC_RT5670 depends on I2C config SND_SOC_RT5677 - tristate + tristate "Realtek RT5677 Codec" depends on I2C select REGMAP_I2C select REGMAP_IRQ From a295be8d6210ab6a6ec9710b5b146cba34f36c6f Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:25 +0200 Subject: [PATCH 042/791] ASoC: meson: gx: add gx-formatter and gx-interface These files are the basic block which allow to shape I2S in GX devices the same as the AXG ones: the DAI backend only controls the interface (i.e. clocks and pins) whereas a formatter takes care of properly formatting the data. gx-formatter and gx-interface are strongly inspired to axg-tdm-formatter and axg-tdm, respectively. The long term plan is to join the two platforms to use the same formatter solution. There is only a minor addition here compared to what has been done for AXG and it's "gx_formatter_create()" which is required in order to let already existing AIU code to make use of this formatter without making any devicetree change. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-1-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Makefile | 1 + sound/soc/meson/gx-formatter.c | 282 +++++++++++++++++++++++++++++++++ sound/soc/meson/gx-formatter.h | 56 +++++++ sound/soc/meson/gx-interface.h | 48 ++++++ 4 files changed, 387 insertions(+) create mode 100644 sound/soc/meson/gx-formatter.c create mode 100644 sound/soc/meson/gx-formatter.h create mode 100644 sound/soc/meson/gx-interface.h diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 24078e4396b0..146ec81526ba 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -4,6 +4,7 @@ snd-soc-meson-aiu-y := aiu.o snd-soc-meson-aiu-y += aiu-acodec-ctrl.o snd-soc-meson-aiu-y += aiu-codec-ctrl.o snd-soc-meson-aiu-y += aiu-encoder-i2s.o +snd-soc-meson-aiu-y += gx-formatter.o snd-soc-meson-aiu-y += aiu-encoder-spdif.o snd-soc-meson-aiu-y += aiu-fifo.o snd-soc-meson-aiu-y += aiu-fifo-i2s.o diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c new file mode 100644 index 000000000000..311e63affb23 --- /dev/null +++ b/sound/soc/meson/gx-formatter.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti + +#include +#include +#include +#include + +#include "gx-formatter.h" + +struct gx_formatter { + struct list_head list; + struct gx_stream *stream; + const struct gx_formatter_driver *drv; + bool enabled; + struct regmap *map; +}; + +static int gx_formatter_enable(struct gx_formatter *formatter) +{ + int ret; + + /* Do nothing if the formatter is already enabled */ + if (formatter->enabled) + return 0; + + /* Setup the stream parameter in the formatter */ + if (formatter->drv->ops->prepare) { + ret = formatter->drv->ops->prepare(formatter->map, + formatter->drv->quirks, + formatter->stream); + if (ret) + return ret; + } + + /* Finally, actually enable the formatter */ + if (formatter->drv->ops->enable) + formatter->drv->ops->enable(formatter->map); + + formatter->enabled = true; + + return 0; +} + +static void gx_formatter_disable(struct gx_formatter *formatter) +{ + /* Do nothing if the formatter is already disabled */ + if (!formatter->enabled) + return; + + if (formatter->drv->ops->disable) + formatter->drv->ops->disable(formatter->map); + + formatter->enabled = false; +} + +static int gx_formatter_attach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Catch up if the stream is already running when we attach */ + if (ts->ready) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + list_add_tail(&formatter->list, &ts->formatter_list); +out: + mutex_unlock(&ts->lock); + return ret; +} + +static void gx_formatter_detach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + + if (!ts) + return; + + mutex_lock(&ts->lock); + list_del(&formatter->list); + mutex_unlock(&ts->lock); + + gx_formatter_disable(formatter); +} + +static int gx_formatter_power_up(struct gx_formatter *formatter, + struct snd_soc_dapm_widget *w) +{ + struct gx_stream *ts = formatter->drv->ops->get_stream(w); + int ret; + + /* + * If we don't get a stream at this stage, it would mean that the + * widget is powering up but is not attached to any backend DAI. + * It should not happen, ever ! + */ + if (WARN_ON(!ts)) + return -ENODEV; + + formatter->stream = ts; + INIT_LIST_HEAD(&formatter->list); + ret = gx_formatter_attach(formatter); + if (ret) + return ret; + + return 0; +} + +static void gx_formatter_power_down(struct gx_formatter *formatter) +{ + gx_formatter_detach(formatter); + formatter->stream = NULL; +} + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event) +{ + struct snd_soc_component *c; + struct gx_formatter *formatter; + int ret = 0; + + c = snd_soc_dapm_to_component(w->dapm); + + if (w->priv) + formatter = w->priv; + else + formatter = snd_soc_component_get_drvdata(c); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = gx_formatter_power_up(formatter, w); + break; + + case SND_SOC_DAPM_PRE_PMD: + gx_formatter_power_down(formatter); + break; + + default: + dev_err(c->dev, "Unexpected event %d\n", event); + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(gx_formatter_event); + +int gx_formatter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct gx_formatter_driver *drv; + struct gx_formatter *formatter; + void __iomem *regs; + + drv = of_device_get_match_data(dev); + if (!drv) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + platform_set_drvdata(pdev, formatter); + formatter->drv = drv; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); + if (IS_ERR(formatter->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(formatter->map)); + return PTR_ERR(formatter->map); + } + + return devm_snd_soc_register_component(dev, drv->component_drv, + NULL, 0); +} +EXPORT_SYMBOL_GPL(gx_formatter_probe); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap) +{ + struct gx_formatter *formatter; + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + + formatter->drv = drv; + formatter->map = regmap; + + w->priv = formatter; + + return 0; +} +EXPORT_SYMBOL_GPL(gx_formatter_create); + +int gx_stream_start(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Start all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + ts->ready = true; + +out: + mutex_unlock(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(gx_stream_start); + +void gx_stream_stop(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + + mutex_lock(&ts->lock); + ts->ready = false; + + /* Stop all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + gx_formatter_disable(formatter); + } + + mutex_unlock(&ts->lock); +} +EXPORT_SYMBOL_GPL(gx_stream_stop); + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface) +{ + struct gx_stream *ts; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + INIT_LIST_HEAD(&ts->formatter_list); + mutex_init(&ts->lock); + ts->iface = iface; + } + + return ts; +} +EXPORT_SYMBOL_GPL(gx_stream_alloc); + +void gx_stream_free(struct gx_stream *ts) +{ + /* + * If the list is not empty, it would mean that one of the formatter + * widget is still powered and attached to the interface while we + * are removing the TDM DAI. It should not be possible + */ + WARN_ON(!list_empty(&ts->formatter_list)); + mutex_destroy(&ts->lock); + kfree(ts); +} +EXPORT_SYMBOL_GPL(gx_stream_free); + +MODULE_DESCRIPTION("Amlogic GX formatter driver"); +MODULE_AUTHOR("Valerio Setti "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h new file mode 100644 index 000000000000..b90b1814d79b --- /dev/null +++ b/sound/soc/meson/gx-formatter.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti + */ + +#ifndef _MESON_GX_FORMATTER_H +#define _MESON_GX_FORMATTER_H + +#include "gx-interface.h" + +struct platform_device; +struct regmap; +struct snd_soc_dapm_widget; +struct snd_kcontrol; + +struct gx_formatter_hw { + unsigned int skew_offset; +}; + +struct gx_formatter_ops { + struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); + void (*enable)(struct regmap *map); + void (*disable)(struct regmap *map); + int (*prepare)(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts); +}; + +struct gx_formatter_driver { + const struct snd_soc_component_driver *component_drv; + const struct regmap_config *regmap_cfg; + const struct gx_formatter_ops *ops; + const struct gx_formatter_hw *quirks; +}; + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event); +int gx_formatter_probe(struct platform_device *pdev); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap); + +/* + * Formatter data is already freed when the associated device is removed, + * so we just need to remove the pointer from the widget. + */ +static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) +{ + w->priv = NULL; +} + +#endif /* _MESON_GX_FORMATTER_H */ diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h new file mode 100644 index 000000000000..65c46dcce32a --- /dev/null +++ b/sound/soc/meson/gx-interface.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti + */ + +#ifndef _MESON_GX_INTERFACE_H +#define _MESON_GX_INTERFACE_H + +#include +#include +#include +#include +#include + +struct gx_iface { + struct clk *mclk; + unsigned long mclk_rate; + + /* format is common to all the DAIs of the iface */ + unsigned int fmt; + + /* For component wide symmetry */ + int rate; + + /* Only for GX platform */ + int bs_quirk; +}; + +struct gx_stream { + struct gx_iface *iface; + struct list_head formatter_list; + struct mutex lock; + unsigned int channels; + unsigned int width; + unsigned int physical_width; + bool ready; + + /* For continuous clock tracking */ + bool clk_enabled; +}; + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface); +void gx_stream_free(struct gx_stream *ts); +int gx_stream_start(struct gx_stream *ts); +void gx_stream_stop(struct gx_stream *ts); + +#endif /* _MESON_GX_INTERFACE_H */ From 9335117a221a7886bb6cac8a15905cf567d3413b Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:26 +0200 Subject: [PATCH 043/791] ASoC: meson: aiu-encoder-i2s: prepare for multiple streams aiu-encoder-i2s is going to be the interface that handles both playback and capture, so this commit does all the required changes to prepare for that since so far it only handled playback: - probe/remove functions are added to allocate/free per stream data, respectively. - 'struc gx_iface' and 'struct gx_stream' are used to store interface or stream associated data, respecively. - interface wide rate symmetry is enforced. - quirks on bclk are also enforced if/when necessary. Clock-wise instead of bulk enabling all the clocks on startup and disabling them on shutdown, only the peripheral's internal ones are enabled/disabled in those functions, whereas MCLK and I2S clock divider are handled in prepare/hw_free. Finally a trigger() callback is also added to start/stop the associated I2S data formatter. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-2-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 207 +++++++++++++++++++++++++++--- sound/soc/meson/aiu.h | 3 + 2 files changed, 193 insertions(+), 17 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index 3b4061508c18..f50b03824ad2 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -10,6 +10,8 @@ #include #include "aiu.h" +#include "gx-formatter.h" +#include "gx-interface.h" #define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) #define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) @@ -112,6 +114,9 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) { + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; + /* * NOTE: this HW is odd. * In most configuration, the i2s divider is 'mclk / blck'. @@ -126,6 +131,18 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, return -EINVAL; } bs += bs / 2; + iface->bs_quirk = true; + } else { + /* + * If the bs quirk is currently applied for one stream and another + * ones tries to setup a configuration for which the quirk is + * not required, then fail. + */ + if (iface->bs_quirk) { + dev_err(component->dev, + "bclk requirements are incompatible with active stream\n"); + return -EINVAL; + } } /* Use CLK_MORE for mclk to bclk divider */ @@ -145,14 +162,15 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, struct snd_pcm_hw_params *params) { struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int srate = params_rate(params); unsigned int fs, bs; int ret; /* Get the oversampling factor */ - fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); + fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); - if (fs % 64) + if ((fs % 64) || (fs == 0)) return -EINVAL; /* Send data MSB first */ @@ -188,24 +206,59 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct gx_iface *iface = ts->iface; struct snd_soc_component *component = dai->component; int ret; - /* Disable the clock while changing the settings */ - aiu_encoder_i2s_divider_enable(component, false); + /* + * Enforce interface wide rate symmetry only if there is more than + * 1 stream active. + */ + if (snd_soc_dai_active(dai) > 1) { + if (iface->rate && iface->rate != params_rate(params)) { + dev_err(dai->dev, "can't set iface rate (%d != %d)\n", + iface->rate, params_rate(params)); + return -EINVAL; + } + } ret = aiu_encoder_i2s_setup_desc(component, params); if (ret) { - dev_err(dai->dev, "setting i2s desc failed\n"); + dev_err(dai->dev, "setting i2s desc failed: %d\n", ret); return ret; } ret = aiu_encoder_i2s_set_clocks(component, params); if (ret) { - dev_err(dai->dev, "setting i2s clocks failed\n"); + dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); return ret; } + iface->rate = params_rate(params); + ts->physical_width = params_physical_width(params); + ts->width = params_width(params); + ts->channels = params_channels(params); + + return 0; +} + +static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + int ret; + + if (ts->clk_enabled) + return 0; + + ret = clk_prepare_enable(ts->iface->mclk); + if (ret) + return ret; + + ts->clk_enabled = true; + aiu_encoder_i2s_divider_enable(component, true); return 0; @@ -214,9 +267,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct gx_iface *iface = ts->iface; struct snd_soc_component *component = dai->component; - aiu_encoder_i2s_divider_enable(component, false); + /* + * If this is the last substream being closed then disable the i2s + * clock divider and clear 'iface->rate'. + */ + if (snd_soc_dai_active(dai) <= 1) { + aiu_encoder_i2s_divider_enable(component, 0); + iface->rate = 0; + iface->bs_quirk = false; + } + + if (ts->clk_enabled) { + clk_disable_unprepare(ts->iface->mclk); + ts->clk_enabled = false; + } return 0; } @@ -224,6 +292,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; unsigned int val = 0; unsigned int skew; @@ -255,9 +325,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) skew = 0; break; default: + dev_err(dai->dev, "unsupported dai format\n"); return -EINVAL; } + iface->fmt = fmt; + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); snd_soc_component_update_bits(component, AIU_CLK_CTRL, AIU_CLK_CTRL_LRCLK_INVERT | @@ -272,6 +345,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + struct gx_iface *iface = &aiu->i2s.iface; int ret; if (WARN_ON(clk_id != 0)) @@ -280,11 +354,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, if (dir == SND_SOC_CLOCK_IN) return 0; - ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); - if (ret) - dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); + ret = clk_set_rate(iface->mclk, freq); + if (ret) { + dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); + return ret; + } - return ret; + iface->mclk_rate = freq; + + return 0; } static const unsigned int hw_channels[] = {2, 8}; @@ -305,15 +383,35 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_channel_constraints); if (ret) { - dev_err(dai->dev, "adding channels constraints failed\n"); + dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); return ret; } - ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); - if (ret) - dev_err(dai->dev, "failed to enable i2s clocks\n"); + /* + * Enable only clocks which are required for the interface internal + * logic. MCLK is enabled/disabled from the formatter and the I2S + * divider is enabled/disabled in "hw_params"/"hw_free", respectively. + */ + ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); + if (ret) { + dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } - return ret; + return 0; } static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, @@ -321,14 +419,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); - clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); + clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); +} + +static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = gx_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + gx_stream_stop(ts); + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) +{ + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + ts = snd_soc_dai_dma_data_get(dai, stream); + if (ts) + gx_stream_free(ts); + + snd_soc_dai_dma_data_set(dai, stream, NULL); + } + + return 0; +} + +static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_dai_get_drvdata(dai); + struct gx_iface *iface = &aiu->i2s.iface; + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + if (!snd_soc_dai_get_widget(dai, stream)) + continue; + + ts = gx_stream_alloc(iface); + if (!ts) { + aiu_encoder_i2s_remove_dai(dai); + return -ENOMEM; + } + snd_soc_dai_dma_data_set(dai, stream, ts); + } + + iface->mclk = aiu->i2s.clks[MCLK].clk; + iface->mclk_rate = clk_get_rate(iface->mclk); + + return 0; } const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { + .probe = aiu_encoder_i2s_probe_dai, + .remove = aiu_encoder_i2s_remove_dai, .hw_params = aiu_encoder_i2s_hw_params, + .prepare = aiu_encoder_i2s_prepare, .hw_free = aiu_encoder_i2s_hw_free, .set_fmt = aiu_encoder_i2s_set_fmt, .set_sysclk = aiu_encoder_i2s_set_sysclk, .startup = aiu_encoder_i2s_startup, .shutdown = aiu_encoder_i2s_shutdown, + .trigger = aiu_encoder_i2s_trigger, }; diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 0f94c8bf6081..68310de0bdf7 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -7,6 +7,8 @@ #ifndef _MESON_AIU_H #define _MESON_AIU_H +#include "gx-formatter.h" + struct clk; struct clk_bulk_data; struct device; @@ -25,6 +27,7 @@ struct aiu_interface { struct clk_bulk_data *clks; unsigned int clk_num; int irq; + struct gx_iface iface; }; struct aiu_platform_data { From 2cbb32d8dce0358a385760349fe19758a0d1a49e Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:27 +0200 Subject: [PATCH 044/791] ASoC: meson: aiu: introduce I2S output formatter Introduce aiu-formatter-i2s, a gx_formatter implementation for the AIU I2S playback path. This is going to replace data formatting tasks that are currently being implemented in aiu-encoder-i2s. This should ideally follow the same design pattern used on the AXG platform (see axg-tdmout), where basically the widget/formatter corresponds to a single audio component. This is not possible in the GX platform though because all the features are currently implemented in the AIU audio component and changing that would require backward incompatible device-tree changes. Therefore aiu-formatter-i2s is kept very simple and it only implements the bare minimum functionalities to provide I2S playback formatting. It's not a standalone component though because this is still belongs to AIU. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-3-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Makefile | 1 + sound/soc/meson/aiu-formatter-i2s.c | 104 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 sound/soc/meson/aiu-formatter-i2s.c diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 146ec81526ba..f9ec0ebb01f0 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -5,6 +5,7 @@ snd-soc-meson-aiu-y += aiu-acodec-ctrl.o snd-soc-meson-aiu-y += aiu-codec-ctrl.o snd-soc-meson-aiu-y += aiu-encoder-i2s.o snd-soc-meson-aiu-y += gx-formatter.o +snd-soc-meson-aiu-y += aiu-formatter-i2s.o snd-soc-meson-aiu-y += aiu-encoder-spdif.o snd-soc-meson-aiu-y += aiu-fifo.o snd-soc-meson-aiu-y += aiu-fifo-i2s.o diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c new file mode 100644 index 000000000000..b4604734fe88 --- /dev/null +++ b/sound/soc/meson/aiu-formatter-i2s.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti + +#include +#include +#include + +#include "aiu.h" +#include "gx-formatter.h" + +#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) +#define AIU_RST_SOFT_I2S_FAST BIT(0) + +#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) + +static struct snd_soc_dai * +aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = aiu_formatter_i2s_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct gx_stream * +aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); + + if (!be) + return NULL; + + return snd_soc_dai_dma_data_get_playback(be); +} + +static int aiu_formatter_i2s_prepare(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts) +{ + /* Always operate in split (classic interleaved) mode */ + unsigned int desc = 0; + unsigned int tmp; + + /* Reset required to update the pipeline */ + regmap_write(map, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); + regmap_read(map, AIU_I2S_SYNC, &tmp); + + switch (ts->physical_width) { + case 16: /* Nothing to do */ + break; + + case 32: + desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT); + break; + + default: + return -EINVAL; + } + + switch (ts->channels) { + case 2: /* Nothing to do */ + break; + case 8: + desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; + break; + default: + return -EINVAL; + } + + regmap_update_bits(map, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_8CH | + AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT, + desc); + + /* Send data MSB first */ + regmap_update_bits(map, AIU_I2S_DAC_CFG, + AIU_I2S_DAC_CFG_MSB_FIRST, + AIU_I2S_DAC_CFG_MSB_FIRST); + + return 0; +} + +const struct gx_formatter_ops aiu_formatter_i2s_ops = { + .get_stream = aiu_formatter_i2s_get_stream, + .prepare = aiu_formatter_i2s_prepare, +}; From 83b83024cdbfdddee104f4a84c2c2f1f8e6659f3 Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:28 +0200 Subject: [PATCH 045/791] ASoC: meson: aiu: use aiu-formatter-i2s to format I2S output data Create a new DAPM widget for "I2S formatter" and place it on the path between FIFO and output DAI interface. Remove I2S output formatting code from aiu-encoder-i2s since it's now implemented from aiu-formatter-i2s. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-4-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 78 ++++++++----------------------- sound/soc/meson/aiu.c | 32 +++++++++++-- sound/soc/meson/aiu.h | 1 + 3 files changed, 48 insertions(+), 63 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index f50b03824ad2..83b579e98f1c 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -13,13 +13,8 @@ #include "gx-formatter.h" #include "gx-interface.h" -#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) -#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) -#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) -#define AIU_RST_SOFT_I2S_FAST BIT(0) -#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) @@ -37,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); } -static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, - struct snd_pcm_hw_params *params) -{ - /* Always operate in split (classic interleaved) mode */ - unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; - - /* Reset required to update the pipeline */ - snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); - snd_soc_component_read(component, AIU_I2S_SYNC); - - switch (params_physical_width(params)) { - case 16: /* Nothing to do */ - break; - - case 32: - desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT); - break; - - default: - return -EINVAL; - } - - switch (params_channels(params)) { - case 2: /* Nothing to do */ - break; - case 8: - desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; - break; - default: - return -EINVAL; - } - - snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, - AIU_I2S_SOURCE_DESC_MODE_8CH | - AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT | - AIU_I2S_SOURCE_DESC_MODE_SPLIT, - desc); - - return 0; -} - static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) @@ -173,11 +125,6 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, if ((fs % 64) || (fs == 0)) return -EINVAL; - /* Send data MSB first */ - snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, - AIU_I2S_DAC_CFG_MSB_FIRST, - AIU_I2S_DAC_CFG_MSB_FIRST); - /* Set bclk to lrlck ratio */ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, AIU_CODEC_DAC_LRCLK_CTRL_DIV, @@ -223,12 +170,6 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, } } - ret = aiu_encoder_i2s_setup_desc(component, params); - if (ret) { - dev_err(dai->dev, "setting i2s desc failed: %d\n", ret); - return ret; - } - ret = aiu_encoder_i2s_set_clocks(component, params); if (ret) { dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); @@ -411,6 +352,25 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, return ret; } + /* + * We're always operating in split mode for the playback stream. + * + * This setting arguably belong to the 'aiu-formatter', but it's kept + * here for backward compatibility reason. At reset the I2S encoder + * operates in normal mode which would only support 8ch, but by default + * only 2ch are enabled. If a playback stream is started without + * changing to split mode, then the I2S encoder doesn't consume audio + * samples and the playback fails. + * Moving this to 'aiu-formatter' would cause the split mode to be set + * only when the formatter is enabled, which doesn't happen at boot as + * the default value for "HDMI CTRL SRC" is "DISABLED". + */ + ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_SPLIT, + AIU_I2S_SOURCE_DESC_MODE_SPLIT); + if (ret < 0) + dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); + return 0; } diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index f2890111c1d2..64ace4d25d92 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, static const struct snd_kcontrol_new aiu_spdif_encode_mux = SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); -static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { - SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, - &aiu_spdif_encode_mux), +#define AIU_WIDGET_SPDIF_SRC_SEL 0 +#define AIU_WIDGET_I2S_FORMATTER 1 + +static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { + [AIU_WIDGET_SPDIF_SRC_SEL] = + SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, + &aiu_spdif_encode_mux), + [AIU_WIDGET_I2S_FORMATTER] = + SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, + gx_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), }; static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { - { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, + { "I2S Formatter", NULL, "I2S FIFO Playback" }, + { "I2S Encoder Playback", NULL, "I2S Formatter" }, { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, @@ -172,6 +181,11 @@ static const struct regmap_config aiu_regmap_cfg = { .max_register = 0x2ac, }; +const struct gx_formatter_driver aiu_formatter_i2s_drv = { + .regmap_cfg = &aiu_regmap_cfg, + .ops = &aiu_formatter_i2s_ops, +}; + static int aiu_clk_bulk_get(struct device *dev, const char * const *ids, unsigned int num, @@ -282,6 +296,14 @@ static int aiu_probe(struct platform_device *pdev) if (ret) return ret; + /* Allocate the aiu-formatter into its widget */ + ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], + &aiu_formatter_i2s_drv, map); + if (ret) { + dev_err(dev, "Failed to allocate aiu formatter\n"); + goto err; + } + /* Register the cpu component of the aiu */ ret = snd_soc_register_component(dev, &aiu_cpu_component, aiu_cpu_dai_drv, @@ -310,12 +332,14 @@ static int aiu_probe(struct platform_device *pdev) return 0; err: + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(dev); return ret; } static void aiu_remove(struct platform_device *pdev) { + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(&pdev->dev); } diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 68310de0bdf7..7d0b98c1f351 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -61,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; +extern const struct gx_formatter_ops aiu_formatter_i2s_ops; #define AIU_IEC958_BPF 0x000 #define AIU_958_MISC 0x010 From b2133ca33e66dafe948c5a219e7f40b2ccd07881 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 01:08:07 +0000 Subject: [PATCH 046/791] ASoC: au1x: psc-ac97: remove unused chans chans is not used in au1xpsc_ac97_hw_params(). Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0g2ds7c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/au1x/psc-ac97.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 94698e08a513..0f9d80883116 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, { struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); unsigned long r, ro, stat; - int chans, t, stype = substream->stream; - - chans = params_channels(params); + int t, stype = substream->stream; r = ro = __raw_readl(AC97_CFG(pscdata)); stat = __raw_readl(AC97_STAT(pscdata)); From c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:10 +0000 Subject: [PATCH 047/791] ASoC: sdw_utils: tidyup .count_sidecar count_sidecar() is not using *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87jyrlety1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 28 ++++++++++---------- sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 4 +-- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 79c21966220b..443d63dc6ea3 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -44,6 +44,18 @@ struct asoc_sdw_codec_info; +struct asoc_sdw_mc_private { + struct snd_soc_card card; + struct snd_soc_jack sdw_headset; + struct device *headset_codec_dev; /* only one headset per card */ + struct device *amp_dev1, *amp_dev2; + bool append_dai_type; + bool ignore_internal_dmic; + void *private; + unsigned long mc_quirk; + int codec_info_list_count; +}; + struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *codec_name; @@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { int (*codec_card_late_probe)(struct snd_soc_card *card); - int (*count_sidecar)(struct snd_soc_card *card, + int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int (*add_sidecar)(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { - struct snd_soc_card card; - struct snd_soc_jack sdw_headset; - struct device *headset_codec_dev; /* only one headset per card */ - struct device *amp_dev1, *amp_dev2; - bool append_dai_type; - bool ignore_internal_dmic; - void *private; - unsigned long mc_quirk; - int codec_info_list_count; -}; - struct asoc_sdw_endpoint { struct list_head list; @@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index e0e32a279787..129a437ae397 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs) { - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { (*num_dais)++; (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..073f3f9205a7 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar(card, &num_dais, num_devs); + ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); if (ret) return ret; From a1332be2a07090cf422507ec812ce2b9ba0a558a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:15 +0000 Subject: [PATCH 048/791] ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints() We can avoid to use *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87ik75etxw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 3 ++- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 443d63dc6ea3..9b28e9aef4f1 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new); int asoc_sdw_get_dai_type(u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..9726a9d33ec6 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..963ce6fd4012 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d43daf9b025d..24226a387cc2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1285,7 +1285,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) goto err_end; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 073f3f9205a7..dd2cc57059d6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1976,14 +1976,13 @@ static int is_sdca_endpoint_present(struct device *dev, return ret; } -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs) { - struct device *dev = card->dev; - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; From 2f82d58a87d707c54ba649a4f71b9e7bc9c56f47 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Tue, 30 Jun 2026 09:52:49 +0100 Subject: [PATCH 049/791] ASoC: codecs: cleanup kconfig indentations Cleanup various bad indentations in the kconfig: 1. spaces instead of tabs (this file mostly uses tabs) 2. too much indentation 3. not enough indentation Signed-off-by: Julian Braha Link: https://patch.msgid.link/20260630085249.380365-1-julianbraha@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e9de333c5c8a..f90a0d4c77ea 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -615,8 +615,8 @@ config SND_SOC_AK4613 depends on I2C config SND_SOC_AK4619 - tristate "AKM AK4619 CODEC" - depends on I2C + tristate "AKM AK4619 CODEC" + depends on I2C config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" @@ -1201,16 +1201,16 @@ config SND_SOC_JZ4725B_CODEC will be called snd-soc-jz4725b-codec. config SND_SOC_JZ4760_CODEC - depends on MACH_INGENIC || COMPILE_TEST - depends on OF - select REGMAP - tristate "Ingenic JZ4760 internal CODEC" - help - Enable support for the internal CODEC found in the JZ4760 SoC - from Ingenic. + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4760 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4760 SoC + from Ingenic. - This driver can also be built as a module. If so, the module - will be called snd-soc-jz4760-codec. + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4760-codec. config SND_SOC_JZ4770_CODEC depends on MACH_INGENIC || COMPILE_TEST @@ -2296,8 +2296,8 @@ config SND_SOC_TLV320ADC3XXX depends on I2C depends on GPIOLIB help - Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 - ADCs. + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. config SND_SOC_TLV320AIC23 tristate @@ -2893,7 +2893,7 @@ config SND_SOC_TPA6130A2 depends on I2C config SND_SOC_LPASS_MACRO_COMMON - tristate + tristate config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK From 3848617c64ac5bf71e02f437e1974720d78843ca Mon Sep 17 00:00:00 2001 From: Narasimharao Vadlamudi Date: Tue, 30 Jun 2026 22:43:33 +0530 Subject: [PATCH 050/791] ASoC: renesas: fsi: Propagate platform_get_irq() errors platform_get_irq() returns a positive IRQ number on success and a negative error code on failure. It no longer returns zero. The driver currently stores the return value in an unsigned int and returns -ENODEV for all failures, which loses useful errors such as -EPROBE_DEFER. Store the IRQ in an int and return the error from platform_get_irq() directly. Acked-by: Kuninori Morimoto Reviewed-by: Geert Uytterhoeven Signed-off-by: Narasimharao Vadlamudi Link: https://patch.msgid.link/20260630171333.36396-1-ahmisaranrao@gmail.com Signed-off-by: Mark Brown --- sound/soc/renesas/fsi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index ae86014c3819..6be6587e1095 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; - unsigned int irq; + int irq; int ret; memset(&info, 0, sizeof(info)); @@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { + if (!res) { dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); return -ENODEV; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); if (!master) return -ENOMEM; From ae2e2f1ff1e80f88e5720a3c642992d182adb025 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 29 Jun 2026 18:30:24 -0700 Subject: [PATCH 051/791] ASoC: mediatek: mt2701: add COMPILE_TEST Enable COMPILE_TEST for mt2701 to get extra build coverage as done with other mediatek platforms. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260630013024.1500623-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 4af7bbb58010..224746e7664f 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK config SND_SOC_MT2701 tristate "ASoC support for Mediatek MT2701 chip" - depends on ARCH_MEDIATEK + depends on ARCH_MEDIATEK || COMPILE_TEST select SND_SOC_MEDIATEK help This adds ASoC driver for Mediatek MT2701 boards From bff7fad1010eea6f183fb110b54171cf8700ef8e Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 24 Jun 2026 23:10:02 +0200 Subject: [PATCH 052/791] ASoC: dt-bindings: Convert cirrus,cs35l36 to DT schema Convert CS35L36 Speaker Amplifier to yaml. Changes: - maintainers email to the generic Cirrus email - Both the codec and downstream worked just fine without VP-supply provided. Align with datasheet for similar models. - add dai-common.yaml to cover for '#sound-dai-cells', 'sound-name-prefix' - updated not yet implemented: cirrus,weak-fet-delay -> cirrus,classh-wk-fet-delay-ms (in both definition and example) cirrus,weak-fet-thld -> cirrus,weak-fet-thld-millivolt (only in the example) - added two required properties: cirrus,boost-ctl-millivolt cirrus,boost-peak-milliamp Assisted-by: OpenAI:gpt-4 Reviewed-by: David Rhodes Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: David Heidelberg Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260624-dt-cirrus-cs35l36-v3-1-ec451d5a2908@ixit.cz Signed-off-by: Mark Brown --- .../bindings/sound/cirrus,cs35l36.yaml | 240 ++++++++++++++++++ .../devicetree/bindings/sound/cs35l36.txt | 168 ------------ 2 files changed, 240 insertions(+), 168 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml delete mode 100644 Documentation/devicetree/bindings/sound/cs35l36.txt diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml new file mode 100644 index 000000000000..2a142b32acf5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml @@ -0,0 +1,240 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs35l36.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS35L36 Speaker Amplifier + +maintainers: + - David Rhodes + - patches@opensource.cirrus.com + +description: + CS35L36 is a boosted mono Class D amplifier + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs35l36 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + VA-supply: + description: Voltage regulator of analog internal section + + VP-supply: + description: Voltage regulator of boost converter + + reset-gpios: + maxItems: 1 + + cirrus,boost-ctl-millivolt: + description: Boost converter output voltage (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2550 + maximum: 12000 + + cirrus,boost-peak-milliamp: + description: Boost-converter peak current limit (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 4500 + minimum: 1600 + maximum: 4500 + + cirrus,boost-ind-nanohenry: + description: Initial inductor estimation reference value (1000=1μH, 1200=1.2μH) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1000 + + cirrus,multi-amp-mode: + description: Hi-Z ASP port when more than one amplifier in system + type: boolean + + cirrus,boost-ctl-select: + description: Boost converter control source selection + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # Control Port + - 1 # Class + - 2 # Sync + + cirrus,amp-pcm-inv: + description: Invert incoming PCM data when true + type: boolean + + cirrus,imon-pol-inv: + description: Invert polarity of outbound IMON feedback when true + type: boolean + + cirrus,vmon-pol-inv: + description: Invert polarity of outbound VMON feedback when true + type: boolean + + cirrus,dcm-mode-enable: + description: Enable boost converter automatic Discontinuous Conduction Mode + type: boolean + + cirrus,weak-fet-disable: + description: Reduce output driver strength in Weak-FET Drive Mode when true + type: boolean + + cirrus,classh-wk-fet-delay-ms: + description: Weak-FET entry delay + default: 100 + enum: [0, 5, 10, 50, 100, 200, 500, 1000] + + cirrus,classh-weak-fet-thld-millivolt: + description: Weak-FET drive threshold + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700] + + cirrus,temp-warn-threshold: + description: Overtemperature warning threshold + $ref: /schemas/types.yaml#/definitions/uint32 + default: 2 + enum: + - 0 # 105°C + - 1 # 115°C + - 2 # 125°C + - 3 # 135°C + + cirrus,irq-drive-select: + description: Interrupt output driver type + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # open-drain + - 1 # push-pull + + cirrus,irq-gpio-select: + description: Programmable IRQ pin selection + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # PDM_DATA/SWIRE_SD/INT + - 1 # GPIO + + cirrus,vpbr-config: + $ref: "#/$defs/vpbr-config" + +$defs: + vpbr-config: + description: Brownout prevention configuration sub-node + type: object + additionalProperties: false + + properties: + cirrus,vpbr-en: + description: VBST brownout prevention enable + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + enum: + - 0 # disabled + - 1 # enabled + + cirrus,vpbr-thld: + description: Initial VPBR threshold voltage + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 31 + + cirrus,vpbr-atk-rate: + description: Attenuation attack step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-atk-vol: + description: VP brownout prevention step size + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-max-attn: + description: Maximum attenuation during VP brownout prevention (dB) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + + cirrus,vpbr-wait: + description: Delay between brownout clearance and attenuation release (ms) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # 10 + - 1 # 100 + - 2 # 250 + - 3 # 500 + + cirrus,vpbr-rel-rate: + description: Attenuation release step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-mute-en: + description: Mute audio if maximum attenuation reached + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 1 + +required: + - compatible + - reg + - interrupts + - VA-supply + - cirrus,boost-ctl-millivolt + - cirrus,boost-peak-milliamp + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@40 { + compatible = "cirrus,cs35l36"; + reg = <0x40>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-ctl-millivolt = <10000>; + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ctl-select = <0>; + cirrus,classh-wk-fet-delay-ms = <100>; + cirrus,classh-weak-fet-thld-millivolt = <100>; + cirrus,temp-warn-threshold = <1>; + cirrus,multi-amp-mode; + cirrus,irq-drive-select = <1>; + cirrus,irq-gpio-select = <1>; + + cirrus,vpbr-config { + cirrus,vpbr-en = <0>; + cirrus,vpbr-thld = <5>; + cirrus,vpbr-atk-rate = <2>; + cirrus,vpbr-atk-vol = <1>; + cirrus,vpbr-max-attn = <9>; + cirrus,vpbr-wait = <1>; + cirrus,vpbr-rel-rate = <5>; + cirrus,vpbr-mute-en = <0>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt deleted file mode 100644 index d34117b8558e..000000000000 --- a/Documentation/devicetree/bindings/sound/cs35l36.txt +++ /dev/null @@ -1,168 +0,0 @@ -CS35L36 Speaker Amplifier - -Required properties: - - - compatible : "cirrus,cs35l36" - - - reg : the I2C address of the device for I2C - - - VA-supply, VP-supply : power supplies for the device, - as covered in - Documentation/devicetree/bindings/regulator/regulator.txt. - - - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost - converter's output voltage in mV. The range is from 2550mV to 12000mV with - increments of 50mV. - (Default) VP - - - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. - Configures the peak current by monitoring the current through the boost FET. - Range starts at 1600mA and goes to a maximum of 4500mA with increments of - 50mA. - (Default) 4.50 Amps - - - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value. - Seeds the digital boost converter's inductor estimation block with the initial - inductance value to reference. - - 1000 = 1uH (Default) - 1200 = 1.2uH - -Optional properties: - - cirrus,multi-amp-mode : Boolean to determine if there are more than - one amplifier in the system. If more than one it is best to Hi-Z the ASP - port to prevent bus contention on the output signal - - - cirrus,boost-ctl-select : Boost converter control source selection. - Selects the source of the BST_CTL target VBST voltage for the boost - converter to generate. - 0x00 - Control Port Value - 0x01 - Class H Tracking (Default) - 0x10 - MultiDevice Sync Value - - - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming - PCM data - - - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound IMON feedback data - - - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound VMON feedback data - - - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable. - This enables the digital boost converter to operate in a low power - (Discontinuous Conduction) mode during low loading conditions. - - - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is - reduced when operating in a Weak-FET Drive Mode and must not be used to drive - a large load. - - - cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay - (in ms) before the Class H algorithm switches to the weak-FET voltage - (after the audio falls and remains below the value specified in WKFET_AMP_THLD). - - 0 = 0ms - 1 = 5ms - 2 = 10ms - 3 = 50ms - 4 = 100ms (Default) - 5 = 200ms - 6 = 500ms - 7 = 1000ms - - - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold. - Configures the signal threshold at which the PWM output stage enters - weak-FET operation. The range is 50mV to 700mV in 50mV increments. - - - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold. - Configures the threshold at which the overtemperature warning condition occurs. - When the threshold is met, the overtemperature warning attenuation is applied - and the TEMP_WARN_EINT interrupt status bit is set. - If TEMP_WARN_MASK = 0, INTb is asserted. - - 0 = 105C - 1 = 115C - 2 = 125C (Default) - 3 = 135C - - - cirrus,irq-drive-select : Selects the driver type of the selected interrupt - output. - - 0 = Open-drain - 1 = Push-pull (Default) - - - cirrus,irq-gpio-select : Selects the pin to serve as the programmable - interrupt output. - - 0 = PDM_DATA / SWIRE_SD / INT (Default) - 1 = GPIO - -Optional properties for the "cirrus,vpbr-config" Sub-node - - - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the - VBST brownout prevention algorithm is enabled or disabled. - - 0 = VBST brownout prevention disabled (default) - 1 = VBST brownout prevention enabled - - See Section 7.31.1 VPBR Config for configuration options & further details - - - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout - threshold voltage - - - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the - amount delay between consecutive volume attenuation steps when a brownout - condition is present and the VP brownout condition is in an attacking state. - - - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP - brownout prevention attacking attenuation step size when operating in either - digital volume or analog gain modes. - - - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention - can apply to the audio signal. - - - cirrus,vpbr-wait : Configures the delay time between a brownout condition - no longer being present and the VP brownout prevention entering an attenuation - release state. - - - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay - between consecutive volume attenuation release steps when a brownout condition - is not longer present and the VP brownout is in an attenuation release state. - - - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value - is reached, the error condition still remains, and this bit is set, the audio - is muted. - -Example: - -cs35l36: cs35l36@40 { - compatible = "cirrus,cs35l36"; - reg = <0x40>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - reset-gpios = <&gpio0 54 0>; - interrupt-parent = <&gpio8>; - interrupts = <3 IRQ_TYPE_LEVEL_LOW>; - - cirrus,boost-ind-nanohenry = <1000>; - cirrus,boost-ctl-millivolt = <10000>; - cirrus,boost-peak-milliamp = <4500>; - cirrus,boost-ctl-select = <0x00>; - cirrus,weak-fet-delay = <0x04>; - cirrus,weak-fet-thld = <0x01>; - cirrus,temp-warn-threshold = <0x01>; - cirrus,multi-amp-mode; - cirrus,irq-drive-select = <0x01>; - cirrus,irq-gpio-select = <0x01>; - - cirrus,vpbr-config { - cirrus,vpbr-en = <0x00>; - cirrus,vpbr-thld = <0x05>; - cirrus,vpbr-atk-rate = <0x02>; - cirrus,vpbr-atk-vol = <0x01>; - cirrus,vpbr-max-attn = <0x09>; - cirrus,vpbr-wait = <0x01>; - cirrus,vpbr-rel-rate = <0x05>; - cirrus,vpbr-mute-en = <0x00>; - }; -}; From 7ffb66fd96ec73dd5413d27624476588beb57c30 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 1 Jul 2026 06:17:57 +0000 Subject: [PATCH 053/791] ASoC: mediatek: mt8173-rt5650: tidyup error message mt8173_rt5650_dev_probe() has strange error message => ret = device_property_read_u32(...); ^^^^^^^^^^^^^^^^^^^^^^^^ if (ret) => dev_err(... "%s snd_soc_register_card fail %d\n", ...); ^^^^^^^^^^^^^^^^^^^^^ It should be "device_property_read_u32() fail". Fix it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/875x2zcjxn.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-rt5650.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 3d6d7bc05b87..8c5096482520 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) &mt8173_rt5650_priv.pll_from); if (ret) { dev_err(&pdev->dev, - "%s snd_soc_register_card fail %d\n", + "%s device_property_read_u32() fail %d\n", __func__, ret); } } From 882e55031187791a2b87810d4a93f866a2da6d47 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:06 +0800 Subject: [PATCH 054/791] ASoC: codecs: ES8389: Modify volatile_register Mark some registers that are not volatile as false And modified the logic for `cache_bypass` during `8389_resume`. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 3484c87853cb..10de2143f8d8 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -50,10 +50,29 @@ static const char * const es8389_core_supplies[] = { static bool es8389_volatile_register(struct device *dev, unsigned int reg) { - if ((reg <= 0xff)) - return true; - else + switch (reg) { + case ES8389_ADCL_VOL: + case ES8389_ADCR_VOL: + case ES8389_MIC1_GAIN: + case ES8389_MIC2_GAIN: + case ES8389_DACL_VOL: + case ES8389_DACR_VOL: + case ES8389_ALC_ON: + case ES8389_ALC_CTL: + case ES8389_ALC_TARGET: + case ES8389_ALC_GAIN: + case ES8389_ADC_MUTE: + case ES8389_OSR_VOL: + case ES8389_DAC_INV: + case ES8389_MIX_VOL: + case ES8389_DAC_MIX: + case ES8389_ADC_RESET: + case ES8389_ADC_MODE: + case ES8389_DMIC_EN: return false; + default: + return true; + } } static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); @@ -861,13 +880,13 @@ static int es8389_resume(struct snd_soc_component *component) regcache_cache_only(es8389->regmap, false); regcache_cache_bypass(es8389->regmap, true); regmap_read(es8389->regmap, ES8389_RESET, ®v); - regcache_cache_bypass(es8389->regmap, false); if (regv == 0xff) es8389_init(component); else es8389_set_bias_level(component, SND_SOC_BIAS_ON); + regcache_cache_bypass(es8389->regmap, false); regcache_sync(es8389->regmap); return 0; From cbc559dd8d46acd0781a4f183d8ec550262714ab Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:07 +0800 Subject: [PATCH 055/791] ASoC: codecs: ES8389: Fix the issue about mclk_src Fix the issue with incorrect modifications to mclk_src When the system needs to be configured to use the MCLK from the SCLK pin, the code still sets the relevant registers to use the MCLK from the MCLK pin Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 2 +- sound/soc/codecs/es8389.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 10de2143f8d8..890c6b4c15e2 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -609,7 +609,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, if (es8389->mclk_src == ES8389_SCLK_PIN) { regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, - ES8389_MCLK_SOURCE, es8389->mclk_src); + ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); } diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index d21e72f876a6..57bf7c5f8b57 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -116,9 +116,11 @@ #define ES8389_TDM_SLOT (0x70 << 0) #define ES8389_TDM_SHIFT 4 -#define ES8389_MCLK_SOURCE (1 << 6) -#define ES8389_MCLK_PIN (1 << 6) -#define ES8389_SCLK_PIN (0 << 6) +#define ES8389_MCLK_MASK (3 << 6) +#define ES8389_MCLK_FROM_SCLK (1 << 6) +#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN +#define ES8389_MCLK_PIN 0 +#define ES8389_SCLK_PIN 1 /* ES8389_FMT */ #define ES8389_S24_LE (0 << 5) From 9367a244afc24e3863689bef126efa2ad01105db Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:08 +0800 Subject: [PATCH 056/791] ASoC: codecs: ES8389: Modify the clock table Updated the configuration for certain frequencies Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 890c6b4c15e2..9d263927d7b7 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -434,52 +434,54 @@ static const struct _coeff_div coeff_div[] = { {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, - {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, - {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, + {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, - {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, + {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, }; static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) From 3bea836903c2b2305c2219e897b217261be8a26a Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:09 +0800 Subject: [PATCH 057/791] ASoC: codecs: ES8389: Modify the initial configuration Modify the initial configuration Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-5-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 9d263927d7b7..26a063186dc5 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -792,7 +792,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); es8389->version = reg; - regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); regmap_write(es8389->regmap, ES8389_RESET, 0x7E); regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); @@ -844,7 +844,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); - regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); + regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); From 87592da1a490abd2adeb57a49fb200d058403cc5 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:10 +0800 Subject: [PATCH 058/791] ASoC: codecs: ES8389: Add private members about HPF Add private members related to HPF. Add Kcontrol for HPF Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-6-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 122 +++++++++++++++++++++++++++++++++++++- sound/soc/codecs/es8389.h | 3 + 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 26a063186dc5..2f9a98a513a1 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -36,6 +36,10 @@ struct es8389_private { unsigned int sysclk; int mastermode; + u8 hpfl; + u8 hpfr; + u32 hpf_freq; + u32 capture_rate; u8 mclk_src; u8 vddd; int version; @@ -82,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); +static const u32 hpf_table[10][10] = { + {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, + {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, + {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, + {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, + {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, + {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, + {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, + {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, + {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, + {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} +}; + +static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) +{ + int best_row = -1, best_col = -1; + u32 min_diff = U32_MAX; + u32 f, diff; + int i, j; + + if ((target_hz > 1020) | (target_hz < 0)) + return false; + + for (i = 0; i < 10; i++) { + for (j = i; j < 10; j++) { + f = hpf_table[i][j]; + + diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); + if (diff < min_diff) { + min_diff = diff; + best_row = i; + best_col = j; + *out = f; + } + } + } + + *hpf1 = best_col + ES8389_HPF_OFFSET; + *hpf2 = best_row + ES8389_HPF_OFFSET; + + return true; +} + +static int es8389_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8389->hpf_freq; + return 0; +} + +static int es8389_hpf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + u32 freq; + bool hpf; + + if (es8389->hpf_freq == ucontrol->value.integer.value[0]) + return 0; + + if (es8389->capture_rate) { + freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; + + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) + return -EBUSY; + + if (es8389->hpf_freq != ucontrol->value.integer.value[0]) + dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." + "the frequency has been set to the closest value, %u Hz\n", + es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); + + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); + } else { + es8389->hpf_freq = ucontrol->value.integer.value[0]; + dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); + } + + return 1; +} + static int es8389_dmic_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -259,6 +349,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), + SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, + es8389_hpf_get, es8389_hpf_set), SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), @@ -585,6 +677,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, int coeff, ret; u8 dmic_enable, state = 0; unsigned int regv; + u32 freq; + bool hpf; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -662,6 +756,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, dev_warn(component->dev, "Clock coefficients do not match"); } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + es8389->capture_rate = params_rate(params); + freq = (es8389->hpf_freq * 48000) / params_rate(params); + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) { + dev_err(component->dev, "The HPF frequency is invalid\n"); + return -EINVAL; + } + } + + return 0; +} + +static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + es8389->capture_rate = 0; + return 0; } @@ -742,8 +858,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x00); } else { - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x00); } @@ -759,6 +875,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) static const struct snd_soc_dai_ops es8389_ops = { .hw_params = es8389_pcm_hw_params, + .hw_free = es8389_pcm_hw_free, .set_fmt = es8389_set_dai_fmt, .set_sysclk = es8389_set_dai_sysclk, .set_tdm_slot = es8389_set_tdm_slot, @@ -934,6 +1051,7 @@ static int es8389_probe(struct snd_soc_component *component) return ret; } + es8389->hpf_freq = ES8389_HPF_DEFAULT; es8389_init(component); es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index 57bf7c5f8b57..de39e9bf96e9 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -106,6 +106,9 @@ #define ES8389_MIC_SEL_MASK (7 << 4) #define ES8389_MIC_DEFAULT (1 << 4) +#define ES8389_HPF_DEFAULT 16 +#define ES8389_HPF_OFFSET 4 + #define ES8389_MASTER_MODE_EN (1 << 0) #define ES8389_TDM_OFF (0 << 0) From 2264927316e5312208659fe0b7efd0001c8975d9 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:11 +0800 Subject: [PATCH 059/791] ASoC: codecs: ES8389: Add INPUTL MUX and INPUTR MUX Add INPUTL MUX and INPUTR MUX in route Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-7-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 2f9a98a513a1..0c7567e2ffc2 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -252,6 +252,16 @@ static const struct soc_enum alc_ramprate = static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); +static const char *const es8389_adcl_mux_txt[] = { + "Normal", + "ADC2 channel to ADC1 channel", +}; + +static const char *const es8389_adcr_mux_txt[] = { + "Normal", + "ADC1 channel to ADC2 channel", +}; + static const char *const es8389_outl_mux_txt[] = { "Normal", "DAC2 channel to DAC1 channel", @@ -279,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { 1, 5, 6 }; +static const struct soc_enum es8389_adcl_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, + ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); + +static const struct snd_kcontrol_new es8389_adcl_mux_controls = + SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); + +static const struct soc_enum es8389_adcr_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, + ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); + +static const struct snd_kcontrol_new es8389_adcr_mux_controls = + SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); + static const struct soc_enum es8389_outl_mux_enum = SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); @@ -409,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { &es8389_adc_mixer_controls[0], ARRAY_SIZE(es8389_adc_mixer_controls)), SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), + SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), + SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), @@ -422,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { {"ADCL", NULL, "PGAL"}, {"ADCR", NULL, "PGAR"}, + {"INPUTL MUX", "Normal", "ADCL"}, + {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, + {"INPUTR MUX", "Normal", "ADCR"}, + {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, - {"ADC Mixer", NULL, "ADCL"}, - {"ADC Mixer", NULL, "ADCR"}, + {"ADC Mixer", NULL, "INPUTL MUX"}, + {"ADC Mixer", NULL, "INPUTR MUX"}, {"ADC MUX", "AMIC", "ADC Mixer"}, {"ADC MUX", "DMIC", "DMIC"}, From e3653722b94df580ab8610f4f38ab43e46f083c0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 06:04:20 +0000 Subject: [PATCH 060/791] ASoC: sof: topology: use for_each_card_rtds() We already have for_each_card_rtds(). Let's use it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5mpet2c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f709935593ef..6de8a6c1c127 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1102,7 +1102,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, full = NULL; partial = NULL; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (rtd->dai_link->stream_name) { if (!strcmp(rtd->dai_link->stream_name, w->sname)) { @@ -1167,7 +1167,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, else return; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || !strstr(rtd->dai_link->stream_name, sname)) From 02fd694e60a7e2c581c7836f6781c01b9b419c8a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 11:13:09 +0700 Subject: [PATCH 061/791] ASoC: samsung: i2s: Avoid mixing goto with guard() cleanup.h recommends not mixing goto-based error handling with cleanup helpers in the same function. Remove the goto path and rely on guard(pm_runtime) for automatic cleanup instead. Fixes: 3d08517b5c67 ("ASoC: samsung: i2s: Use guard() for spin locks") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701041310.230725-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f80f697a5d55..f80e8d498156 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -512,7 +513,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, u32 mod, mask, val = 0; int ret = 0; - pm_runtime_get_sync(dai->dev); + guard(pm_runtime_active)(dai->dev); scoped_guard(spinlock_irqsave, &priv->lock) mod = readl(priv->addr + I2SMOD); @@ -537,8 +538,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } if (dir == SND_SOC_CLOCK_IN) @@ -566,7 +566,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, } else { priv->rclk_srcrate = clk_get_rate(priv->op_clk); - goto done; + return 0; } } @@ -580,14 +580,14 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, if (WARN_ON(IS_ERR(priv->op_clk))) { ret = PTR_ERR(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } ret = clk_prepare_enable(priv->op_clk); if (ret) { clk_put(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } priv->rclk_srcrate = clk_get_rate(priv->op_clk); @@ -595,11 +595,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } else { /* Call can't be on the active DAI */ - goto done; + return 0; } if (clk_id == 1) @@ -607,8 +606,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } scoped_guard(spinlock_irqsave, &priv->lock) { @@ -616,13 +614,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, mod = (mod & ~mask) | val; writel(mod, priv->addr + I2SMOD); } -done: - pm_runtime_put(dai->dev); return 0; -err: - pm_runtime_put(dai->dev); - return ret; } static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) From 47a0dde9a3bdd01359ce3a5f0b59a9de33b73dce Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 11:13:10 +0700 Subject: [PATCH 062/791] ASoC: ti: j721e-evm: Avoid mixing goto with guard() The previous guard(mutex) conversion mixed cleanup helpers with goto-based error handling, which is discouraged by the cleanup.h guidelines. Restore mutex_lock()/mutex_unlock() instead. Fixes: 6f4cf77320ae ("ASoC: ti: j721e-evm: Use guard() for mutex locks") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701041310.230725-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index c214ae0d7b95..312298e0b004 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -4,6 +4,7 @@ * Author: Peter Ujfalusi */ +#include #include #include #include @@ -263,7 +264,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) int ret = 0; int i; - guard(mutex)(&priv->mutex); + mutex_lock(&priv->mutex); domain->active++; @@ -303,6 +304,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) out: if (ret) domain->active--; + mutex_unlock(&priv->mutex); return ret; } From fb5d1b1c5f8a920ee697545fa6dee16825085717 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sat, 27 Jun 2026 11:52:51 +0800 Subject: [PATCH 063/791] ASoC: samsung: aries_audio_probe: double of_node_put due to direct assignment without of_node_get In aries_audio_probe(), aries_dai[0].platforms->of_node is assigned the same pointer as aries_dai[0].cpus->of_node (from of_parse_phandle) without calling of_node_get(). When the sound card is deregistered, the ASoC framework calls of_node_put() on both cpus->of_node and platforms->of_node, causing a double put on the same node and a refcount underflow. Add of_node_get(aries_dai[0].cpus->of_node) before the assignment. Cc: stable@vger.kernel.org Fixes: 7a3a7671fa6c ("ASoC: samsung: Add driver for Aries boards") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260627035251.60172-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/samsung/aries_wm8994.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 48ccc1d1854b..6db91b73f25c 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -658,6 +658,7 @@ static int aries_audio_probe(struct platform_device *pdev) goto out; } + of_node_get(aries_dai[0].cpus->of_node); aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; /* Set CPU of_node for BT DAI */ From bb7c62fdbfecfe15c98c83567f6e1de7d02e52fc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 15:05:17 +0700 Subject: [PATCH 064/791] ASoC: samsung: spdif: Preserve the original clock acquisition error devm_clk_get() may return different error codes, including -EPROBE_DEFER. The current code overwrites the original error with -ENOENT, preventing deferred probing from working correctly. Replace dev_err() with dev_err_probe() so the original error code is preserved and propagated to the caller. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701080517.298294-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/spdif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 7fc46d55c522..53eaabaf8956 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); if (IS_ERR(spdif->pclk)) { - dev_err(&pdev->dev, "failed to get peri-clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), + "failed to get peri-clock\n"); goto err0; } ret = clk_prepare_enable(spdif->pclk); @@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); if (IS_ERR(spdif->sclk)) { - dev_err(&pdev->dev, "failed to get internal source clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), + "failed to get internal source clock\n"); goto err1; } ret = clk_prepare_enable(spdif->sclk); From 0eb0e3c623ac1da8b85d518043fef7660af7805d Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:03 +0800 Subject: [PATCH 065/791] ASoC: loongson: Fix error handling in ACPI property parsing In loongson_card_parse_acpi(), the return value of device_property_read_string() for the `codec-dai-name` property was ignored. If the property is missing or invalid, an uninitialized pointer would be used later, potentially leading to undefined behavior. Fix this by checking the return value and propagating the error appropriately. Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/cover.1780538113.git.zhoubinbin@loongson.cn?part=5 Fixes: ddb538a3004b ("ASoC: loongson: Factor out loongson_card_acpi_find_device() function") Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/08e44a54708eae053be148524346bb8dfcd55b03.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 7910d5d9ac4f..ea895fe6b5e9 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -91,7 +91,7 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) const char *codec_dai_name; struct acpi_device *adev; struct device *phy_dev; - int i; + int i, ret; /* fixup platform name based on reference node */ adev = loongson_card_acpi_find_device(card, "cpu"); @@ -108,7 +108,9 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) return -ENOENT; snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + ret = device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + if (ret) + return ret; for (i = 0; i < card->num_links; i++) { loongson_dai_links[i].platforms->name = dev_name(phy_dev); From 914e95aaec0df7dc2f3e3e5a774b09f92f37cd91 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:04 +0800 Subject: [PATCH 066/791] ASoC: dt-bindings: loongson,ls2k1000-i2s: Document Loongson-2K0300 compatible Add a new compatible string `loongson,ls2k0300-i2s` for the I2S controller found on Loongson-2K0300 SoC. Unlike Loongson-2K1000, Loongson-2K0300 does not require the second register region for APB DMA configuration, so update the binding to allow a single reg entry. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/af092f9eabdc170c3d7951b29eee6512f748eb48.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../bindings/sound/loongson,ls2k1000-i2s.yaml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml index da79510bb2d9..51e23c189f7a 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml @@ -14,9 +14,12 @@ allOf: properties: compatible: - const: loongson,ls2k1000-i2s + enum: + - loongson,ls2k0300-i2s + - loongson,ls2k1000-i2s reg: + minItems: 1 items: - description: Loongson I2S controller Registers. - description: APB DMA config register for Loongson I2S controller. @@ -49,6 +52,23 @@ required: unevaluatedProperties: false +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k1000-i2s + +then: + properties: + reg: + minItems: 2 + +else: + properties: + reg: + maxItems: 1 + examples: - | #include From 41ab1f8a0edaa98d59162fbe801c1f162fc6cf9a Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:05 +0800 Subject: [PATCH 067/791] ASoC: loongson: Add Loongson-2K0300 I2S controller support The Loongson-2K0300 I2S interface differs significantly from the Loongson-2K1000. Although both utilize external DMA controllers, the Loongson-2K0300 does not require additional registers for routing configuration. Due to hardware design flaw, an extra controller reset sequence is required during probe. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/4d3caa62d4275e1495505387198408f4d30d453c.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_i2s_plat.c | 42 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c index ac054b6ce632..82d95c6644ef 100644 --- a/sound/soc/loongson/loongson_i2s_plat.c +++ b/sound/soc/loongson/loongson_i2s_plat.c @@ -2,7 +2,7 @@ // // Loongson I2S controller master mode dirver(platform device) // -// Copyright (C) 2023-2024 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // // Author: Yingkun Meng // Binbin Zhou @@ -21,6 +21,7 @@ #include "loongson_i2s.h" #include "loongson_dma.h" +/* Loongson-2K1000 APBDMA routing */ #define LOONGSON_I2S_RX_DMA_OFFSET 21 #define LOONGSON_I2S_TX_DMA_OFFSET 18 @@ -30,6 +31,11 @@ #define LOONGSON_DMA3_CONF 0x3 #define LOONGSON_DMA4_CONF 0x4 +struct loongson_i2s_plat_config { + int rev_id; + int (*i2s_dma_config)(struct platform_device *pdev); +}; + static int loongson_i2s_apbdma_config(struct platform_device *pdev) { int val; @@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) return 0; } +static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { + .rev_id = 1, +}; + +static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { + .rev_id = 0, + .i2s_dma_config = loongson_i2s_apbdma_config, +}; + static int loongson_i2s_plat_probe(struct platform_device *pdev) { + const struct loongson_i2s_plat_config *plat_config; struct device *dev = &pdev->dev; struct loongson_i2s *i2s; struct resource *res; @@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (!i2s) return -ENOMEM; - ret = loongson_i2s_apbdma_config(pdev); - if (ret) - return ret; + plat_config = device_get_match_data(dev); + if (!plat_config) + return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (plat_config->i2s_dma_config) { + ret = plat_config->i2s_dma_config(pdev); + if (ret) + return ret; + } + + i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->reg_base)) return dev_err_probe(dev, PTR_ERR(i2s->reg_base), "devm_ioremap_resource failed\n"); @@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (IS_ERR(i2s_clk)) return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); i2s->clk_rate = clk_get_rate(i2s_clk); + i2s->rev_id = plat_config->rev_id; dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); dev_set_name(dev, LS_I2S_DRVNAME); dev_set_drvdata(dev, i2s); + if (i2s->rev_id == 1) { + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); + fsleep(200); + } + ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, &loongson_i2s_dai, 1); if (ret) @@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) } static const struct of_device_id loongson_i2s_ids[] = { - { .compatible = "loongson,ls2k1000-i2s" }, + { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, + { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_i2s_ids); From 95133a9ac817242dded1d15643a2aa5faaa289d8 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:25 +0800 Subject: [PATCH 068/791] ASoC: dt-bindings: loongson,ls-audio-card: Use common sound card Reference the common sound card properties. This allows removing the `model` property and directly using the common `audio-routing` property later on. Acked-by: Rob Herring (Arm) Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/fa72a429a9d076d381f7d514184f19d5a35ffa51.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../bindings/sound/loongson,ls-audio-card.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index 61e8babed402..e1b7445a8b22 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -8,20 +8,20 @@ title: Loongson 7axxx/2kxxx ASoC audio sound card driver maintainers: - Yingkun Meng + - Binbin Zhou description: The binding describes the sound card present in loongson 7axxx/2kxxx platform. The sound card is an ASoC component which uses Loongson I2S controller to transfer the audio data. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: const: loongson,ls-audio-card - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs @@ -47,12 +47,11 @@ properties: required: - compatible - - model - mclk-fs - cpu - codec -additionalProperties: false +unevaluatedProperties: false examples: - | From 5460b4ddc76a04eb4ad05333f904bad67f1730e2 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:26 +0800 Subject: [PATCH 069/791] ASoC: dt-bindings: loongson,ls-audio-card: Add ctcisz forever pi compatible Add a new compatible string `loongson,ls2k0300-forever-pi-audio-card` for the audio card on Loongson-2K0300 ctcisz forever pi SoC. It uses a different DAI format compared to existing Loongson platforms. The existing "loongson,ls-audio-card" remains valid for LS7A, Loongson-2K1000 and Loongson-2K2000. Signed-off-by: Binbin Zhou Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/183d809cd51874bcb78743273e4b7617f120fedb.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/loongson,ls-audio-card.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index e1b7445a8b22..8c214e5d04b1 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -20,7 +20,9 @@ allOf: properties: compatible: - const: loongson,ls-audio-card + enum: + - loongson,ls-audio-card # Loongson-2K1000/Loongson-2K2000/LS7A + - loongson,ls2k0300-forever-pi-audio-card # CTCISZ Forever Pi mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs From da659805e0b055f8f12de022e28a9983d2f3923f Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:27 +0800 Subject: [PATCH 070/791] ASoC: loongson: Add Loongson-2K0300 CTCISZ Forever Pi sound card support The Loongson-2K0300 audio card uses a different DAI format compared to existing Loongson platforms. Move the dai_fmt setting from the static DAI link to runtime hw_params via snd_soc_runtime_set_dai_fmt(), and pass the correct format through driver match data. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/ed1314e1d3275fd20aff47397345a88fad6e9368.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index ea895fe6b5e9..0e63cbcad57a 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -2,8 +2,9 @@ // // Loongson ASoC Audio Machine driver // -// Copyright (C) 2023 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // Author: Yingkun Meng +// Binbin Zhou // #include @@ -18,6 +19,19 @@ static char codec_name[SND_ACPI_I2C_ID_LEN]; struct loongson_card_data { struct snd_soc_card snd_card; unsigned int mclk_fs; + const struct loongson_card_config *cfg; +}; + +struct loongson_card_config { + unsigned int fmt; +}; + +static const struct loongson_card_config ls2k1000_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, +}; + +static const struct loongson_card_config ls2k0300_forever_pi_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, }; static int loongson_card_hw_params(struct snd_pcm_substream *substream, @@ -45,7 +59,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, return ret; } - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); } static const struct snd_soc_ops loongson_ops = { @@ -61,8 +75,6 @@ static struct snd_soc_dai_link loongson_dai_links[] = { { .name = "Loongson Audio Port", .stream_name = "Loongson Audio", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(analog), .ops = &loongson_ops, }, @@ -177,6 +189,10 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) if (!ls_priv) return -ENOMEM; + ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); + if (!ls_priv->cfg) + return -EINVAL; + card = &ls_priv->snd_card; card->dev = dev; @@ -202,7 +218,15 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) } static const struct of_device_id loongson_asoc_dt_ids[] = { - { .compatible = "loongson,ls-audio-card" }, + /* Loongson-2K1000/Loongson-2K2000/LS7A */ + { + .compatible = "loongson,ls-audio-card", + .data = &ls2k1000_card_config + }, + { + .compatible = "loongson,ls2k0300-forever-pi-audio-card", + .data = &ls2k0300_forever_pi_card_config + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); From 3ddae79479f6c96c4083951c7c4511d7236e7982 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:28 +0800 Subject: [PATCH 071/791] ASoC: dt-bindings: loongson,ls-audio-card: Add ATK-DL2K0300B compatible Add new compatible for the ATK-DL2K0300B development board based on Loongson-2K0300. Unlike others, this board features GPIO-controlled headphone detection, headphone control, and speaker enable. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/71430fcee5951fb7a7d52e2091a87707db85e06c.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../sound/loongson,ls-audio-card.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index 8c214e5d04b1..dc7f4afbb777 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -23,6 +23,7 @@ properties: enum: - loongson,ls-audio-card # Loongson-2K1000/Loongson-2K2000/LS7A - loongson,ls2k0300-forever-pi-audio-card # CTCISZ Forever Pi + - loongson,ls2k0300-dl2k0300b-audio-card # ATK-DL2K0300B mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs @@ -47,6 +48,18 @@ properties: required: - sound-dai + spkr-en-gpios: + maxItems: 1 + description: The GPIO that enables the speakers + + hp-ctl-gpios: + maxItems: 1 + description: The GPIO that control the headphones + + hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + required: - compatible - mclk-fs @@ -69,3 +82,28 @@ examples: sound-dai = <&es8323>; }; }; + + - | + #include + + sound { + compatible = "loongson,ls2k0300-dl2k0300b-audio-card"; + model = "loongson-audio"; + mclk-fs = <512>; + hp-det-gpios = <&gpio 81 GPIO_ACTIVE_HIGH>; + spkr-en-gpios = <&gpio 86 GPIO_ACTIVE_HIGH>; + hp-ctl-gpios = <&gpio 87 GPIO_ACTIVE_HIGH>; + audio-routing = + "Headphone", "LOUT1", + "Headphone", "ROUT1", + "Speaker", "LOUT2", + "Speaker", "ROUT2"; + + cpu { + sound-dai = <&i2s>; + }; + + codec { + sound-dai = <&es8388>; + }; + }; From dde4064a4913319cf9b19e745af3417a35f0f738 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:48 +0800 Subject: [PATCH 072/791] ASoC: loongson: Add headphone jack detection and DAPM routing Extend the Loongson audio machine driver with jack detection, DAPM widgets support, enabling proper switching between headphones and speakers on the Loongson-2K0300 ATK-DL2K0300B board. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/c4b1503220b6fb433ecdcb881556827579041c3d.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 131 ++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 0e63cbcad57a..25cd12eab4b1 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -7,33 +7,126 @@ // Binbin Zhou // +#include +#include #include +#include +#include +#include #include #include -#include -#include -#include static char codec_name[SND_ACPI_I2C_ID_LEN]; struct loongson_card_data { struct snd_soc_card snd_card; unsigned int mclk_fs; + struct gpio_desc *gpiod_hp_det; + struct gpio_desc *gpiod_hp_ctl; + struct gpio_desc *gpiod_spkr_en; const struct loongson_card_config *cfg; }; struct loongson_card_config { unsigned int fmt; + bool add_hp_jack; + bool add_dapm_widgets; + bool add_dapm_routes; }; static const struct loongson_card_config ls2k1000_card_config = { .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, }; static const struct loongson_card_config ls2k0300_forever_pi_card_config = { .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, }; +static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = true, + .add_dapm_widgets = true, + .add_dapm_routes = true, +}; + +static int tegra_machine_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); + struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); + + if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) + gpiod_set_value_cansleep(priv->gpiod_spkr_en, + SND_SOC_DAPM_EVENT_ON(event)); + + if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) + gpiod_set_value_cansleep(priv->gpiod_hp_ctl, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", tegra_machine_event), + SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), +}; + +/* Headphones Jack */ + +static struct snd_soc_jack loongson_asoc_hp_jack; + +static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, +}; + +static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { + .name = "Headphones detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) + return 0; + + ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", + SND_JACK_HEADPHONE, + &loongson_asoc_hp_jack, + loongson_asoc_hp_jack_pins, + ARRAY_SIZE(loongson_asoc_hp_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); + return ret; + } + + loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; + + ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); + if (ret) + dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); + + return ret; +} + static int loongson_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -75,6 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { { .name = "Loongson Audio Port", .stream_name = "Loongson Audio", + .init = loongson_asoc_machine_init, SND_SOC_DAILINK_REG(analog), .ops = &loongson_ops, }, @@ -135,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) static int loongson_card_parse_of(struct loongson_card_data *data) { - struct device_node *cpu, *codec; struct snd_soc_card *card = &data->snd_card; + struct device_node *cpu, *codec; struct device *dev = card->dev; int ret, i; + data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); + if (IS_ERR(data->gpiod_hp_det)) + return PTR_ERR(data->gpiod_hp_det); + + data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_hp_ctl)) + return PTR_ERR(data->gpiod_hp_ctl); + + data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_spkr_en)) + return PTR_ERR(data->gpiod_spkr_en); + + if (data->cfg->add_dapm_routes) { + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) + return ret; + } + cpu = of_get_child_by_name(dev->of_node, "cpu"); if (!cpu) { dev_err(dev, "platform property missing or invalid\n"); return -EINVAL; } + codec = of_get_child_by_name(dev->of_node, "codec"); if (!codec) { dev_err(dev, "audio-codec property missing or invalid\n"); @@ -199,6 +312,12 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dai_link = loongson_dai_links; card->num_links = ARRAY_SIZE(loongson_dai_links); + + if (ls_priv->cfg->add_dapm_widgets) { + card->dapm_widgets = loongson_aosc_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); + } + snd_soc_card_set_drvdata(card, ls_priv); ret = device_property_read_string(dev, "model", &card->name); @@ -227,6 +346,10 @@ static const struct of_device_id loongson_asoc_dt_ids[] = { .compatible = "loongson,ls2k0300-forever-pi-audio-card", .data = &ls2k0300_forever_pi_card_config }, + { + .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", + .data = &ls2k0300_dl2k0300b_card_config + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); From 5f988d622318e5f485c60a8be661a81ed9f74c53 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:49 +0800 Subject: [PATCH 073/791] ASoC: es8328: Add DAPM routes from MIC inputs to Mic Bias The ES8328 codec has differential/single-ended microphone inputs (LINPUT1/RINPUT1, LINPUT2/RINPUT2) that require connection to the internal Mic Bias generator for proper operation. Currently, these routes are missing, which can cause microphone recording to fail. Add the missing DAPM routes to link the input pins to the Mic Bias supply, ensuring the microphone bias voltage is correctly applied. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/dcf1f8ae4f1f192a1d63e9fe7044b0218119b5eb.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 9838fe42cb6f..aaa6646ad4c5 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { { "Mic Bias", NULL, "Mic Bias Gen" }, + { "LINPUT1", NULL, "Mic Bias" }, + { "RINPUT1", NULL, "Mic Bias" }, + { "LINPUT2", NULL, "Mic Bias" }, + { "RINPUT2", NULL, "Mic Bias" }, + { "Left Mixer", NULL, "Left DAC" }, { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, { "Left Mixer", "Right Playback Switch", "Right DAC" }, From 58ad83cf62d1e534bd2c8e1d597acd8213757fda Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:38 +0700 Subject: [PATCH 074/791] ALSA: control: Drop redundant stream_open() return check The previous conversion from nonseekable_open() to stream_open() retained the existing error check. Since stream_open() always returns 0, remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-2-phucduc.bui@gmail.com --- sound/core/control.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 7a8dc506221e..037e455bb11b 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -55,9 +55,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) struct snd_ctl_file *ctl; int i, err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); if (!card) { From 44f35a7050c4a8f461bd486ad78c6e0995392feb Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:39 +0700 Subject: [PATCH 075/791] ALSA: mixer: oss: Drop redundant nonseekable_open() return check nonseekable_open() always returns 0, so the error check is unnecessary. Remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-3-phucduc.bui@gmail.com --- sound/core/oss/mixer_oss.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 591cff800329..f5bcc7896542 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -30,9 +30,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) struct snd_mixer_oss_file *fmixer; int err; - err = nonseekable_open(inode, file); - if (err < 0) - return err; + nonseekable_open(inode, file); card = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_MIXER); From 77a5dc04fe7936b021ad7b7ae7c4ba1083d1b68a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:40 +0700 Subject: [PATCH 076/791] ALSA: pcm: oss: Drop redundant nonseekable_open() return check nonseekable_open() always returns 0, so the error check is unnecessary. Remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-4-phucduc.bui@gmail.com --- sound/core/oss/pcm_oss.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 9826d9a0be6d..0d7818eb3e14 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2495,9 +2495,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) int nonblock; wait_queue_entry_t wait; - err = nonseekable_open(inode, file); - if (err < 0) - return err; + nonseekable_open(inode, file); pcm = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_PCM); From 290a94d4b9ee8a78ccb4e4a9ee05c59d8907c781 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:41 +0700 Subject: [PATCH 077/791] ALSA: pcm: Drop redundant nonseekable_open() return check nonseekable_open() always returns 0, so the error check is unnecessary. Remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-5-phucduc.bui@gmail.com --- sound/core/pcm_native.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d4e04b5088c5..3462cd5275a2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2873,9 +2873,10 @@ static int snd_pcm_open_file(struct file *file, static int snd_pcm_playback_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - int err = nonseekable_open(inode, file); - if (err < 0) - return err; + int err; + + nonseekable_open(inode, file); + pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK); err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); @@ -2887,9 +2888,10 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file) static int snd_pcm_capture_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - int err = nonseekable_open(inode, file); - if (err < 0) - return err; + int err; + + nonseekable_open(inode, file); + pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_CAPTURE); err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); From 866405c5802fecbe30f5b8c27886a0704572b440 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:42 +0700 Subject: [PATCH 078/791] ALSA: rawmidi: Drop redundant stream_open() return check The previous conversion from nonseekable_open() to stream_open() retained the existing error check. Since stream_open() always returns 0, remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-6-phucduc.bui@gmail.com --- sound/core/rawmidi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 4dfd9d53e6d3..789254a88e53 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -441,9 +441,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) return -EINVAL; /* invalid combination */ - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); if (maj == snd_major) { rmidi = snd_lookup_minor_data(iminor(inode), From b1d9880aaa2114e814229d0b3365cab52c60038a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:43 +0700 Subject: [PATCH 079/791] ALSA: seq: Drop redundant stream_open() return check The previous conversion from nonseekable_open() to stream_open() retained the existing error check. Since stream_open() always returns 0, remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-7-phucduc.bui@gmail.com --- sound/core/seq/seq_clientmgr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 28782e1776fa..8fe872367568 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -296,11 +296,8 @@ static int snd_seq_open(struct inode *inode, struct file *file) int c, mode; /* client id */ struct snd_seq_client *client; struct snd_seq_user_client *user; - int err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); scoped_guard(mutex, ®ister_mutex) { client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); From b8c89ae642aef22ea059c2ee4ab2dfacb6b65f08 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 2 Jul 2026 15:44:44 +0700 Subject: [PATCH 080/791] ALSA: timer: Drop redundant stream_open() return check The previous conversion from nonseekable_open() to stream_open() retained the existing error check. Since stream_open() always returns 0, remove the dead error handling path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260702084445.519669-8-phucduc.bui@gmail.com --- sound/core/timer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 51c6ac4df9f4..937d7996f7ab 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1531,11 +1531,8 @@ static int realloc_user_queue(struct snd_timer_user *tu, int size) static int snd_timer_user_open(struct inode *inode, struct file *file) { struct snd_timer_user *tu; - int err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); tu = kzalloc_obj(*tu); if (tu == NULL) From bd8f2187618980f5274a74321ea4844df8667488 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 3 Jul 2026 17:38:40 +0700 Subject: [PATCH 081/791] ALSA: core: propagate actual error code from register_chrdev The alsa_sound_init() function currently returns a hardcoded -EIO if register_chrdev() fails, masking specific error codes from __register_chrdev() such as -EINVAL, -ENOMEM, or -EBUSY. Masking these failures under a generic -EIO makes system-level diagnostics unnecessarily difficult. Propagating explicit error codes from register_chrdev() is also an established convention widely practiced across other subsystems. Fix this by capturing and propagating the dynamic error code from register_chrdev(). While at it, also assign the return value of snd_info_init() to the 'err' variable instead of hardcoding -ENOMEM to ensure consistency and future-proofing. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260703103840.253527-1-phucduc.bui@gmail.com Signed-off-by: Takashi Iwai --- sound/core/sound.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sound/core/sound.c b/sound/core/sound.c index 8d05fe0d263b..3e1969a52b21 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -395,15 +395,21 @@ int __init snd_minor_info_init(void) static int __init alsa_sound_init(void) { + int err; + snd_major = major; snd_ecards_limit = cards_limit; - if (register_chrdev(major, "alsa", &snd_fops)) { + + err = register_chrdev(major, "alsa", &snd_fops); + if (err < 0) { pr_err("ALSA core: unable to register native major device number %d\n", major); - return -EIO; + return err; } - if (snd_info_init() < 0) { + + err = snd_info_init(); + if (err < 0) { unregister_chrdev(major, "alsa"); - return -ENOMEM; + return err; } #ifdef CONFIG_SND_DEBUG From 380d6b549d5aa96a2c485d91e1a978abd7567f65 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 1 Jul 2026 02:36:19 +0000 Subject: [PATCH 082/791] ASoC: sof: nocodec: tidyup sof_nocodec_bes_setup() It has only 1 card (= sof_nocodec_card). No need to use parameter to get it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5mjcu70.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sof/nocodec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 11a95dba3c9c..fae36595b81a 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { static int sof_nocodec_bes_setup(struct device *dev, struct snd_soc_dai_driver *drv, struct snd_soc_dai_link *links, - int link_num, struct snd_soc_card *card) + int link_num) { + struct snd_soc_card *card = &sof_nocodec_card; struct snd_soc_dai_link_component *dlc; int i; - if (!drv || !links || !card) + if (!drv || !links) return -EINVAL; /* set up BE dai_links */ @@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, if (!links) return -ENOMEM; - return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); + return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); } static int sof_nocodec_probe(struct platform_device *pdev) From 39222ca2ff2b233d09b363e7d227de8c1bf0b91b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 1 Jul 2026 02:36:23 +0000 Subject: [PATCH 083/791] ASoC: sof: nocodec: tidyup sof_nocodec_setup() We can get necessary info from mach. Let's cleanup sof_nocodec_setup(). Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87fr23cu6w.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sof/nocodec.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index fae36595b81a..34744f8ae34a 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -68,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, return 0; } -static int sof_nocodec_setup(struct device *dev, - u32 num_dai_drivers, - struct snd_soc_dai_driver *dai_drivers) +static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) { + u32 num_dai_drivers = mach->mach_params.num_dai_drivers; + struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; struct snd_soc_dai_link *links; /* create dummy BE dai_links */ @@ -93,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) snd_soc_card_set_topology_name(card, "sof"); - ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, - mach->mach_params.dai_drivers); + ret = sof_nocodec_setup(card->dev, mach); if (ret < 0) return ret; From 406e181cb32546db0867f4c44bdbe5e23a56dbde Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 15:43:36 +0700 Subject: [PATCH 084/791] ASoC: samsung: i2s: Use dev_err_probe() for iis clock error Switch the iis clock error path to dev_err_probe(). This folds the dev_err() and return into a single statement and, when devm_clk_get() returns -EPROBE_DEFER, avoids logging a spurious error on every probe retry while still recording the reason in the deferred-probe debugfs. No functional change other than the demoted log level on deferred probe; the returned error code is unchanged. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701084336.308886-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f80e8d498156..56d11741dabd 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1441,10 +1441,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) regs_base = res->start; priv->clk = devm_clk_get(&pdev->dev, "iis"); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "Failed to get iis clock\n"); - return PTR_ERR(priv->clk); - } + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "Failed to get iis clock\n"); + ret = clk_prepare_enable(priv->clk); if (ret != 0) { From cd054a6e272caa97ab808ef6f5588749a1429108 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 2 Jul 2026 00:14:57 +0530 Subject: [PATCH 085/791] ASoC: codecs: lpass-wsa-macro: Switch to PM clock framework for runtime PM Convert the LPASS WSA macro codec driver to runtime PM clock management by using the PM clock framework. Replace manual macro/dcodec/mclk/npl/fsgen clock toggling with PM clock helpers and runtime PM callbacks. Keep the SWR gate runtime PM reference from SWR clock enable until disable so autosuspend does not gate clocks while SWR is still prepared. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_WSA_MACRO since this patch introduces PM clock APIs. Tighten error unwind by checking pm_runtime_put_sync_suspend() in probe and by restoring regcache state if pm_clk_resume()/regcache_sync() fails. Suggested-by: Mark Brown Signed-off-by: Ajay Kumar Nandam Reviewed-by: Srinivas Kandagatla Tested-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260702-xo-sd-codec-v7-b4-v8-1-d39d0fdb7859@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/lpass-wsa-macro.c | 137 ++++++++++++----------------- 2 files changed, 56 insertions(+), 82 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..92cfa623782c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2897,6 +2897,7 @@ config SND_SOC_LPASS_MACRO_COMMON config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 5ad0448af649..718564ee381e 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) { struct regmap *regmap = wsa->regmap; + int ret; if (enable) { - int ret; - - ret = clk_prepare_enable(wsa->mclk); - if (ret) { - dev_err(wsa->dev, "failed to enable mclk\n"); + ret = pm_runtime_resume_and_get(wsa->dev); + if (ret < 0) return ret; - } + wsa_macro_mclk_enable(wsa, true); regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, @@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, CDC_WSA_SWR_CLK_EN_MASK, 0); wsa_macro_mclk_enable(wsa, false); - clk_disable_unprepare(wsa->mclk); + + ret = pm_runtime_put_autosuspend(wsa->dev); + if (ret < 0) + dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); } return 0; @@ -2774,25 +2776,23 @@ static int wsa_macro_probe(struct platform_device *pdev) clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - ret = clk_prepare_enable(wsa->macro); + ret = devm_pm_clk_create(dev); if (ret) - goto err; + return ret; - ret = clk_prepare_enable(wsa->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; - ret = clk_prepare_enable(wsa->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + return ret; - ret = clk_prepare_enable(wsa->npl); - if (ret) - goto err_npl; - - ret = clk_prepare_enable(wsa->fsgen); - if (ret) - goto err_fsgen; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; /* reset swr ip */ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, @@ -2809,56 +2809,37 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa_macro_dai, ARRAY_SIZE(wsa_macro_dai)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = wsa_macro_register_mclk_output(wsa); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; - -err_clkout: - clk_disable_unprepare(wsa->fsgen); -err_fsgen: - clk_disable_unprepare(wsa->npl); -err_npl: - clk_disable_unprepare(wsa->mclk); -err_mclk: - clk_disable_unprepare(wsa->dcodec); -err_dcodec: - clk_disable_unprepare(wsa->macro); -err: +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); return ret; - -} - -static void wsa_macro_remove(struct platform_device *pdev) -{ - struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(wsa->macro); - clk_disable_unprepare(wsa->dcodec); - clk_disable_unprepare(wsa->mclk); - clk_disable_unprepare(wsa->npl); - clk_disable_unprepare(wsa->fsgen); } static int wsa_macro_runtime_suspend(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); + int ret; regcache_cache_only(wsa->regmap, true); - regcache_mark_dirty(wsa->regmap); - clk_disable_unprepare(wsa->fsgen); - clk_disable_unprepare(wsa->npl); - clk_disable_unprepare(wsa->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(wsa->regmap, false); + return ret; + } + + regcache_mark_dirty(wsa->regmap); return 0; } @@ -2866,36 +2847,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) static int wsa_macro_runtime_resume(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); - int ret; + int ret, sret; - ret = clk_prepare_enable(wsa->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + return ret; + } + regcache_cache_only(wsa->regmap, false); + + ret = regcache_sync(wsa->regmap); + if (ret) { + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + sret = pm_clk_suspend(dev); + if (sret) + dev_err(dev, + "failed to suspend clocks after regcache sync failure: %d\n", + sret); return ret; } - ret = clk_prepare_enable(wsa->npl); - if (ret) { - dev_err(dev, "unable to prepare mclkx2\n"); - goto err_npl; - } - - ret = clk_prepare_enable(wsa->fsgen); - if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; - } - - regcache_cache_only(wsa->regmap, false); - regcache_sync(wsa->regmap); - return 0; -err_fsgen: - clk_disable_unprepare(wsa->npl); -err_npl: - clk_disable_unprepare(wsa->mclk); - - return ret; } static const struct dev_pm_ops wsa_macro_pm_ops = { @@ -2929,7 +2903,6 @@ static struct platform_driver wsa_macro_driver = { .pm = pm_ptr(&wsa_macro_pm_ops), }, .probe = wsa_macro_probe, - .remove = wsa_macro_remove, }; module_platform_driver(wsa_macro_driver); From eb667d0fbdd38d5a800b9e7aafc9a6c14530b9bf Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 2 Jul 2026 00:14:58 +0530 Subject: [PATCH 086/791] ASoC: codecs: lpass-va-macro: Switch to PM clock framework for runtime PM Convert the LPASS VA macro codec driver to runtime PM clock management by using the PM clock framework. Replace manual macro/dcodec/mclk/npl clock handling with PM clock helpers and runtime PM callbacks, and keep runtime PM references around fsgen clock gating so PM-clock-managed clocks remain active while fsgen is enabled. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_VA_MACRO since this patch introduces PM clock APIs. Improve failure unwind paths: handle runtime PM put errors in probe/fsgen paths and restore regcache state correctly on resume failure. Suggested-by: Mark Brown Signed-off-by: Ajay Kumar Nandam Reviewed-by: Srinivas Kandagatla Tested-by: Srinivas Kandagatla Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260702-xo-sd-codec-v7-b4-v8-2-d39d0fdb7859@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/lpass-va-macro.c | 131 ++++++++++++++++-------------- 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 92cfa623782c..030bb902fdef 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2904,6 +2904,7 @@ config SND_SOC_LPASS_WSA_MACRO config SND_SOC_LPASS_VA_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 528d5b167ecf..814a14f0d117 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1346,34 +1347,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) { struct va_macro *va = to_va_macro(hw); struct regmap *regmap = va->regmap; - int ret; + int ret, rpm_ret; - if (va->has_swr_master) { - ret = clk_prepare_enable(va->mclk); - if (ret) - return ret; - } + ret = pm_runtime_resume_and_get(va->dev); + if (ret < 0) + return ret; ret = va_macro_mclk_enable(va, true); + if (ret) { + rpm_ret = pm_runtime_put_autosuspend(va->dev); + if (rpm_ret < 0) + dev_warn(va->dev, + "runtime PM put failed in fsgen enable unwind: %d\n", + rpm_ret); + return ret; + } if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); - return ret; + return 0; } static void fsgen_gate_disable(struct clk_hw *hw) { struct va_macro *va = to_va_macro(hw); struct regmap *regmap = va->regmap; + int ret; if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, CDC_VA_SWR_CLK_EN_MASK, 0x0); va_macro_mclk_enable(va, false); - if (va->has_swr_master) - clk_disable_unprepare(va->mclk); + + ret = pm_runtime_put_autosuspend(va->dev); + if (ret < 0) + dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); } static int fsgen_gate_is_enabled(struct clk_hw *hw) @@ -1534,6 +1544,7 @@ static int va_macro_probe(struct platform_device *pdev) void __iomem *base; u32 sample_rate = 0; int ret; + int rpm_ret; va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); if (!va) @@ -1601,22 +1612,24 @@ static int va_macro_probe(struct platform_device *pdev) clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); } - ret = clk_prepare_enable(va->macro); + ret = devm_pm_clk_create(dev); if (ret) goto err; - ret = clk_prepare_enable(va->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + goto err; - ret = clk_prepare_enable(va->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + goto err; - if (va->has_npl_clk) { - ret = clk_prepare_enable(va->npl); - if (ret) - goto err_npl; + rpm_ret = pm_runtime_resume_and_get(dev); + if (rpm_ret < 0) { + ret = rpm_ret; + goto err; } /** @@ -1629,7 +1642,7 @@ static int va_macro_probe(struct platform_device *pdev) /* read version from register */ ret = va_macro_set_lpass_codec_version(va); if (ret) - goto err_clkout; + goto err_rpm_put; } if (va->has_swr_master) { @@ -1659,35 +1672,28 @@ static int va_macro_probe(struct platform_device *pdev) va_macro_dais, ARRAY_SIZE(va_macro_dais)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = va_macro_register_fsgen_output(va); if (ret) - goto err_clkout; + goto err_rpm_put; va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); if (IS_ERR(va->fsgen)) { ret = PTR_ERR(va->fsgen); - goto err_clkout; + goto err_rpm_put; } + rpm_ret = pm_runtime_put_autosuspend(dev); + if (rpm_ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); + return 0; -err_clkout: - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); -err_npl: - clk_disable_unprepare(va->mclk); -err_mclk: - clk_disable_unprepare(va->dcodec); -err_dcodec: - clk_disable_unprepare(va->macro); +err_rpm_put: + rpm_ret = pm_runtime_put_sync_suspend(dev); + if (rpm_ret < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); err: lpass_macro_pds_exit(va->pds); @@ -1698,53 +1704,52 @@ static void va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); - - clk_disable_unprepare(va->mclk); - clk_disable_unprepare(va->dcodec); - clk_disable_unprepare(va->macro); - lpass_macro_pds_exit(va->pds); } static int va_macro_runtime_suspend(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); + int ret; regcache_cache_only(va->regmap, true); + + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(va->regmap, false); + return ret; + } + regcache_mark_dirty(va->regmap); - if (va->has_npl_clk) - clk_disable_unprepare(va->npl); - - clk_disable_unprepare(va->mclk); - return 0; } static int va_macro_runtime_resume(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); - int ret; + int ret, sret; - ret = clk_prepare_enable(va->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(va->dev, "unable to prepare mclk\n"); + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); return ret; } - if (va->has_npl_clk) { - ret = clk_prepare_enable(va->npl); - if (ret) { - clk_disable_unprepare(va->mclk); - dev_err(va->dev, "unable to prepare npl\n"); - return ret; - } - } - regcache_cache_only(va->regmap, false); - regcache_sync(va->regmap); + + ret = regcache_sync(va->regmap); + if (ret) { + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); + sret = pm_clk_suspend(dev); + if (sret) + dev_err(va->dev, + "failed to suspend clocks after regcache sync failure: %d\n", + sret); + return ret; + } return 0; } From 541735571578b84987868c5662089f73bac36895 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 2 Jul 2026 00:14:59 +0530 Subject: [PATCH 087/791] ASoC: codecs: lpass-wsa-macro: Use devm_clk_hw_register() for MCLK output Switch WSA MCLK output registration to devm_clk_hw_register() so the clk hw is automatically unregistered on probe failure and remove. Reviewed-by: Konrad Dybcio Signed-off-by: Ajay Kumar Nandam Reviewed-by: Srinivas Kandagatla Tested-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260702-xo-sd-codec-v7-b4-v8-3-d39d0fdb7859@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 718564ee381e..f511816aa4a0 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2658,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) init.num_parents = 1; wsa->hw.init = &init; hw = &wsa->hw; - ret = clk_hw_register(wsa->dev, hw); + ret = devm_clk_hw_register(wsa->dev, hw); if (ret) return ret; From e08d63e63ac09826b0e78c55cb7faa2eb16872a1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Jul 2026 15:29:18 +0800 Subject: [PATCH 088/791] ASoC: codecs: max98390: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during resume. max98390_resume() currently ignores that failure and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260702072918.85779-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98390.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 2bbedf84ee5d..66309e87fdbd 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -952,11 +952,17 @@ static int max98390_suspend(struct device *dev) static int max98390_resume(struct device *dev) { struct max98390_priv *max98390 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "%s:Enter\n", __func__); regcache_cache_only(max98390->regmap, false); - regcache_sync(max98390->regmap); + ret = regcache_sync(max98390->regmap); + if (ret) { + regcache_cache_only(max98390->regmap, true); + regcache_mark_dirty(max98390->regmap); + return ret; + } return 0; } From 8aa079a408d0732b1bab9930d327bccf77cf7d0a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:37:45 +0000 Subject: [PATCH 089/791] ASoC: mediatek: mt8365-mt6357: tidyup mach_priv It sets soc_card_data (1) in Card private data at (A), but the function (z) after that gets it as priv (2) at (B). These are different data (*). (z) static int mt8365_mt6357_gpio_probe(...) { (B) struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); ... ^^^^(2) ^^^^^^^^^^^ } static int mt8365_mt6357_dev_probe(*soc_card_data, ...) { ^^^^^^^^^^^^^(1) ... struct mt8365_mt6357_priv *mach_priv; ... ^^^^^^^^^(2) (*) soc_card_data->mach_priv = mach_priv; ^^^^^^^^^^^^^(1) ^^^^^^^^^(2) (A) snd_soc_card_set_drvdata(card, soc_card_data); ^^^^^^^^^^^ ^^^^^^^^^^^^^(1) (z) mt8365_mt6357_gpio_probe(card); ... } Depending on the defined order in the struct (s), they may be the same pointer, but mach_priv (2) is not top of soc_card_data (1), thus the function (z) is getting wrong pointer. Fix it. (1) (s) struct mtk_soc_card_data { const struct mtk_sof_priv *sof_priv; ... void *mach_priv; }; (2) Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzlteuae.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8365/mt8365-mt6357.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c index 10f9ef73c130..38bd86b67ecc 100644 --- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c +++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c @@ -71,7 +71,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; int ret = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -102,7 +103,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; int ret = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -246,7 +248,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) { - struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); + struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; struct device *dev = card->dev; int ret, i; From ec2332472f587a32d4f90970efaf5acfdf58ce48 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:02 +0530 Subject: [PATCH 090/791] ASoC: amd: acp: add ACPI machine table for ACP7.B/7.F SOF driver Add snd_soc_acpi_amd_acp7x_sof_machines[] ACPI machine table for ACP7.B and ACP7.F PCI revision based platforms. Add the extern declaration to mach-config.h so that it can be referenced from the SOF PCI driver. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 12 ++++++++++++ sound/soc/amd/mach-config.h | 1 + 2 files changed, 13 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0d977f4f758d..93a2182d4e86 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -360,5 +360,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { + { + .id = "AMDI1010", + .drv_name = "acp7x-dsp", + .pdata = &acp_quirk_data, + .fw_filename = "sof-acp7x.ri", + .sof_tplg_filename = "sof-acp7x.tplg", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); + MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index 5b6362103ca0..b602a983feb7 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; struct config_entry { u32 flags; From 0ec5afad9d31c4f2b92933d20261e4095e26ad9b Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:03 +0530 Subject: [PATCH 091/791] ASoC: SOF: amd: add base platform support for ACP7.B/7.F Add SOF support for ACP7.B and ACP7.F PCI revision based platforms. This covers Kconfig/Makefile entries, register offset definitions, DMA descriptor/channel/status paths, PGFSM power-on handling, PCI device driver, I2S/DMIC DAI definitions, and IPC SW interrupt trigger offset selection for ACP7.B/7.F. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 10 ++ sound/soc/sof/amd/Makefile | 2 + sound/soc/sof/amd/acp-dsp-offset.h | 17 ++++ sound/soc/sof/amd/acp-ipc.c | 13 ++- sound/soc/sof/amd/acp.c | 23 ++++- sound/soc/sof/amd/acp.h | 9 ++ sound/soc/sof/amd/acp7x.c | 142 +++++++++++++++++++++++++++++ sound/soc/sof/amd/pci-acp7x.c | 116 +++++++++++++++++++++++ 8 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 sound/soc/sof/amd/acp7x.c create mode 100644 sound/soc/sof/amd/pci-acp7x.c diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 05faf1c6d6fc..7413bfdb0943 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -103,5 +103,15 @@ config SND_SOC_SOF_AMD_ACP70 Select this option for SOF support on AMD ACP7.0/ACP7.1 version based platforms. Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. +config SND_SOC_SOF_AMD_ACP7X + tristate "SOF support for ACP7.B/7.F platforms" + depends on SND_SOC_SOF_PCI + depends on AMD_NODE + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support on AMD ACP7.B and ACP7.F PCI + revision based platforms. + Say Y if you want to enable SOF on ACP7.B/7.F based platforms. + If unsure select "N". endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 6ae39fd5a836..bb1c0ddef69d 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o snd-sof-amd-acp63-y := pci-acp63.o acp63.o snd-sof-amd-acp70-y := pci-acp70.o acp70.o +snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o @@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o +obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 08583a91afbc..bea1bd3afa70 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -68,12 +68,14 @@ #define ACP5X_PGFSM_BASE 0x1424 #define ACP6X_PGFSM_BASE 0x1024 #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE +#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE #define PGFSM_CONTROL_OFFSET 0x0 #define PGFSM_STATUS_OFFSET 0x4 #define ACP3X_CLKMUX_SEL 0x1424 #define ACP5X_CLKMUX_SEL 0x142C #define ACP6X_CLKMUX_SEL 0x102C #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL +#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 @@ -86,21 +88,31 @@ #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 +#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB +#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 +#define ACP7X_EXT_INTR_STAT 0x1A1C +#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 +#define ACP7X_EXT_INTR_STAT1 0x1A20 #define ACP3X_DSP_SW_INTR_BASE 0x1814 #define ACP5X_DSP_SW_INTR_BASE 0x1814 #define ACP6X_DSP_SW_INTR_BASE 0x1808 #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE +#define ACP7X_DSP_SW_INTR_BASE 0x1860 #define DSP_SW_INTR_CNTL_OFFSET 0x0 #define DSP_SW_INTR_STAT_OFFSET 0x4 +#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) #define DSP_SW_INTR_TRIG_OFFSET 0x8 +#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 #define ACP3X_ERROR_STATUS 0x18C4 #define ACP6X_ERROR_STATUS 0x1A4C #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS +#define ACP7X_ERROR_STATUS 0x1A88 #define ACP3X_AXI2DAGB_SEM_0 0x1880 #define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 +#define ACP7X_AXI2DAGB_SEM_0 0x18F4 /* ACP common registers to report errors related to I2S & SoundWire interfaces */ #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 @@ -123,6 +135,7 @@ #define ACP_SCRATCH_REG_0 0x10000 #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL +#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL /* Cache window registers */ #define ACP_DSP0_CACHE_OFFSET0 0x0420 @@ -139,5 +152,9 @@ #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) #define ACP70_SDW0_PME_STAT BIT(26) #define ACP70_SDW1_PME_STAT BIT(27) +#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 +#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF +#define ACP7X_ZSC_DSP_CTRL 0x001014 +#define ACP7X_PME_EN ACP70_PME_EN #endif diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 94025bc799ea..494f9cb0c06f 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) struct snd_sof_dev *sdev = adata->dev; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); u32 swintr_trigger; + unsigned int swintr_trigger_reg_offset; + switch (adata->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; + break; + default: + swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; + } swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + - DSP_SW_INTR_TRIG_OFFSET); + swintr_trigger_reg_offset); swintr_trigger |= 0x01; - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, swintr_trigger); } diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index e6af8927baa0..bb3766aacf0e 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -60,6 +60,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; break; @@ -101,6 +103,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_cntl_0 = ACP70_DMA_CNTL_0; acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; @@ -342,6 +346,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) case ACP70_PCI_ID: case ACP71_PCI_ID: case ACP72_PCI_ID: + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: acp_dma_ch_sts = ACP70_DMA_CH_STS; break; default: @@ -595,6 +601,11 @@ static int acp_power_on(struct snd_sof_dev *sdev) acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; break; + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; + acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; + break; default: return -EINVAL; } @@ -604,7 +615,8 @@ static int acp_power_on(struct snd_sof_dev *sdev) acp_pgfsm_cntl_mask); ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, - !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + !val, ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); if (ret < 0) dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); @@ -703,6 +715,13 @@ static int acp_init(struct snd_sof_dev *sdev) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); break; + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, + ACP7X_IDMA_ERROR_MASK); + break; } return 0; } @@ -749,6 +768,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) case ACP72_PCI_ID: enable = true; break; + default: + break; } snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 7bcb76676a98..063aa41dd237 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -76,6 +76,12 @@ #define ACP70_PCI_ID 0x70 #define ACP71_PCI_ID 0x71 #define ACP72_PCI_ID 0x72 +#define ACP7B_PCI_ID 0x7B +#define ACP7F_PCI_ID 0x7F + +#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F +#define ACP7X_PGFSM_STATUS_MASK 0xFFF +#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET #define HOST_BRIDGE_CZN 0x1630 #define HOST_BRIDGE_VGH 0x1645 @@ -344,6 +350,9 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_acp70_ops; int sof_acp70_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_acp7x_ops; +int sof_acp7x_ops_init(struct snd_sof_dev *sdev); + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c new file mode 100644 index 000000000000..87c70014d777 --- /dev/null +++ b/sound/soc/sof/amd/acp7x.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2025 Advanced Micro Devices, Inc. +// +// Authors: Vijendar Mukunda + +/* + * Hardware interface for Audio DSP on ACP7.B/7.F platforms + */ + +#include +#include +#include +#include + +#include "../ops.h" +#include "../sof-audio.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_TDM0_INSTANCE 0 +#define I2S_TDM1_INSTANCE 1 +#define I2S_TDM2_INSTANCE 2 +#define PDM0_DMIC_INSTANCE 3 +#define PDM1_DMIC_INSTANCE 4 + +static struct snd_soc_dai_driver acp7x_sof_dai[] = { + [I2S_TDM0_INSTANCE] = { + .id = I2S_TDM0_INSTANCE, + .name = "acp-sof-i2s0", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_TDM1_INSTANCE] = { + .id = I2S_TDM1_INSTANCE, + .name = "acp-sof-i2s1", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_TDM2_INSTANCE] = { + .id = I2S_TDM2_INSTANCE, + .name = "acp-sof-i2s2", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM0_DMIC_INSTANCE] = { + .id = PDM0_DMIC_INSTANCE, + .name = "acp-sof-dmic0", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM1_DMIC_INSTANCE] = { + .id = PDM1_DMIC_INSTANCE, + .name = "acp-sof-dmic1", + .capture = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 96000, + }, + }, +}; + +struct snd_sof_dsp_ops sof_acp7x_ops; +EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); + +int sof_acp7x_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); + + sof_acp7x_ops.drv = acp7x_sof_dai; + sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); + + return 0; +} diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c new file mode 100644 index 000000000000..532e15313795 --- /dev/null +++ b/sound/soc/sof/amd/pci-acp7x.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda + +/* + * PCI interface for ACP7.B/7.F devices + */ + +#include +#include +#include +#include + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 +#define ACP7X_REG_START 0x1240000 +#define ACP7X_REG_END 0x125C000 + +static const struct sof_amd_acp_desc acp7x_chip_info = { + .name = "acp7x", + .pgfsm_base = ACP7X_PGFSM_BASE, + .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, + .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, + .ext_intr_stat = ACP7X_EXT_INTR_STAT, + .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, + .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, + .acp_error_stat = ACP7X_ERROR_STATUS, + .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, + .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, + .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, + .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, + .reg_start_addr = ACP7X_REG_START, + .reg_end_addr = ACP7X_REG_END, +}; + +static const struct sof_dev_desc acp7x_desc = { + .machines = snd_soc_acpi_amd_acp7x_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &acp7x_chip_info, + .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), + .ipc_default = SOF_IPC_TYPE_3, + .default_fw_path = { + [SOF_IPC_TYPE_3] = "amd/sof", + }, + .default_tplg_path = { + [SOF_IPC_TYPE_3] = "amd/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC_TYPE_3] = "sof-acp7x.ri", + }, + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_acp7x_ops, + .ops_init = sof_acp7x_ops_init, +}; + +static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + unsigned int flag; + + switch (pci->revision) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + break; + default: + return -ENODEV; + } + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + return sof_pci_probe(pci, pci_id); +} + +static void acp7x_pci_remove(struct pci_dev *pci) +{ + sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id acp7x_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&acp7x_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_acp7x_driver = { + .name = KBUILD_MODNAME, + .id_table = acp7x_pci_ids, + .probe = acp7x_pci_probe, + .remove = acp7x_pci_remove, + .driver = { + .pm = pm_ptr(&sof_pci_pm), + }, +}; +module_pci_driver(snd_sof_pci_amd_acp7x_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("ACP7X SOF Driver"); +MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); +MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); From 237efba38c4f93d049299eca45b6ab9a643331d7 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:04 +0530 Subject: [PATCH 092/791] ASoC: SOF: amd: mask ACP7x PGFSM status poll For ACP7.B/7.F, poll only the PGFSM tile status bits (P0-P4) and consider the tiles powered on when the masked status becomes 0. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index bb3766aacf0e..f7b22f9979f6 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -577,6 +577,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) unsigned int base = desc->pgfsm_base; unsigned int val; unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; + bool use_masked_status = false; int ret; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); @@ -605,6 +606,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) case ACP7F_PCI_ID: acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; + use_masked_status = true; break; default: return -EINVAL; @@ -614,9 +616,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, acp_pgfsm_cntl_mask); - ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, - !val, ACP_REG_POLL_INTERVAL, - ACP_REG_POLL_TIMEOUT_US); + if (use_masked_status) + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, + base + PGFSM_STATUS_OFFSET, val, + !(val & acp_pgfsm_status_mask), + ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + else + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, + base + PGFSM_STATUS_OFFSET, val, + !val, ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); if (ret < 0) dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); From 571464f543f681f7b6f0014adc9fb18a120785e9 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:05 +0530 Subject: [PATCH 093/791] ASoC: SOF: amd: refactor SW1 I2S error reason clear in acp_irq_handler Replace the open-coded pci_rev comparison with a switch statement for clearing ACP_SW1_I2S_ERROR_REASON. This makes the per-platform control explicit and easier to extend. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-5-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index f7b22f9979f6..1f0fc052ea1a 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -540,8 +540,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ - if (adata->pci_rev >= ACP_RMB_PCI_ID) + switch (adata->pci_rev) { + case ACP_RMB_PCI_ID: + case ACP63_PCI_ID: + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); + break; + default: + break; + } snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); irq_flag = 1; } From 78d99c2c5eee0f6cd1adfe33dd0c266baac50fb8 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:06 +0530 Subject: [PATCH 094/791] ASoC: SOF: amd: add ACP7x probe and remove Add amd_sof_acp7x_probe() and amd_sof_acp7x_remove() for ACP7.B/7.F. Wire probe and remove into sof_acp7x_ops_init(). Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-6-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 102 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.h | 4 ++ sound/soc/sof/amd/acp7x.c | 2 + 3 files changed, 108 insertions(+) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 1f0fc052ea1a..b2c319857ad8 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -12,6 +12,7 @@ * Hardware interface for generic AMD ACP processor */ +#include #include #include #include @@ -1030,6 +1031,107 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); +int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + const struct sof_amd_acp_desc *chip; + const union acpi_object *obj; + struct acpi_device *adev; + unsigned int addr; + int ret; + + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); + return -EIO; + } + adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); + if (!adata) + return -ENOMEM; + + adata->dev = sdev; + adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(adata->dmic_dev)) { + dev_err(sdev->dev, "failed to register platform for dmic codec\n"); + return PTR_ERR(adata->dmic_dev); + } + + addr = pci_resource_start(pci, ACP_DSP_BAR); + sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); + if (!sdev->bar[ACP_DSP_BAR]) { + dev_err(sdev->dev, "ioremap error\n"); + ret = -ENXIO; + goto unregister_dev; + } + + pci_set_master(pci); + adata->addr = addr; + adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; + adata->pci_rev = pci->revision; + mutex_init(&adata->acp_lock); + sdev->pdata->hw_pdata = adata; + + ret = acp_init(sdev); + if (ret < 0) + goto unregister_dev; + + adev = ACPI_COMPANION(&pci->dev); + + if (adev) { + if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", + ACPI_TYPE_INTEGER, &obj)) + adata->acp_sof_signed_firmware_image = obj->integer.value; + } + + sdev->dsp_box.offset = 0; + sdev->dsp_box.size = BOX_SIZE_512; + + sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; + sdev->host_box.size = BOX_SIZE_512; + + sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; + sdev->debug_box.size = BOX_SIZE_1024; + + if (adata->acp_sof_signed_firmware_image) { + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-code.bin", chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto unregister_dev; + } + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-data.bin", chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto unregister_dev; + } + } + + adata->enable_fw_debug = enable_fw_debug; + acp_memory_init(sdev); + acp_dsp_stream_init(sdev); + + return 0; + +unregister_dev: + platform_device_unregister(adata->dmic_dev); + return ret; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); + +void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + if (adata->dmic_dev) + platform_device_unregister(adata->dmic_dev); + + acp_reset(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("AMD ACP sof driver"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 063aa41dd237..46b946132de8 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -277,6 +277,7 @@ struct acp_dev_data { /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ bool acp70_sdw1_wake_event; unsigned int pci_rev; + int acp_sof_signed_firmware_image; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -353,6 +354,9 @@ int sof_acp70_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_acp7x_ops; int sof_acp7x_ops_init(struct snd_sof_dev *sdev); +int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); +void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c index 87c70014d777..3366e1b638af 100644 --- a/sound/soc/sof/amd/acp7x.c +++ b/sound/soc/sof/amd/acp7x.c @@ -137,6 +137,8 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev) sof_acp7x_ops.drv = acp7x_sof_dai; sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); + sof_acp7x_ops.probe = amd_sof_acp7x_probe; + sof_acp7x_ops.remove = amd_sof_acp7x_remove; return 0; } From a3674345f90226758b1dfd55c594cc3d4cdf80f6 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:07 +0530 Subject: [PATCH 095/791] ASoC: SOF: amd: add ACP7x IRQ handler for DSP IPC Add acp7x_irq_handler() and register it from amd_sof_acp7x_probe() for DSP doorbell IPC interrupts on ACP7.B/7.F. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-7-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 49 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index b2c319857ad8..5df984b93dec 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -580,6 +580,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) +{ + struct snd_sof_dev *sdev = dev_id; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + unsigned int base = desc->dsp_intr_base; + unsigned int val; + unsigned int ext_intr_stat; + int irq_flag = 0; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); + if (val & ACP_DSP_TO_HOST_IRQ) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, + ACP_DSP_TO_HOST_IRQ); + return IRQ_WAKE_THREAD; + } + + ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); + if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); + irq_flag = 1; + } + + if (irq_flag) + return IRQ_HANDLED; + + return IRQ_NONE; +} + static int acp_power_on(struct snd_sof_dev *sdev) { const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); @@ -1039,6 +1068,7 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) const union acpi_object *obj; struct acpi_device *adev; unsigned int addr; + unsigned int irqflags; int ret; chip = get_chip_info(sdev->pdata); @@ -1079,6 +1109,16 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) adev = ACPI_COMPANION(&pci->dev); + sdev->ipc_irq = pci->irq; + irqflags = IRQF_SHARED; + + ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, + irqflags, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); + goto unregister_dev; + } + if (adev) { if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", ACPI_TYPE_INTEGER, &obj)) @@ -1099,13 +1139,13 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) "sof-%s-code.bin", chip->name); if (!adata->fw_code_bin) { ret = -ENOMEM; - goto unregister_dev; + goto free_ipc_irq; } adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, "sof-%s-data.bin", chip->name); if (!adata->fw_data_bin) { ret = -ENOMEM; - goto unregister_dev; + goto free_ipc_irq; } } @@ -1115,6 +1155,8 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) return 0; +free_ipc_irq: + free_irq(sdev->ipc_irq, sdev); unregister_dev: platform_device_unregister(adata->dmic_dev); return ret; @@ -1125,6 +1167,9 @@ void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) { struct acp_dev_data *adata = sdev->pdata->hw_pdata; + if (sdev->ipc_irq) + free_irq(sdev->ipc_irq, sdev); + if (adata->dmic_dev) platform_device_unregister(adata->dmic_dev); From cbdafd2acdee72f698edd4ab9429bf1f7613ff94 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:08 +0530 Subject: [PATCH 096/791] ASoC: SOF: amd: extend signed firmware pre-run for ACP7x Parse SizeFWSigned from the ACP image header when loading signed firmware on ACP7.B/7.F platforms. Keep the legacy ACP_FIRMWARE_SIGNATURE subtraction for pre-7B platforms using quirks. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-8-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 14 ++++++++++++-- sound/soc/sof/amd/acp.h | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 98324bbade15..af04f053fd65 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "../ops.h" #include "acp-dsp-offset.h" @@ -173,10 +174,19 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) adata = sdev->pdata->hw_pdata; - if (adata->quirks && adata->quirks->signed_fw_image) + if (adata->pci_rev >= ACP7B_PCI_ID) { + if (adata->acp_sof_signed_firmware_image) { + size_fw = get_unaligned_le32(adata->bin_buf + + ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); + size_fw += ACP_IMAGE_HEADER_SIZE; + } else { + size_fw = adata->fw_bin_size; + } + } else if (adata->quirks && adata->quirks->signed_fw_image) { size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; - else + } else { size_fw = adata->fw_bin_size; + } page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; adata->fw_bin_page_count = page_count; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 46b946132de8..c2f3add05d0c 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -113,6 +113,9 @@ #define PROBE_STATUS_BIT BIT(31) #define ACP_FIRMWARE_SIGNATURE 0x100 +#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE +#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 + #define ACP_ERROR_IRQ_MASK BIT(29) #define ACP_SDW0_IRQ_MASK BIT(21) #define ACP_SDW1_IRQ_MASK BIT(2) From d6869ae07d21acc6bab7aa0bc068c1c74b03926d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:09 +0530 Subject: [PATCH 097/791] ASoC: SOF: amd: require full ACP header for ACP7 signed firmware ACP7.B/7.F signed images read SizeFWSigned from a fixed offset inside the ACP header. Reject firmware buffers shorter than the header so we never read past the end of the supplied image. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-9-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index af04f053fd65..e4a17e6656c0 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -176,6 +176,11 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) if (adata->pci_rev >= ACP7B_PCI_ID) { if (adata->acp_sof_signed_firmware_image) { + if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { + dev_err(sdev->dev, "Invalid signed firmware size %u\n", + adata->fw_bin_size); + return -EINVAL; + } size_fw = get_unaligned_le32(adata->bin_buf + ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); size_fw += ACP_IMAGE_HEADER_SIZE; From c04de2d88c0b2807b71480916a10adea83c85f02 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:10 +0530 Subject: [PATCH 098/791] ASoC: SOF: amd: validate SizeFWSigned before signed FW length on ACP7x The ACP7.B/7.F ACPI signed path already reads SizeFWSigned from the image header into size_fw. Before adding ACP_IMAGE_HEADER_SIZE for SHA DMA, reject payload size zero or any size_fw with size_fw > fw_bin_size - ACP_IMAGE_HEADER_SIZE, so size_fw + ACP_IMAGE_HEADER_SIZE cannot exceed the supplied firmware buffer. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-10-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index e4a17e6656c0..76335c78255f 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -183,6 +183,13 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) } size_fw = get_unaligned_le32(adata->bin_buf + ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); + if (!size_fw || + size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { + dev_err(sdev->dev, + "Invalid signed firmware payload size %u (max %u)\n", + size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); + return -EINVAL; + } size_fw += ACP_IMAGE_HEADER_SIZE; } else { size_fw = adata->fw_bin_size; From 7a81f54a0483edc6cbdb2fb066ccf2dc3e258ac7 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:11 +0530 Subject: [PATCH 099/791] ASoC: SOF: amd: add post-firmware-run delay for ACP7x Add sof_acp7x_post_fw_run_delay() to introduce a small delay after firmware boot completion on resume to avoid DSP entering an unrecoverable state. Register it as post_fw_run callback only when the ACPI property acp-sof-post_fw_run_delay is set, following the same pattern used by the Vangogh platform. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-11-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp7x.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c index 3366e1b638af..c1b9c623bc39 100644 --- a/sound/soc/sof/amd/acp7x.c +++ b/sound/soc/sof/amd/acp7x.c @@ -11,6 +11,7 @@ * Hardware interface for Audio DSP on ACP7.B/7.F platforms */ +#include #include #include #include @@ -127,11 +128,29 @@ static struct snd_soc_dai_driver acp7x_sof_dai[] = { }, }; +static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) +{ + /* + * Resuming from suspend in some cases may cause the DSP firmware + * to enter an unrecoverable faulty state. Delaying a bit any host + * to DSP transmission right after firmware boot completion seems + * to resolve the issue. + */ + if (!sdev->first_boot) + usleep_range(100, 150); + + return 0; +} + struct snd_sof_dsp_ops sof_acp7x_ops; EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); int sof_acp7x_ops_init(struct snd_sof_dev *sdev) { + struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); + const union acpi_object *obj; + int acp_sof_post_fw_run_delay = 0; + /* common defaults */ memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); @@ -140,5 +159,14 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev) sof_acp7x_ops.probe = amd_sof_acp7x_probe; sof_acp7x_ops.remove = amd_sof_acp7x_remove; + if (adev) { + if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", + ACPI_TYPE_INTEGER, &obj)) + acp_sof_post_fw_run_delay = obj->integer.value; + } + + if (acp_sof_post_fw_run_delay) + sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; + return 0; } From d0c32fdc1907b95e57b26467052f5f4bea9f4273 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:12 +0530 Subject: [PATCH 100/791] ASoC: SOF: amd: extend configure_and_run_sha_dma for ACPI signed FW flag Check adata->acp_sof_signed_firmware_image alongside the existing quirk flag so that ACP7.B/7.F platforms configured through ACPI also get the SHA DMA header included during signed firmware loading. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-12-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 5df984b93dec..c2143821fb83 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -288,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } - if (adata->quirks && adata->quirks->signed_fw_image) + if ((adata->quirks && adata->quirks->signed_fw_image) || + adata->acp_sof_signed_firmware_image) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); From 7af6ac41cd470b04ebd1d2ee972159f2dd5be7cd Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:13 +0530 Subject: [PATCH 101/791] ASoC: SOF: amd: wire signed firmware load callback for ACP7x via ACPI Read ACPI property acp-sof-signed-firmware-image in sof_acp7x_ops_init() and register acp_sof_load_signed_firmware as the load_firmware callback only when the property is set. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-13-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp7x.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c index c1b9c623bc39..94b7ea08b309 100644 --- a/sound/soc/sof/amd/acp7x.c +++ b/sound/soc/sof/amd/acp7x.c @@ -149,6 +149,7 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev) { struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); const union acpi_object *obj; + int acp_sof_signed_firmware_image = 0; int acp_sof_post_fw_run_delay = 0; /* common defaults */ @@ -160,11 +161,18 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev) sof_acp7x_ops.remove = amd_sof_acp7x_remove; if (adev) { + if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", + ACPI_TYPE_INTEGER, &obj)) + acp_sof_signed_firmware_image = obj->integer.value; + if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", ACPI_TYPE_INTEGER, &obj)) acp_sof_post_fw_run_delay = obj->integer.value; } + if (acp_sof_signed_firmware_image) + sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; + if (acp_sof_post_fw_run_delay) sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; From deb631c3f387d0530a7c10de5e71b8c6cbe8c732 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:14 +0530 Subject: [PATCH 102/791] ASoC: SOF: amd: load ACP7.B/7.F signed data firmware to SRAM ACP7.B and ACP7.F signed firmware data blocks must be written to SRAM instead of DRAM. Select SOF_FW_BLK_TYPE_SRAM for PCI revision 0x7B and above in acp_sof_load_signed_firmware(). Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-14-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 76335c78255f..f1ec85694323 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -334,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) } kfree(fw_filename); - ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, - (void *)adata->fw_dbin->data, - adata->fw_dbin->size); + if (adata->pci_rev >= ACP7B_PCI_ID) + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); + else + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); return ret; } EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); From 08c5e98b7b5ff5aa0c2774bf58a5a71e2741f603 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:15 +0530 Subject: [PATCH 103/791] ASoC: SOF: amd: add ACP I2S format field and topology token Add format field to sof_ipc_dai_acp_params for ACP I2S format selection. Add SOF_TKN_AMD_ACPI2S_FORMAT (1703) to the existing SOF_ACPI2S_TOKENS tuple and wire it into acpi2s_tokens[] so integrators continue using the same ACPI2S token group as earlier ACP I2S topologies, not a separate ACPTDM-specific token set. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-15-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai-amd.h | 1 + include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/ipc3-topology.c | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 59cd014392c1..e2107da6558f 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { uint32_t fsync_rate; /* FSYNC frequency in Hz */ uint32_t tdm_slots; uint32_t tdm_mode; + uint32_t format; } __packed; /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index f4a7baadb44d..cc694a397987 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -223,6 +223,7 @@ #define SOF_TKN_AMD_ACPI2S_RATE 1700 #define SOF_TKN_AMD_ACPI2S_CH 1701 #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 +#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 /* MICFIL PDM */ #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 4e066bbded91..9eb8335a3c07 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, }; -/* ACPI2S */ +/* + * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM + * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. + */ static const struct sof_topology_token acpi2s_tokens[] = { {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, @@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, + {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, format)}, }; /* MICFIL PDM */ From 3805b8e6f932fbf9bfe5803b6a85328cc468b75d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:16 +0530 Subject: [PATCH 104/791] ASoC: SOF: amd: add ACP7x I2S DAI type and topology support Add SOF_DAI_AMD_I2S DAI type for ACP7.B/7.F I2S/TDM interfaces. Register the ACPTDM topology DAI name and map it to SOF_DAI_AMD_I2S; IPC3 continues to parse ACP I2S link parameters through SOF_ACPI2S_TOKENS (including the format token from the prior commit), not a new token group named after ACPTDM. Add sof_link_acp_i2s_load() and the SOF_DAI_AMD_I2S PCM dai link fixup path. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-16-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 2 ++ sound/soc/sof/ipc3-pcm.c | 6 ++++++ sound/soc/sof/ipc3-topology.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 3 ++- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 0b6a6ba6489a..e3fe492e78f5 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -91,6 +91,7 @@ enum sof_ipc_dai_type { SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ + SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ }; /* general purpose DAI configuration */ @@ -122,6 +123,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; struct sof_ipc_dai_acp_sdw_params acp_sdw; + struct sof_ipc_dai_acp_params acp_i2s; }; } __packed; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 90ef5d99f626..143bf0fe8dd9 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_I2S: + rate->min = private->dai_config->acp_i2s.fsync_rate; + rate->max = private->dai_config->acp_i2s.fsync_rate; + channels->min = private->dai_config->acp_i2s.tdm_slots; + channels->max = private->dai_config->acp_i2s.tdm_slots; + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 9eb8335a3c07..26d85ca9be26 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1368,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof return 0; } +static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); + config->hdr.size = size; + + ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1697,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_AMD_SDW: ret = sof_link_acp_sdw_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_I2S: + ret = sof_link_acp_i2s_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6de8a6c1c127..6fd69ba11c41 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, {"ACP_SDW", SOF_DAI_AMD_SDW}, - + {"ACPTDM", SOF_DAI_AMD_I2S}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -1994,6 +1994,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_I2S: token_id = SOF_ACPI2S_TOKENS; num_tuples += token_list[SOF_ACPI2S_TOKENS].count; break; From 1c9646f3180ea0256b4f93d9585cdde1f1bac65d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:17 +0530 Subject: [PATCH 105/791] ASoC: SOF: amd: add system and runtime PM ops for ACP7x Add amd_sof_acp7x_suspend() and amd_sof_acp7x_resume() for ACP7.B/7.F platforms power management. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-17-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 71 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.h | 4 +++ sound/soc/sof/amd/acp7x.c | 5 +++ 3 files changed, 80 insertions(+) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index c2143821fb83..f89ad86260b4 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -1178,6 +1178,77 @@ void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); +int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) +{ + struct acp_dev_data *acp_data; + int ret; + bool enable = false; + + acp_data = sdev->pdata->hw_pdata; + + ret = acp_reset(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Reset failed\n"); + return ret; + } + switch (acp_data->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + enable = true; + break; + default: + break; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); + + return 0; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + int ret; + + acp_data = sdev->pdata->hw_pdata; + + ret = acp_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Init failed\n"); + return ret; + } + ret = acp_memory_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Memory init failed\n"); + return ret; + } + + switch (acp_data->pci_rev) { + case ACP7B_PCI_ID: + case ACP7F_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) +{ + return amd_sof_acp7x_suspend(sdev, 0); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); + +int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) +{ + return amd_sof_acp7x_resume(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("AMD ACP sof driver"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index c2f3add05d0c..16d66b2eaa70 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -359,6 +359,10 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev); int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); +int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); +int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); +int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); +int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); /* Machine configuration */ diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c index 94b7ea08b309..b6722d11168c 100644 --- a/sound/soc/sof/amd/acp7x.c +++ b/sound/soc/sof/amd/acp7x.c @@ -176,5 +176,10 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev) if (acp_sof_post_fw_run_delay) sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; + sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; + sof_acp7x_ops.resume = amd_sof_acp7x_resume; + sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; + sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; + return 0; } From a118fea777a2c04da7c5ccf1141d317838a79a46 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:27:12 +0800 Subject: [PATCH 106/791] ASoC: codecs: cs42l42-sdw: Propagate regcache_sync() errors cs42l42_sdw_runtime_resume() first syncs LATCH_TO_VP and then syncs the full regmap, but currently ignores both return values and can report success after a failed replay. Check both sync operations, return the first error, and restore cache- only/dirty state on failure. Signed-off-by: Pengpeng Hou Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260704072712.91035-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42-sdw.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index b8256ce0b8fb..f1efbc2b532b 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -482,10 +482,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) regcache_cache_only(cs42l42->regmap, false); /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ - regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); - regcache_sync(cs42l42->regmap); + ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + if (ret) + goto err_sync; + + ret = regcache_sync(cs42l42->regmap); + if (ret) + goto err_sync; return 0; + +err_sync: + regcache_cache_only(cs42l42->regmap, true); + regcache_mark_dirty(cs42l42->regmap); + return ret; } static int cs42l42_sdw_resume(struct device *dev) From e074c12c428c633e079154301207a6079a208583 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:15:52 +0800 Subject: [PATCH 107/791] ASoC: cs35l33: drain threaded IRQ before runtime suspend cs35l33_runtime_suspend() currently switches the codec into regcache_cache_only(true) and powers it down without first quiescing the threaded IRQ registered by devm_request_threaded_irq(). That leaves a window where cs35l33_irq_thread() can still run after suspend has closed off live register access. A running system can reach this during runtime PM while the driver still has critical fault IRQs unmasked. If the threaded handler runs in that window, it reads volatile INT_STATUS_1/2 after cache_only has been enabled, ignores the regmap_read() failures, and can still drive the AMP_SHORT_RLS, CAL_ERR_RLS, OTE_RLS, and OTW_RLS release paths. Use disable_irq() before entering cache_only/power-off so any in-flight threaded handler is drained and no new IRQ thread can run during the suspended state. Re-enable the IRQ only after runtime_resume() has restored live register access with regcache_sync(). Since probe only warns if devm_request_threaded_irq() fails, track whether the IRQ was actually installed before disabling or re-enabling it. Fixes: 3333cb7187b9 ("ASoC: cs35l33: Initial commit of the cs35l33 CODEC driver.") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260611161553.3378721-2-runyu.xiao@seu.edu.cn Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l33.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index f49edb9ea1f2..e217da124549 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -40,6 +40,7 @@ struct cs35l33_private { struct regmap *regmap; struct gpio_desc *reset_gpio; bool amp_cal; + bool irq_requested; int mclk_int; struct regulator_bulk_data core_supplies[2]; int num_core_supplies; @@ -881,6 +882,9 @@ static int cs35l33_runtime_resume(struct device *dev) goto err; } + if (cs35l33->irq_requested) + enable_irq(to_i2c_client(dev)->irq); + return 0; err: @@ -900,6 +904,10 @@ static int cs35l33_runtime_suspend(struct device *dev) /* redo the calibration in next power up */ cs35l33->amp_cal = false; + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l33->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l33->regmap, true); regcache_mark_dirty(cs35l33->regmap); regulator_bulk_disable(cs35l33->num_core_supplies, @@ -1154,10 +1162,12 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l33", cs35l33); + cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l33", cs35l33); if (ret != 0) dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l33->irq_requested = true; /* We could issue !RST or skip it based on AMP topology */ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, From 4105a4c0678b2808fc8046b60321b4f1cc7dae75 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:15:53 +0800 Subject: [PATCH 108/791] ASoC: cs35l34: drain threaded IRQ before runtime suspend cs35l34_runtime_suspend() currently switches the codec into regcache_cache_only(true), asserts reset low, and powers the device off without first quiescing the threaded IRQ registered by devm_request_threaded_irq(). That leaves a window where cs35l34_irq_thread() can still run after suspend has removed live hardware access. A running system can reach this during runtime PM while the driver still has critical fault IRQs unmasked. If the threaded handler runs in that window, it reads volatile INT_STATUS_1..4 after cache_only has been enabled, ignores the regmap_read() failures, and can still execute the PROT_RELEASE_CTL release sequence or the BST fault power-down writes. Use disable_irq() before entering cache_only/reset-low/power-off so any in-flight threaded handler is drained and no new IRQ thread can run while the device is suspended. Re-enable the IRQ only after runtime_resume() has restored live register access with regcache_sync(). Since probe only logs request_threaded_irq() failures and keeps going, track whether the IRQ was actually installed before disabling or re-enabling it. Fixes: c1124c09e103 ("ASoC: cs35l34: Initial commit of the cs35l34 CODEC driver.") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260611161553.3378721-3-runyu.xiao@seu.edu.cn Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l34.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index e80984b22159..3f265bd760f7 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -45,6 +45,7 @@ struct cs35l34_private { int num_core_supplies; int mclk_int; bool tdm_mode; + bool irq_requested; struct gpio_desc *reset_gpio; /* Active-low reset GPIO */ }; @@ -1032,10 +1033,12 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l34", cs35l34); + cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l34", cs35l34); if (ret != 0) dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l34->irq_requested = true; cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW); @@ -1140,6 +1143,9 @@ static int cs35l34_runtime_resume(struct device *dev) dev_err(dev, "Failed to restore register cache\n"); goto err; } + + if (cs35l34->irq_requested) + enable_irq(to_i2c_client(dev)->irq); return 0; err: regcache_cache_only(cs35l34->regmap, true); @@ -1153,6 +1159,10 @@ static int cs35l34_runtime_suspend(struct device *dev) { struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l34->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l34->regmap, true); regcache_mark_dirty(cs35l34->regmap); From 6943ea99422ed840ee15dc546db1cdbd0b325620 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:39:00 +0530 Subject: [PATCH 109/791] ASoC: SOF: amd: add missing blank line between Kconfig entries Add a missing blank line between SND_SOC_SOF_AMD_ACP70 and SND_SOC_SOF_AMD_ACP7X config entries to conform to Kconfig formatting convention. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20260707060918.2535962-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 7413bfdb0943..903e7ec3b3ba 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -103,6 +103,7 @@ config SND_SOC_SOF_AMD_ACP70 Select this option for SOF support on AMD ACP7.0/ACP7.1 version based platforms. Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. + config SND_SOC_SOF_AMD_ACP7X tristate "SOF support for ACP7.B/7.F platforms" depends on SND_SOC_SOF_PCI From f16eaa38ea640884f66d24865c770c3f6c43bd42 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Tue, 7 Jul 2026 10:11:39 +0800 Subject: [PATCH 110/791] ALSA: hda/core: add cleanup in snd_hdac_bus_alloc_stream_pages() The current error handling in snd_hdac_bus_alloc_stream_pages() returns directly on failure without cleaning up already allocated resources. While callers are supposed to release those via snd_hdac_bus_free_stream_pages() at destructor, adding explicit cleanup makes the function more self-contained and safer against future misuse. Add proper error cleanup path using goto labels to free previously allocated BDL DMA buffers, position buffer, and ring buffer in reverse allocation order. Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_8E5BBBD19D53B1EFCDB6E89F3B6246A70B06@qq.com Signed-off-by: Takashi Iwai --- sound/hda/core/controller.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index 6312ad7af71d..32048ef32d96 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -719,7 +719,7 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) BDL_SIZE, &s->bdl); num_streams++; if (err < 0) - return -ENOMEM; + goto error_bdl; } if (WARN_ON(!num_streams)) @@ -728,12 +728,24 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) err = snd_dma_alloc_pages(dma_type, bus->dev, num_streams * 8, &bus->posbuf); if (err < 0) - return -ENOMEM; + goto error_bdl; list_for_each_entry(s, &bus->stream_list, list) s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb); + err = snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb); + if (err < 0) + goto error_posbuf; + return 0; + +error_posbuf: + snd_dma_free_pages(&bus->posbuf); +error_bdl: + list_for_each_entry(s, &bus->stream_list, list) { + if (s->bdl.area) + snd_dma_free_pages(&s->bdl); + } + return -ENOMEM; } EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); From cd3447e1b6425efd1704ed07f1f245c842927eb0 Mon Sep 17 00:00:00 2001 From: Evgenii Burenchev Date: Mon, 6 Jul 2026 16:16:34 +0300 Subject: [PATCH 111/791] ALSA: via82xx: Remove unreachable branch in snd_via686_pcm_pointer() The condition if (count && size < count) can never evaluate to true. The VIA DMA count register is masked with 0x00ffffff before use, while the DMA buffer size is limited to 0x00fffffe bytes. As a result, 'count' can never exceed 'size', making the condition permanently false. This branch has therefore been unreachable since the driver was introduced. Remove the unreachable branch without changing runtime behavior. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Evgenii Burenchev Link: https://patch.msgid.link/20260706131638.15311-1-evg28bur@yandex.ru Signed-off-by: Takashi Iwai --- sound/pci/via82xx_modem.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 9b84d3fb9eaf..b32f84ac17cc 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -573,24 +573,18 @@ static inline unsigned int calc_linear_pos(struct via82xx_modem *chip, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count); #endif - if (count && size < count) { + if (! count) + /* bogus count 0 on the DMA boundary? */ + res = viadev->idx_table[idx].offset; + else + /* count register returns full size + * when end of buffer is reached + */ + res = viadev->idx_table[idx].offset + size; + if (check_invalid_pos(viadev, res)) { dev_dbg(chip->card->dev, - "invalid via82xx_cur_ptr, using last valid pointer\n"); + "invalid via82xx_cur_ptr (2), using last valid pointer\n"); res = viadev->lastpos; - } else { - if (! count) - /* bogus count 0 on the DMA boundary? */ - res = viadev->idx_table[idx].offset; - else - /* count register returns full size - * when end of buffer is reached - */ - res = viadev->idx_table[idx].offset + size; - if (check_invalid_pos(viadev, res)) { - dev_dbg(chip->card->dev, - "invalid via82xx_cur_ptr (2), using last valid pointer\n"); - res = viadev->lastpos; - } } } viadev->lastpos = res; /* remember the last position */ From 5171dddf10f8ebd62e2eb6dfea40d770f6d70e5f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 14:45:56 +0700 Subject: [PATCH 112/791] ALSA: core: use ARRAY_SIZE() in snd_minor_info_read() Use ARRAY_SIZE(snd_minors) instead of SNDRV_OS_MINORS directly, for consistency with snd_find_free_minor() and snd_unregister_device() in the same file. No functional change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260707074556.569451-1-phucduc.bui@gmail.com Signed-off-by: Takashi Iwai --- sound/core/sound.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/sound.c b/sound/core/sound.c index 3e1969a52b21..17c380d8d8be 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -358,7 +358,7 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu struct snd_minor *mptr; guard(mutex)(&sound_mutex); - for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { + for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { mptr = snd_minors[minor]; if (!mptr) continue; From bf08a5f698dcb8495efd94f14d863a8e6b123d25 Mon Sep 17 00:00:00 2001 From: Yu-Hsuan Hsu Date: Sun, 5 Jul 2026 12:59:30 +0000 Subject: [PATCH 113/791] ALSA: aloop: Add 'hrtimer' option to timer_source The snd-aloop driver currently defaults to using the system jiffies timer (struct timer_list). On systems configured with a low timer interrupt frequency (e.g., CONFIG_HZ=250), the jiffies resolution (4ms per tick) is insufficient for precise audio timing. For example, a 10ms audio period requires 2.5 jiffies ticks, causing timing jitter that leads to capture underruns. Introduce "hrtimer" as a supported timer_source option. When timer_source="hrtimer" is set, aloop uses high-resolution timers (hrtimer) to drive period updates. This provides nanosecond-level accuracy regardless of CONFIG_HZ and operates independently of other hardware audio cards. Signed-off-by: Yu-Hsuan Hsu Link: https://patch.msgid.link/20260705125941.1203871-1-yuhsuan@chromium.org Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 118 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 06bfe09eae1a..236f49a7fb8b 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); module_param_array(pcm_notify, int, NULL, 0444); MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); module_param_array(timer_source, charp, NULL, 0444); -MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default]."); +MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default], 'hrtimer' for high-resolution timer."); #define NO_PITCH 100000 @@ -161,8 +162,11 @@ struct loopback_pcm { unsigned int period_size_frac; /* period size in jiffies ticks */ unsigned int last_drift; unsigned long last_jiffies; - /* If jiffies timer is used */ + /* If jiffies / hrtimer is used */ struct timer_list timer; +#ifdef CONFIG_HIGH_RES_TIMERS + struct hrtimer hrtimer; +#endif /* size of per channel buffer in case of non-interleaved access */ unsigned int channel_buf_n; @@ -232,6 +236,31 @@ static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +/* call in cable->lock */ +static int loopback_hrtimer_start(struct loopback_pcm *dpcm) +{ + unsigned long tick; + unsigned int rate_shift = get_rate_shift(dpcm); + + if (rate_shift != dpcm->pcm_rate_shift) { + dpcm->pcm_rate_shift = rate_shift; + dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); + } + if (dpcm->period_size_frac <= dpcm->irq_pos) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + tick = dpcm->period_size_frac - dpcm->irq_pos; + tick = DIV_ROUND_UP(tick, dpcm->pcm_bps); + hrtimer_start(&dpcm->hrtimer, + ns_to_ktime(div_u64((u64)tick * NSEC_PER_SEC, HZ)), + HRTIMER_MODE_REL_SOFT); + + return 0; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_start(struct loopback_pcm *dpcm) { @@ -270,6 +299,16 @@ static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +/* call in cable->lock */ +static inline int loopback_hrtimer_stop(struct loopback_pcm *dpcm) +{ + hrtimer_cancel(&dpcm->hrtimer); + + return 0; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_stop(struct loopback_pcm *dpcm) { @@ -300,6 +339,15 @@ static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +static inline int loopback_hrtimer_stop_sync(struct loopback_pcm *dpcm) +{ + hrtimer_cancel(&dpcm->hrtimer); + + return 0; +} +#endif + /* call in loopback->cable_lock */ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) { @@ -734,6 +782,30 @@ static void loopback_jiffies_timer_function(struct timer_list *t) snd_pcm_period_elapsed(dpcm->substream); } +#ifdef CONFIG_HIGH_RES_TIMERS +static enum hrtimer_restart loopback_hrtimer_function(struct hrtimer *t) +{ + struct loopback_pcm *dpcm = container_of(t, struct loopback_pcm, hrtimer); + bool period_elapsed = false; + + scoped_guard(spinlock_irqsave, &dpcm->cable->lock) { + if (loopback_jiffies_timer_pos_update(dpcm->cable) & + (1 << dpcm->substream->stream)) { + loopback_hrtimer_start(dpcm); + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + period_elapsed = true; + } + } + } + + if (period_elapsed) + snd_pcm_period_elapsed(dpcm->substream); + + return HRTIMER_NORESTART; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime, unsigned long resolution) @@ -907,6 +979,21 @@ static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm, snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); } +#ifdef CONFIG_HIGH_RES_TIMERS +static void loopback_hrtimer_dpcm_info(struct loopback_pcm *dpcm, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, " update_pending:\t%u\n", + dpcm->period_update_pending); + snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); + snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); + snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", + dpcm->last_jiffies, jiffies); + snd_iprintf(buffer, " timer_expires:\t%llu\n", + ktime_to_ns(hrtimer_get_expires(&dpcm->hrtimer))); +} +#endif + static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm, struct snd_info_buffer *buffer) { @@ -1097,6 +1184,26 @@ static const struct loopback_ops loopback_jiffies_timer_ops = { .dpcm_info = loopback_jiffies_timer_dpcm_info, }; +#ifdef CONFIG_HIGH_RES_TIMERS +static int loopback_hrtimer_open(struct loopback_pcm *dpcm) +{ + hrtimer_setup(&dpcm->hrtimer, loopback_hrtimer_function, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + + return 0; +} + +static const struct loopback_ops loopback_hrtimer_ops = { + .open = loopback_hrtimer_open, + .start = loopback_hrtimer_start, + .stop = loopback_hrtimer_stop, + .stop_sync = loopback_hrtimer_stop_sync, + .close_substream = loopback_hrtimer_stop_sync, + .pos_update = loopback_jiffies_timer_pos_update, + .dpcm_info = loopback_hrtimer_dpcm_info, +}; +#endif + static int loopback_parse_timer_id(const char *str, struct snd_timer_id *tid) { @@ -1274,7 +1381,12 @@ static int loopback_open(struct snd_pcm_substream *substream) spin_lock_init(&cable->lock); snd_refcount_init(&cable->stop_count); cable->hw = loopback_pcm_hardware; - if (loopback->timer_source) +#ifdef CONFIG_HIGH_RES_TIMERS + if (loopback->timer_source && !strcmp(loopback->timer_source, "hrtimer")) + cable->ops = &loopback_hrtimer_ops; + else +#endif + if (loopback->timer_source && loopback->timer_source[0]) cable->ops = &loopback_snd_timer_ops; else cable->ops = &loopback_jiffies_timer_ops; From b45f4e8d787cfc667d38658da1b7ffb05c8b78a7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:01 +0700 Subject: [PATCH 114/791] ALSA: control: Return -ENODEV instead of -EFAULT in snd_ctl_open() When try_module_get() fails in snd_ctl_open(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other unavailable-device paths in this function(the NULL card check and the snd_card_file_add() failure). This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-2-phucduc.bui@gmail.com --- sound/core/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/control.c b/sound/core/control.c index 037e455bb11b..0ca9fff56e51 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -68,7 +68,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) goto __error1; } if (!try_module_get(card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); From c49e88ea6cee4454c4bd849ac5e8abd2f6a7ac26 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:02 +0700 Subject: [PATCH 115/791] ALSA: hwdep: Return -ENODEV instead of -EFAULT in snd_hwdep_open() When try_module_get() fails in snd_hwdep_open(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other unavailable-device paths in this function(the NULL hw check and the card->shutdown check). This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-3-phucduc.bui@gmail.com --- sound/core/hwdep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 973d11732bfd..352047e0b33e 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -87,7 +87,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) if (!try_module_get(hw->card->module)) { snd_card_unref(hw->card); - return -EFAULT; + return -ENODEV; } init_waitqueue_entry(&wait, current); From 52f15bf51b6c61f03c51e9acfa38145ee6c7c07a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:03 +0700 Subject: [PATCH 116/791] ALSA: info: Return -ENODEV instead of -EFAULT in alloc_info_private() When try_module_get() fails in alloc_info_private(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other -ENODEV path in the same function. This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-4-phucduc.bui@gmail.com --- sound/core/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/info.c b/sound/core/info.c index 54834dbe6b59..3b8ccda04e1a 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -78,7 +78,7 @@ static int alloc_info_private(struct snd_info_entry *entry, if (!entry || !entry->p) return -ENODEV; if (!try_module_get(entry->module)) - return -EFAULT; + return -ENODEV; data = kzalloc_obj(*data); if (!data) { module_put(entry->module); From fb0fb4b94362a89da605058269bceb481a5e60b6 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:04 +0700 Subject: [PATCH 117/791] ALSA: mixer: oss: Return -ENODEV instead of -EFAULT in snd_mixer_oss_open() When try_module_get() fails in snd_mixer_oss_open(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other unavailable-device paths in this function(the NULL card check and the NULL mixer_oss check). This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-5-phucduc.bui@gmail.com --- sound/core/oss/mixer_oss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index f5bcc7896542..3a1dd1edd26d 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -58,7 +58,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) kfree(fmixer); snd_card_file_remove(card, file); snd_card_unref(card); - return -EFAULT; + return -ENODEV; } snd_card_unref(card); return 0; From d9468d5286c1b6483bcaa0e89d9b2862941f3387 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:05 +0700 Subject: [PATCH 118/791] ALSA: pcm: oss: Return -ENODEV instead of -EFAULT in snd_pcm_oss_open() When try_module_get() fails in snd_pcm_oss_open(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other unavailable-device paths in this function(the NULL pcm check and the card->shutdown check). This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-6-phucduc.bui@gmail.com --- sound/core/oss/pcm_oss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 0d7818eb3e14..8ae43755fb9b 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2507,7 +2507,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) if (err < 0) goto __error1; if (!try_module_get(pcm->card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { From a0a47b0ae2d8f24db465a3e3d69e237e69cd7d09 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 7 Jul 2026 15:45:06 +0700 Subject: [PATCH 119/791] ALSA: pcm: Return -ENODEV instead of -EFAULT in snd_pcm_open() When try_module_get() fails in snd_pcm_open(), the owning module is going away and the device is no longer usable, so -EFAULT ("bad address") is the wrong error. Return -ENODEV, matching the other unavailable-device paths in this function(the NULL pcm check and the card->shutdown check). This changes the errno reported to userspace on this rare race. No other behaviour changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260707084506.718583-7-phucduc.bui@gmail.com --- sound/core/pcm_native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 3462cd5275a2..4fe5fab8d096 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2913,7 +2913,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) if (err < 0) goto __error1; if (!try_module_get(pcm->card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } init_waitqueue_entry(&wait, current); From ae1f30f4bdaac9aca75e95785db542a3f087a965 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 09:35:40 +0700 Subject: [PATCH 120/791] ALSA: control: preserve snd_card_file_add() error code in snd_ctl_open() snd_ctl_open() unconditionally overwrites the return value of snd_card_file_add() with -ENODEV on failure, discarding the actual error code. Fix this by directly returning the original error code returned by snd_card_file_add() (e.g. -ENOMEM or -ENODEV). This behavior is consistent with the error handling used in other functions such as snd_mixer_oss_open(), snd_hwdep_open(), snd_pcm_oss_open(), and others. There is no functional change other than the returned error code in this failure path. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260708023540.6962-1-phucduc.bui@gmail.com --- sound/core/control.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 0ca9fff56e51..73d7ba0f509f 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -63,10 +63,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file) goto __error1; } err = snd_card_file_add(card, file); - if (err < 0) { - err = -ENODEV; + if (err < 0) goto __error1; - } if (!try_module_get(card->module)) { err = -ENODEV; goto __error2; From 75c4027e741db349bafd2f6f9983a5b9a53e173f Mon Sep 17 00:00:00 2001 From: Leo Tsai Date: Wed, 8 Jul 2026 11:55:52 +0800 Subject: [PATCH 121/791] ALSA: hda/cm9825: Add IBP support for DFI The IBP project is an DFI platform with a fixed audio configuration consisting of headset(headphone and mic-in). The audio routing and pin assignments are defined according to the board-level hardware design and are not intended to be dynamically changed. Signed-off-by: Leo Tsai Link: https://patch.msgid.link/20260708035552.44429-1-antivirus621@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/cm9825.c | 148 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 6 deletions(-) diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c index eeb9ca38d2d8..8a74862d8a81 100644 --- a/sound/hda/codecs/cm9825.c +++ b/sound/hda/codecs/cm9825.c @@ -15,7 +15,8 @@ enum { QUIRK_CM_STD = 0x0, - QUIRK_GENE_TWL7_SSID = 0x160dc000 + QUIRK_GENE_TWL7_SSID = 0x160dc000, + QUIRK_IBP_SSID = 0x15bd3275 }; /* CM9825 Offset Definitions */ @@ -40,13 +41,19 @@ enum { #define CM9825_VERB_SET_GAD 0x7b4 #define CM9825_VERB_SET_TMOD 0x7b5 #define CM9825_VERB_SET_SNR 0x7b6 +#define CM9825_VERB_SET_OMTP 0x7ef +#define CM9825_VERB_READ_OMTP 0xfec struct cmi_spec { struct hda_gen_spec gen; const struct hda_verb *chip_d0_verbs; const struct hda_verb *chip_d3_verbs; + const struct hda_verb *chip_playback_start_verbs; + const struct hda_verb *chip_playback_stop_verbs; const struct hda_verb *chip_hp_present_verbs; const struct hda_verb *chip_hp_remove_verbs; + const struct hda_verb *chip_lineout_present_verbs; + const struct hda_verb *chip_lineout_remove_verbs; struct hda_codec *codec; struct delayed_work unsol_inputs_work; struct delayed_work unsol_lineout_work; @@ -193,6 +200,94 @@ static const struct hda_verb cm9825_gene_twl7_playback_stop_verbs[] = { {} }; +/* + * To save power, AD/CLK is turned off. + */ +static const struct hda_verb cm9825_ibp_d3_verbs[] = { + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_PLL, 0x01}, + {0x43, CM9825_VERB_SET_NEG, 0xc2}, + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_VNEG, 0x50}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {0x43, CM9825_VERB_SET_PDNEG, 0x04}, + {0x43, CM9825_VERB_SET_CDALR, 0xf6}, + {0x43, CM9825_VERB_SET_OTP, 0xcd}, + {} +}; + +/* + * D0 configuration: enable PLL/CLK/ADC/DAC and optimize performance + */ +static const struct hda_verb cm9825_ibp_d0_verbs[] = { + {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {0x43, CM9825_VERB_SET_SNR, 0x38}, + {0x43, CM9825_VERB_SET_PLL, 0x00}, + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {0x43, CM9825_VERB_SET_VNEG, 0x56}, + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, + {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, + {0x43, CM9825_VERB_SET_CDALR, 0xf4}, + {0x43, CM9825_VERB_SET_OTP, 0xcd}, + {0x43, CM9825_VERB_SET_MTCBA, 0x61}, + {0x43, CM9825_VERB_SET_OCP, 0x33}, + {0x43, CM9825_VERB_SET_GAD, 0x07}, + {0x43, CM9825_VERB_SET_TMOD, 0x26}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE | 0xa0, 0x2d}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE | 0x90, 0x2d}, + {0x43, CM9825_VERB_SET_HPF_1, 0x40}, + {0x43, CM9825_VERB_SET_HPF_2, 0x40}, + {} +}; + +/* + * Enable mbias, ADC. + */ +static const struct hda_verb cm9825_ibp_lineout_present_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x8c}, + {0x43, CM9825_VERB_SET_MBIAS, 0x10}, + {} +}; + +/* + * Disable mbias, ADC. + */ +static const struct hda_verb cm9825_ibp_lineout_remove_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {} +}; + +/* + * Turn on the DAC (widget NID 0x43) in the playback path by writing + * a sequence of vendor-specific verbs. + */ +static const struct hda_verb cm9825_ibp_playback_start_verbs[] = { + {0x43, CM9825_VERB_SET_DACL, 0xaa}, + {0x43, CM9825_VERB_SET_D2S, 0xf2}, + {0x43, CM9825_VERB_SET_VDO, 0xc4}, + {0x43, CM9825_VERB_SET_SNR, 0x30}, + {} +}; + +/* + * Shut down the playback path. The order differs slightly from the + * enable sequence, likely to avoid audible pop noise when powering + * down the output stage. + */ +static const struct hda_verb cm9825_ibp_playback_stop_verbs[] = { + {0x43, CM9825_VERB_SET_VDO, 0xc0}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_VDO, 0x80}, + {0x43, CM9825_VERB_SET_SNR, 0x38}, + {} +}; + static void cm9825_update_jk_plug_status(struct hda_codec *codec, hda_nid_t nid) { struct cmi_spec *spec = codec->spec; @@ -232,6 +327,23 @@ static void cm9825_unsol_lineout_delayed(struct work_struct *work) struct cmi_spec *spec = container_of(to_delayed_work(work), struct cmi_spec, unsol_lineout_work); + hda_nid_t line_out_pin = spec->gen.autocfg.line_out_pins[0]; + bool line_out_jack_plugin = false; + + line_out_jack_plugin = snd_hda_jack_detect(spec->codec, line_out_pin); + + codec_dbg(spec->codec, "lineout_jack_plugin %d, lineout_pin 0x%X\n", + (int)line_out_jack_plugin, line_out_pin); + + if (!line_out_jack_plugin) { + /* Jack plugout */ + snd_hda_sequence_write(spec->codec, + spec->chip_lineout_remove_verbs); + } else { + /* Jack plugin */ + snd_hda_sequence_write(spec->codec, + spec->chip_lineout_present_verbs); + } cm9825_update_jk_plug_status(spec->codec, spec->gen.autocfg.line_out_pins[0]); @@ -362,12 +474,10 @@ static void cm9825_playback_pcm_hook(struct hda_pcm_stream *hinfo, switch (action) { case HDA_GEN_PCM_ACT_PREPARE: - snd_hda_sequence_write(spec->codec, - cm9825_gene_twl7_playback_start_verbs); + snd_hda_sequence_write(codec, spec->chip_playback_start_verbs); break; case HDA_GEN_PCM_ACT_CLEANUP: - snd_hda_sequence_write(spec->codec, - cm9825_gene_twl7_playback_stop_verbs); + snd_hda_sequence_write(codec, spec->chip_playback_stop_verbs); break; default: return; @@ -471,7 +581,8 @@ static int cm9825_resume(struct hda_codec *codec) if (codec->core.subsystem_id == QUIRK_CM_STD) cm9825_cm_std_resume(codec); - else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID) { + else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID || + codec->core.subsystem_id == QUIRK_IBP_SSID) { snd_hda_codec_init(codec); snd_hda_sequence_write(codec, spec->chip_d0_verbs); } @@ -487,6 +598,7 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) struct cmi_spec *spec; struct auto_pin_cfg *cfg; int err = 0; + unsigned int val; spec = kzalloc_obj(*spec); if (spec == NULL) @@ -523,9 +635,33 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->chip_d0_verbs = cm9825_gene_twl7_d0_verbs; spec->chip_d3_verbs = cm9825_gene_twl7_d3_verbs; spec->gen.pcm_playback_hook = cm9825_playback_pcm_hook; + spec->chip_playback_start_verbs = + cm9825_gene_twl7_playback_start_verbs; + spec->chip_playback_stop_verbs = + cm9825_gene_twl7_playback_stop_verbs; /* Internal fixed device, Rear, Mic-in, 3.5mm */ snd_hda_codec_set_pincfg(codec, 0x37, 0x24A70100); break; + case QUIRK_IBP_SSID: + snd_hda_codec_set_name(codec, "CM9825 IBP"); + spec->chip_d0_verbs = cm9825_ibp_d0_verbs; + spec->chip_d3_verbs = cm9825_ibp_d3_verbs; + spec->gen.pcm_playback_hook = cm9825_playback_pcm_hook; + spec->chip_lineout_present_verbs = + cm9825_ibp_lineout_present_verbs; + spec->chip_lineout_remove_verbs = + cm9825_ibp_lineout_remove_verbs; + spec->chip_playback_start_verbs = + cm9825_ibp_playback_start_verbs; + spec->chip_playback_stop_verbs = cm9825_ibp_playback_stop_verbs; + + /* OMTP */ + val = + snd_hda_codec_read(codec, 0x46, 0, CM9825_VERB_READ_OMTP, + 0x0); + snd_hda_codec_write(codec, 0x46, 0, CM9825_VERB_SET_OMTP, + (val >> 24) & 0x7f); + break; default: err = -ENXIO; break; From cb63976eac60a3dd0da07b771a8ecdf061872027 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 8 Jul 2026 06:48:27 +0000 Subject: [PATCH 122/791] ASoC: amd: acp6x-mach: tidyup strange alignment yc_acp_quirk_table[] has strange alignment item. Tidyup it. Signed-off-by: Kuninori Morimoto Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/874iiageo4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index d6df7de70b27..8b121593f26a 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -753,12 +753,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { } }, { - .driver_data = &acp6x_card, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), - } - }, + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), + } + }, { .driver_data = &acp6x_card, .matches = { From 7ccad56689576e874a271d8dcb322952da605878 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Tue, 7 Jul 2026 15:57:20 +0900 Subject: [PATCH 123/791] ASoC: dt-bindings: wlf,wm8524: Add audio-graph port support Add port property referencing audio-graph-port.yaml to allow WM8524 to be used with audio-graph-card and audio-graph-card2. Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260707065725.312450-2-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/wlf,wm8524.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml index 4d951ece394e..dffc41d5f7de 100644 --- a/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml +++ b/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml @@ -24,6 +24,10 @@ properties: description: a GPIO spec for the MUTE pin. + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - wlf,mute-gpios From 28017bf7f76246ffbbd2710b5057e407003eabc9 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Tue, 7 Jul 2026 15:57:21 +0900 Subject: [PATCH 124/791] ASoC: dt-bindings: fsl,micfil: Add audio-graph port support Add port property referencing audio-graph-port.yaml to allow MICFIL to be used with audio-graph-card and audio-graph-card2. Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260707065725.312450-3-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,micfil.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml index c47b7a097490..4c7dadb310de 100644 --- a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml @@ -66,6 +66,10 @@ properties: "#sound-dai-cells": const: 0 + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - reg From cf3bd20c43f77b4cce57ea2a738de8055aa12c97 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Tue, 7 Jul 2026 15:57:22 +0900 Subject: [PATCH 125/791] ASoC: dt-bindings: dmic-codec: Add audio-graph port support Add port property referencing audio-graph-port.yaml to allow DMIC to be used with audio-graph-card and audio-graph-card2. Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260707065725.312450-4-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/dmic-codec.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/dmic-codec.yaml b/Documentation/devicetree/bindings/sound/dmic-codec.yaml index cc3c84dd4c26..83c23e029ea4 100644 --- a/Documentation/devicetree/bindings/sound/dmic-codec.yaml +++ b/Documentation/devicetree/bindings/sound/dmic-codec.yaml @@ -39,6 +39,10 @@ properties: wakeup-delay-ms: description: Delay (in ms) after enabling the DMIC + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible From 907e6a701029bc2cd27b821ec25615c5215a6133 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 8 Jul 2026 11:58:40 +0200 Subject: [PATCH 126/791] ASoC: pm4125: Switch to irq_domain_create_linear() irq_domain_add_linear() is going away as being obsolete now. Switch to the preferred irq_domain_create_linear(). That differs in the first parameter: It takes more generic struct fwnode_handle instead of struct device_node. Therefore, of_fwnode_handle() is added around the parameter. Note some of the users can likely use dev->fwnode directly instead of indirect of_fwnode_handle(dev->of_node). But dev->fwnode is not guaranteed to be set for all, so this has to be investigated on case to case basis (by people who can actually test with the HW). Signed-off-by: Jiri Slaby (SUSE) Cc: Thomas Gleixner Cc: Srinivas Kandagatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260708095840.385526-1-jirislaby@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pm4125.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c index 29655175ea28..674a2fbc4eda 100644 --- a/sound/soc/codecs/pm4125.c +++ b/sound/soc/codecs/pm4125.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) { - pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); + pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); if (!(pm4125->virq)) { dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); return -EINVAL; From ed1d19d37f5bc8de9f516891b42b4e9a63e8856e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:18:48 +0800 Subject: [PATCH 127/791] ASoC: codecs: max98927: Propagate regcache_sync() errors max98927_resume() leaves cache-only mode and replays cached register state into the device. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704071848.47928-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98927.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 65e6fdb30eec..524300d8fb0b 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) static int max98927_resume(struct device *dev) { struct max98927_priv *max98927 = dev_get_drvdata(dev); + int ret; regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); regcache_cache_only(max98927->regmap, false); - regcache_sync(max98927->regmap); + ret = regcache_sync(max98927->regmap); + if (ret) { + regcache_cache_only(max98927->regmap, true); + regcache_mark_dirty(max98927->regmap); + return ret; + } return 0; } From 29da72e58fde87e632f2fa43e448fcd1e3ab31b0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:21:40 +0800 Subject: [PATCH 128/791] ASoC: codecs: uda1342: Leave cache-only mode before syncing uda1342_suspend() puts the regmap into cache-only mode. uda1342_resume() marks the cache dirty and syncs it, but does not first leave cache-only mode and ignores sync failures. Leave cache-only mode before syncing the dirty cache, propagate sync failures, and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072140.67222-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/uda1342.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c index 12f5757f4210..4e2d6240a869 100644 --- a/sound/soc/codecs/uda1342.c +++ b/sound/soc/codecs/uda1342.c @@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) static int uda1342_resume(struct device *dev) { struct uda1342_priv *uda1342 = dev_get_drvdata(dev); + int ret; + regcache_cache_only(uda1342->regmap, false); regcache_mark_dirty(uda1342->regmap); - regcache_sync(uda1342->regmap); + ret = regcache_sync(uda1342->regmap); + if (ret) { + regcache_cache_only(uda1342->regmap, true); + regcache_mark_dirty(uda1342->regmap); + return ret; + } return 0; } From b2a06f7c9514092862021081d59eeb123d3554a5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:40:00 +0800 Subject: [PATCH 129/791] ASoC: codecs: max98363: Propagate regcache_sync() errors max98363_resume() waits for SoundWire initialization, leaves cache-only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074000.42578-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98363.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 099dc5bf6195..0e32b7a94cb0 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) return ret; regcache_cache_only(max98363->regmap, false); - regcache_sync(max98363->regmap); + ret = regcache_sync(max98363->regmap); + if (ret) { + regcache_cache_only(max98363->regmap, true); + regcache_mark_dirty(max98363->regmap); + return ret; + } return 0; } From b64317ec00741a572fd6801cac176e34ffba0e0a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:40:44 +0800 Subject: [PATCH 130/791] ASoC: codecs: max98373-sdw: Propagate regcache_sync() errors max98373_resume() waits for SoundWire initialization, leaves cache-only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074044.45520-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98373-sdw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 8fe9c58e1a62..638ef343813b 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) } regcache_cache_only(max98373->regmap, false); - regcache_sync(max98373->regmap); + ret = regcache_sync(max98373->regmap); + if (ret) { + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return ret; + } return 0; } From 808d65f3fb42222a7816ca1509e177447417d447 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:19:33 +0800 Subject: [PATCH 131/791] ASoC: codecs: pm4125-sdw: Propagate regcache_sync() errors pm4125_sdw_runtime_resume() leaves cache-only mode and replays cached register state when a regmap is present. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704071933.52450-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/pm4125-sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c index 1c612ae4a4b2..307b66025426 100644 --- a/sound/soc/codecs/pm4125-sdw.c +++ b/sound/soc/codecs/pm4125-sdw.c @@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) { struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); + int ret; if (priv->regmap) { regcache_cache_only(priv->regmap, false); - regcache_sync(priv->regmap); + ret = regcache_sync(priv->regmap); + if (ret) { + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + return ret; + } } return 0; From ca571406c52186eb5068e36d6a46512adf1b3a79 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:20:31 +0800 Subject: [PATCH 132/791] ASoC: codecs: rt5645: Propagate regcache_sync() errors Both rt5645_resume() and rt5645_sys_resume() leave cache-only mode and replay cached register state, but both paths ignore regcache_sync() failures and can report success. Check both sync operations, return the error, and restore cache- only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072031.60768-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e9819653b30d..93a4148ccccd 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3518,9 +3518,15 @@ static int rt5645_suspend(struct snd_soc_component *component) static int rt5645_resume(struct snd_soc_component *component) { struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int ret; regcache_cache_only(rt5645->regmap, false); - regcache_sync(rt5645->regmap); + ret = regcache_sync(rt5645->regmap); + if (ret) { + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return ret; + } return 0; } @@ -4331,9 +4337,15 @@ static int rt5645_sys_suspend(struct device *dev) static int rt5645_sys_resume(struct device *dev) { struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + int ret; regcache_cache_only(rt5645->regmap, false); - regcache_sync(rt5645->regmap); + ret = regcache_sync(rt5645->regmap); + if (ret) { + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return ret; + } if (rt5645->hp_jack) { rt5645->jack_type = 0; From a1d01870b45d77c25e719235e5c8d67d7bf545d2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:22:36 +0800 Subject: [PATCH 133/791] ASoC: codecs: wcd937x-sdw: Propagate regcache_sync() errors wcd937x_sdw_runtime_resume() leaves cache-only mode and replays cached register state when a regmap is present. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072236.70862-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x-sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index 7a18bed7f347..1ad75c226060 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) static int wcd937x_sdw_runtime_resume(struct device *dev) { struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } return 0; From 495d266f67f46e4ef0497c98a1588be43a39f95e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:23:47 +0800 Subject: [PATCH 134/791] ASoC: codecs: wcd939x-sdw: Propagate regcache_sync() errors wcd939x_sdw_runtime_resume() leaves cache-only mode and replays cached register state when a regmap is present. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072347.76000-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x-sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index 95f4be287e79..69b9b08118f0 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) static int wcd939x_sdw_runtime_resume(struct device *dev) { struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } return 0; From cc5c698bbca05d0467239c5d7aa7ec8697b77ab2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:24:54 +0800 Subject: [PATCH 135/791] ASoC: codecs: wsa883x: Propagate regcache_sync() errors wsa883x_runtime_resume() leaves cache-only mode and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072454.80981-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 468d2b38a22a..24a5904d8e6c 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1696,9 +1696,15 @@ static int wsa883x_runtime_suspend(struct device *dev) static int wsa883x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return ret; + } return 0; } From 6ed25217ae6925bc9f511328a7c39e9f1a795298 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:26:16 +0800 Subject: [PATCH 136/791] ASoC: codecs: wsa884x: Propagate regcache_sync() errors wsa884x_runtime_resume() leaves cache-only mode and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704072616.88634-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 6c6b497657d0..567861dd42ad 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -2147,9 +2147,15 @@ static int wsa884x_runtime_suspend(struct device *dev) static int wsa884x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return ret; + } return 0; } From f814f9dfdc6151788b3aa937ddbb18a07d16d032 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:35:24 +0800 Subject: [PATCH 137/791] ASoC: codecs: rt712-sdca-dmic: Propagate regcache_sync() errors rt712_sdca_dmic_dev_resume() clears cache-only mode for both regmaps and replays cached register state. Both regcache_sync() calls currently ignore their return values. Check both sync operations, return the first error, and restore both regmaps to cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704073524.27201-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-dmic.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 85779547653e..8860d81134e7 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -916,10 +916,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) } regcache_cache_only(rt712->regmap, false); - regcache_sync(rt712->regmap); + ret = regcache_sync(rt712->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt712->mbq_regmap, false); - regcache_sync(rt712->mbq_regmap); + ret = regcache_sync(rt712->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + regcache_mark_dirty(rt712->regmap); + regcache_mark_dirty(rt712->mbq_regmap); + return ret; } static const struct dev_pm_ops rt712_sdca_dmic_pm = { From ba8c728e7566193e3b7ad0a91e751f6d441d5596 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:36:46 +0800 Subject: [PATCH 138/791] ASoC: codecs: rt712-sdca-sdw: Propagate regcache_sync() errors rt712_sdca_dev_resume() clears cache-only mode for both regmaps and replays cached register state. Both regcache_sync() calls currently ignore their return values. Check both sync operations, return the first error, and restore both regmaps to cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704073646.33065-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 70d661ce2ef2..ebdf7b308331 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -472,10 +472,23 @@ static int rt712_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt712->regmap, false); - regcache_sync(rt712->regmap); + ret = regcache_sync(rt712->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt712->mbq_regmap, false); - regcache_sync(rt712->mbq_regmap); + ret = regcache_sync(rt712->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + regcache_mark_dirty(rt712->regmap); + regcache_mark_dirty(rt712->mbq_regmap); + return ret; } static const struct dev_pm_ops rt712_sdca_pm = { From 7a0886826308cb07ce9056bf656f7282b959062d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:37:58 +0800 Subject: [PATCH 139/791] ASoC: codecs: rt721-sdca-sdw: Propagate regcache_sync() errors rt721_sdca_dev_resume() clears cache-only mode for both regmaps and replays cached register state. Both regcache_sync() calls currently ignore their return values. Check both sync operations, return the first error, and restore both regmaps to cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704073758.37156-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt721-sdca-sdw.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 041b381e582b..6e27e761afb6 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -510,10 +510,23 @@ static int rt721_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt721->regmap, false); - regcache_sync(rt721->regmap); + ret = regcache_sync(rt721->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt721->mbq_regmap, false); - regcache_sync(rt721->mbq_regmap); + ret = regcache_sync(rt721->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt721->regmap, true); + regcache_cache_only(rt721->mbq_regmap, true); + regcache_mark_dirty(rt721->regmap); + regcache_mark_dirty(rt721->mbq_regmap); + return ret; } static const struct dev_pm_ops rt721_sdca_pm = { From 439f04ca429b819d481c24ff6da1923ab29d4830 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:41:37 +0800 Subject: [PATCH 140/791] ASoC: codecs: rt1017-sdca-sdw: Propagate regcache_sync() errors rt1017_sdca_dev_resume() waits for SoundWire initialization, leaves cache-only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074137.47606-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt1017-sdca-sdw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 95405cea8143..caf75e5657ef 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt1017->regmap, false); - regcache_sync(rt1017->regmap); + ret = regcache_sync(rt1017->regmap); + if (ret) { + regcache_cache_only(rt1017->regmap, true); + regcache_mark_dirty(rt1017->regmap); + return ret; + } return 0; } From c9ad95acabb2f117d4dfa9b3ca4888851256bb73 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:42:38 +0800 Subject: [PATCH 141/791] ASoC: codecs: rt1316-sdw: Propagate regcache_sync() errors rt1316_dev_resume() waits for SoundWire initialization, leaves cache- only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074238.50145-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt1316-sdw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index ca318dbd946e..359b52ba1ef9 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) } regcache_cache_only(rt1316->regmap, false); - regcache_sync(rt1316->regmap); + ret = regcache_sync(rt1316->regmap); + if (ret) { + regcache_cache_only(rt1316->regmap, true); + regcache_mark_dirty(rt1316->regmap); + return ret; + } return 0; } From f047e6a0f93ae3994abc4622486aeb992429f38b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:43:50 +0800 Subject: [PATCH 142/791] ASoC: codecs: rt722-sdca-sdw: Propagate regcache_sync() errors rt722_sdca_dev_resume() waits for SoundWire initialization, leaves cache-only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074350.53023-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca-sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e68aa0350a5b..d2db33b4f684 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -557,7 +557,13 @@ static int rt722_sdca_dev_resume(struct device *dev) } regcache_cache_only(rt722->regmap, false); - regcache_sync(rt722->regmap); + ret = regcache_sync(rt722->regmap); + if (ret) { + regcache_cache_only(rt722->regmap, true); + regcache_mark_dirty(rt722->regmap); + return ret; + } + return 0; } From e857b163c8f441d477db36f0aca1cf6e258a4be7 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:44:34 +0800 Subject: [PATCH 143/791] ASoC: codecs: wsa881x: Propagate regcache_sync() errors wsa881x_runtime_resume() powers the device up, waits for SoundWire initialization, leaves cache-only mode, and replays cached register state. It currently ignores regcache_sync() failures and returns success. Propagate sync failures, restore cache-only/dirty state, and undo the shutdown GPIO state acquired by resume. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074434.54200-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 5174614c3e83..47ac3e4985a5 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) } regcache_cache_only(regmap, false); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + gpiod_direction_output(wsa881x->sd_n, 1); + return ret; + } return 0; } From abe8ddff80fdfce5255ab9f5931aaed5b058bb65 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:45:50 +0800 Subject: [PATCH 144/791] ASoC: codecs: rt5514: Propagate bias restore errors rt5514_set_bias_level() restores normal recording settings when leaving the DSP-enabled state, but ignores both the patch write and regcache_sync() failures. The bias transition can therefore report success even when the restore did not complete. Propagate the write and sync errors from the bias-level restore path. Only clear dsp_enabled after both restore operations succeed, so a failed restore keeps the internal state retryable. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704074550.57755-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt5514.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 00a4a208d2fa..58aecd4c83a6 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, * settings to make sure recording properly. */ if (rt5514->dsp_enabled) { - rt5514->dsp_enabled = 0; - regmap_multi_reg_write(rt5514->i2c_regmap, - rt5514_i2c_patch, - ARRAY_SIZE(rt5514_i2c_patch)); + ret = regmap_multi_reg_write(rt5514->i2c_regmap, + rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret) + return ret; + regcache_mark_dirty(rt5514->regmap); - regcache_sync(rt5514->regmap); + ret = regcache_sync(rt5514->regmap); + if (ret) + return ret; + + rt5514->dsp_enabled = 0; } } break; From e38cbb75248a4027389c8a3d0bbd27bb760ba992 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:49:46 +0800 Subject: [PATCH 145/791] ASoC: codecs: lpass-rx-macro: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state. The RX macro MCLK helper and runtime resume path currently ignore that failure and report success. Propagate the error from the MCLK helper users and from runtime resume. If runtime resume fails after enabling clocks, restore cache-only/dirty state and unwind the clocks acquired by the resume path. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704034946.20369-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 6233aa9f5bc6..550fc76109c6 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -2030,9 +2030,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { }, }; -static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) +static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) { struct regmap *regmap = rx->regmap; + int ret; if (mclk_enable) { if (rx->rx_mclk_users == 0) { @@ -2047,14 +2048,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) CDC_RX_FS_MCLK_CNT_EN_MASK, CDC_RX_FS_MCLK_CNT_ENABLE); regcache_mark_dirty(regmap); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) + return ret; } rx->rx_mclk_users++; } else { if (rx->rx_mclk_users <= 0) { dev_err(rx->dev, "%s: clock already disabled\n", __func__); rx->rx_mclk_users = 0; - return; + return 0; } rx->rx_mclk_users--; if (rx->rx_mclk_users == 0) { @@ -2068,6 +2071,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) CDC_RX_CLK_MCLK2_EN_MASK, 0x0); } } + + return 0; } static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, @@ -2079,11 +2084,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - rx_macro_mclk_enable(rx, true); - break; + return rx_macro_mclk_enable(rx, true); case SND_SOC_DAPM_POST_PMD: - rx_macro_mclk_enable(rx, false); - break; + return rx_macro_mclk_enable(rx, false); default: dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); ret = -EINVAL; @@ -3677,7 +3680,11 @@ static int swclk_gate_enable(struct clk_hw *hw) return ret; } - rx_macro_mclk_enable(rx, true); + ret = rx_macro_mclk_enable(rx, true); + if (ret) { + clk_disable_unprepare(rx->mclk); + return ret; + } regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, CDC_RX_SWR_CLK_EN_MASK, 1); @@ -4003,9 +4010,15 @@ static int rx_macro_runtime_resume(struct device *dev) goto err_fsgen; } regcache_cache_only(rx->regmap, false); - regcache_sync(rx->regmap); + ret = regcache_sync(rx->regmap); + if (ret) + goto err_sync; return 0; +err_sync: + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + clk_disable_unprepare(rx->fsgen); err_fsgen: clk_disable_unprepare(rx->npl); err_npl: From ea70ad6ac28809f238d054b375cbe218f713deee Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:50:52 +0800 Subject: [PATCH 146/791] ASoC: codecs: lpass-tx-macro: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state. The TX macro MCLK helper and runtime resume path currently ignore that failure and report success. Propagate the error from the MCLK helper users and from runtime resume. If runtime resume fails after enabling clocks, restore cache-only/dirty state and unwind the clocks acquired by the resume path. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035052.29804-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index f7d168f557dd..b6b5ef802035 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -614,6 +614,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, bool mclk_enable) { struct regmap *regmap = tx->regmap; + int ret; if (mclk_enable) { if (tx->tx_mclk_users == 0) { @@ -626,7 +627,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, CDC_TX_FS_CNT_EN_MASK, CDC_TX_FS_CNT_ENABLE); regcache_mark_dirty(regmap); - regcache_sync(regmap); + ret = regcache_sync(regmap); + if (ret) + return ret; } tx->tx_mclk_users++; } else { @@ -739,11 +742,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - tx_macro_mclk_enable(tx, true); - break; + return tx_macro_mclk_enable(tx, true); case SND_SOC_DAPM_POST_PMD: - tx_macro_mclk_enable(tx, false); - break; + return tx_macro_mclk_enable(tx, false); default: break; } @@ -2155,7 +2156,11 @@ static int swclk_gate_enable(struct clk_hw *hw) return ret; } - tx_macro_mclk_enable(tx, true); + ret = tx_macro_mclk_enable(tx, true); + if (ret) { + clk_disable_unprepare(tx->mclk); + return ret; + } regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_CLK_EN_MASK, @@ -2437,9 +2442,15 @@ static int tx_macro_runtime_resume(struct device *dev) } regcache_cache_only(tx->regmap, false); - regcache_sync(tx->regmap); + ret = regcache_sync(tx->regmap); + if (ret) + goto err_sync; return 0; +err_sync: + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + clk_disable_unprepare(tx->fsgen); err_fsgen: clk_disable_unprepare(tx->npl); err_npl: From 83cc6d88e37754f6ef3d7ce4a5c335688bab22b8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:52:04 +0800 Subject: [PATCH 147/791] ASoC: codecs: max98373: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after resume. max98373_resume() currently ignores that failure and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035204.39073-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98373-i2c.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index 8805bd01153c..cbd7ebffa6d6 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) static int max98373_resume(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98373->regmap, false); max98373_reset(max98373, dev); - regcache_sync(max98373->regmap); + ret = regcache_sync(max98373->regmap); + if (ret) { + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return ret; + } + return 0; } From 49ea357ea779ed24530e402655500c89d484b1db Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:53:16 +0800 Subject: [PATCH 148/791] ASoC: codecs: max98388: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after resume. max98388_resume() currently ignores that failure and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035316.47011-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98388.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c index a4c57152d25f..d97dab41bf0f 100644 --- a/sound/soc/codecs/max98388.c +++ b/sound/soc/codecs/max98388.c @@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) static int max98388_resume(struct device *dev) { struct max98388_priv *max98388 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98388->regmap, false); max98388_reset(max98388, dev); - regcache_sync(max98388->regmap); + ret = regcache_sync(max98388->regmap); + if (ret) { + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + return ret; + } return 0; } From 2c5e237e7e2a4256d080b44ec39a60a3d8e6d355 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:54:37 +0800 Subject: [PATCH 149/791] ASoC: codecs: rt1318-sdw: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after the SoundWire slave has reinitialized. rt1318_dev_resume() currently ignores that failure and returns success. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035437.57792-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt1318-sdw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index c038ac0e3b76..ef18cd63d461 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) return ret; regcache_cache_only(rt1318->regmap, false); - regcache_sync(rt1318->regmap); + ret = regcache_sync(rt1318->regmap); + if (ret) { + regcache_cache_only(rt1318->regmap, true); + regcache_mark_dirty(rt1318->regmap); + return ret; + } return 0; } From aeb606a6437dfed9a98a6c2b056f9086a881f210 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:55:48 +0800 Subject: [PATCH 150/791] ASoC: codecs: rt9120: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after runtime resume. rt9120_runtime_resume() currently ignores that failure and returns success. Propagate the error, restore cache-only/dirty state, and power the device back down on sync failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035548.67713-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt9120.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index 97f56af25577..e51cd4a31894 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) static int rt9120_runtime_resume(struct device *dev) { struct rt9120_data *data = dev_get_drvdata(dev); + int ret; if (data->pwdnn_gpio) { gpiod_set_value(data->pwdnn_gpio, 1); msleep(RT9120_CHIPON_WAITMS); regcache_cache_only(data->regmap, false); - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret) { + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + gpiod_set_value(data->pwdnn_gpio, 0); + return ret; + } } return 0; From 70f88374e4e40d851a8a36bc2a6138697bbfcb5a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:57:05 +0800 Subject: [PATCH 151/791] ASoC: codecs: tas2552: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after runtime resume. tas2552_runtime_resume() currently ignores that failure and returns success. Propagate the error, restore cache-only/dirty state, and put the amplifier back into shutdown on sync failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035705.77946-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/tas2552.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 1a7650b9b2a7..22f93e29454f 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) static int tas2552_runtime_resume(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); + int ret; gpiod_set_value_cansleep(tas2552->enable_gpio, 1); tas2552_sw_shutdown(tas2552, 0); regcache_cache_only(tas2552->regmap, false); - regcache_sync(tas2552->regmap); + ret = regcache_sync(tas2552->regmap); + if (ret) { + regcache_cache_only(tas2552->regmap, true); + regcache_mark_dirty(tas2552->regmap); + tas2552_sw_shutdown(tas2552, 1); + gpiod_set_value_cansleep(tas2552->enable_gpio, 0); + return ret; + } return 0; } From 0d6b2d6f93a6715827a9b3c027cd8448d76e0e47 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:57:46 +0800 Subject: [PATCH 152/791] ASoC: codecs: tas2783-sdw: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state after SoundWire resume or attach handling. tas2783 currently ignores that failure. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035746.82560-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 3d0b116544cc..db58c50e8a83 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1099,7 +1099,13 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) } regcache_cache_only(tas_dev->regmap, false); - regcache_sync(tas_dev->regmap); + ret = regcache_sync(tas_dev->regmap); + if (ret) { + regcache_cache_only(tas_dev->regmap, true); + regcache_mark_dirty(tas_dev->regmap); + return ret; + } + return 0; } @@ -1210,6 +1216,7 @@ static s32 tas_update_status(struct sdw_slave *slave, { struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev); struct device *dev = &slave->dev; + int ret; dev_dbg(dev, "Peripheral status = %s", status == SDW_SLAVE_UNATTACHED ? "unattached" : @@ -1227,7 +1234,12 @@ static s32 tas_update_status(struct sdw_slave *slave, /* updated the cache data to device */ regcache_cache_only(tas_dev->regmap, false); - regcache_sync(tas_dev->regmap); + ret = regcache_sync(tas_dev->regmap); + if (ret) { + regcache_cache_only(tas_dev->regmap, true); + regcache_mark_dirty(tas_dev->regmap); + return ret; + } /* perform I/O transfers required for Slave initialization */ return tas_io_init(&slave->dev, slave); From 72f1d7d21fbfea6bc2c09032044acfa372306fc0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:58:57 +0800 Subject: [PATCH 153/791] ASoC: codecs: wcd938x-sdw: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. wcd938x_sdw_runtime_resume() currently ignores that failure. Propagate the error and restore cache-only/dirty state on failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035857.90264-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x-sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 0f0cc0ac3056..e5ad368418a2 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) static int wcd938x_sdw_runtime_resume(struct device *dev) { struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); + int ret; if (wcd->regmap) { regcache_cache_only(wcd->regmap, false); - regcache_sync(wcd->regmap); + ret = regcache_sync(wcd->regmap); + if (ret) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + return ret; + } } pm_runtime_mark_last_busy(dev); From 5e5ea96e68c7751a3d38827f8d11b68810872e8f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 11:59:57 +0800 Subject: [PATCH 154/791] ASoC: codecs: cs35l32: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. cs35l32_runtime_resume() currently ignores that failure. Propagate the error, restore cache-only/dirty state, and unwind the reset and supply state acquired by runtime resume. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704035957.97236-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l32.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index c835088de578..8288fb6d50be 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); regcache_cache_only(cs35l32->regmap, false); - regcache_sync(cs35l32->regmap); + ret = regcache_sync(cs35l32->regmap); + if (ret) { + regcache_cache_only(cs35l32->regmap, true); + regcache_mark_dirty(cs35l32->regmap); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + return ret; + } return 0; } From 9497ea79d71916c2e782b20cbcf9047a58c74a98 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:00:44 +0800 Subject: [PATCH 155/791] ASoC: codecs: wm2200: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. wm2200_runtime_resume() currently ignores that failure. Propagate the error, restore cache-only/dirty state, and unwind the LDO and core supplies acquired by runtime resume. Signed-off-by: Pengpeng Hou Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260704040044.2402-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm2200.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index ba8ce2e6e615..bc78b553b6cb 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) } regcache_cache_only(wm2200->regmap, false); - regcache_sync(wm2200->regmap); + ret = regcache_sync(wm2200->regmap); + if (ret) { + regcache_cache_only(wm2200->regmap, true); + regcache_mark_dirty(wm2200->regmap); + gpiod_set_value_cansleep(wm2200->ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + return ret; + } return 0; } From d5f4c137844ff1366916f96932b033279340a8b2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:01:32 +0800 Subject: [PATCH 156/791] ASoC: codecs: wm5100: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. wm5100_runtime_resume() currently ignores that failure. Propagate the error, restore cache-only/dirty state, and unwind the LDO and core supplies acquired by runtime resume. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040132.8278-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm5100.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index bd94fa53c362..2fe937a93026 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) } regcache_cache_only(wm5100->regmap, false); - regcache_sync(wm5100->regmap); + ret = regcache_sync(wm5100->regmap); + if (ret) { + regcache_cache_only(wm5100->regmap, true); + regcache_mark_dirty(wm5100->regmap); + gpiod_set_value_cansleep(wm5100->ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + return ret; + } return 0; } From 56ce50099e64e78dd6152edb02adf90f6471af78 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:02:20 +0800 Subject: [PATCH 157/791] ASoC: codecs: wm8962: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. wm8962_runtime_resume() currently ignores that failure and continues programming the device. Propagate the error, restore cache-only/dirty state, and unwind the supplies and clock acquired by runtime resume. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040220.12045-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index de18b1f85a32..2db822fc1de7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3937,7 +3937,9 @@ static int wm8962_runtime_resume(struct device *dev) WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, 0); - regcache_sync(wm8962->regmap); + ret = regcache_sync(wm8962->regmap); + if (ret) + goto cache_sync_err; regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, @@ -3955,6 +3957,11 @@ static int wm8962_runtime_resume(struct device *dev) return 0; +cache_sync_err: + regcache_cache_only(wm8962->regmap, true); + regcache_mark_dirty(wm8962->regmap); + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); disable_clock: clk_disable_unprepare(wm8962->pdata.mclk); return ret; From a407338ca621abe7fbc8feba2a4f432d8818b876 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:03:30 +0800 Subject: [PATCH 158/791] ASoC: codecs: cs4349: Propagate regcache_sync() errors regcache_sync() can fail while replaying cached register state during runtime resume. cs4349_runtime_resume() currently ignores that failure and returns success. Propagate the error, restore cache-only/dirty state, and hold reset low again on sync failure. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040330.23616-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/cs4349.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 6ac6d306b054..9a7f70175717 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) gpiod_set_value_cansleep(cs4349->reset_gpio, 1); regcache_cache_only(cs4349->regmap, false); - regcache_sync(cs4349->regmap); + ret = regcache_sync(cs4349->regmap); + if (ret) { + regcache_cache_only(cs4349->regmap, true); + regcache_mark_dirty(cs4349->regmap); + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); + return ret; + } return 0; } From 39dadd459c88d1eb5dc7ccbfc605cf078c162b96 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:04:18 +0800 Subject: [PATCH 159/791] ASoC: codecs: rt1320-sdw: Propagate regcache_sync() errors rt1320_dev_resume() clears cache-only mode for both regmaps and replays cached register state into the device. Both regcache_sync() calls currently ignore their return values, so resume can report success even if either replay failed. Check both sync operations and return the first error. On failure, restore both regmaps to cache-only mode and mark both caches dirty, so a later successful resume attempt starts from a coherent suspended cache state instead of a partially live pair of regmaps. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040418.28181-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 18d7ebff505f..a30fb575918d 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -3626,10 +3626,23 @@ static int rt1320_dev_resume(struct device *dev) return ret; regcache_cache_only(rt1320->regmap, false); - regcache_sync(rt1320->regmap); + ret = regcache_sync(rt1320->regmap); + if (ret) + goto err_sync; + regcache_cache_only(rt1320->mbq_regmap, false); - regcache_sync(rt1320->mbq_regmap); + ret = regcache_sync(rt1320->mbq_regmap); + if (ret) + goto err_sync; + return 0; + +err_sync: + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + regcache_mark_dirty(rt1320->regmap); + regcache_mark_dirty(rt1320->mbq_regmap); + return ret; } static const struct dev_pm_ops rt1320_pm = { From 446a8b7a03a4d666fb66fbf63163e0245e6c087e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:05:30 +0800 Subject: [PATCH 160/791] ASoC: codecs: rt5682-sdw: Propagate regcache_sync() errors rt5682_io_init() and rt5682_dev_resume() both leave cache-only mode before replaying cached register state with regcache_sync(). Both paths ignore the sync result, which can hide a failed hardware restore. Propagate regcache_sync() failures from both paths. The first hardware init path now has a dedicated sync-failure cleanup that restores cache bypass, cache-only, and dirty-cache state before balancing the runtime PM reference. The resume path restores both regmaps to cache-only mode and marks the main cache dirty before returning the error. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040530.40047-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682-sdw.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index dec8c2147d68..e49ec28b6103 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -410,7 +410,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) if (rt5682->first_hw_init) { regcache_cache_bypass(rt5682->regmap, false); regcache_mark_dirty(rt5682->regmap); - regcache_sync(rt5682->regmap); + ret = regcache_sync(rt5682->regmap); + if (ret) + goto err_sync; /* volatile registers */ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, @@ -472,8 +474,16 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt5682->hw_init = true; rt5682->first_hw_init = true; + goto out; + +err_sync: + regcache_cache_bypass(rt5682->regmap, false); + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); err_nodev: +out: pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); @@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); - regcache_sync(rt5682->regmap); + ret = regcache_sync(rt5682->regmap); + if (ret) { + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return ret; + } return 0; } From d20de7409dfa8e3341af7f157cd42130bd4ba3a7 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:06:51 +0800 Subject: [PATCH 161/791] ASoC: codecs: max98396: Unwind supplies on resume failure max98396_resume() enables the core, pvdd, and vbat supplies before leaving cache-only mode, resetting the device, and replaying cached register state. The function currently ignores regcache_sync() failures, and the optional pvdd/vbat enable failures return without unwinding supplies that were already enabled in this resume attempt. Rework the function to use common error labels. This propagates sync failures, restores cache-only/dirty state on failed cache replay, and disables each supply acquired by this resume path before returning the error. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040651.47558-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98396.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 9c1d7213410c..e5a2fe824c03 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) if (max98396->pvdd) { ret = regulator_enable(max98396->pvdd); if (ret < 0) - return ret; + goto err_core_supplies; } if (max98396->vbat) { ret = regulator_enable(max98396->vbat); if (ret < 0) - return ret; + goto err_pvdd; } regcache_cache_only(max98396->regmap, false); max98396_reset(max98396, dev); - regcache_sync(max98396->regmap); + ret = regcache_sync(max98396->regmap); + if (ret < 0) { + regcache_cache_only(max98396->regmap, true); + regcache_mark_dirty(max98396->regmap); + goto err_vbat; + } + return 0; + +err_vbat: + if (max98396->vbat) + regulator_disable(max98396->vbat); +err_pvdd: + if (max98396->pvdd) + regulator_disable(max98396->pvdd); +err_core_supplies: + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + + return ret; } static const struct dev_pm_ops max98396_pm = { From 181bce73427cff76d280f14d691a7d7b95baa919 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 12:07:47 +0800 Subject: [PATCH 162/791] ASoC: codecs: max98090: Propagate runtime regcache_sync() errors max98090_runtime_suspend() puts the regmap into cache-only mode. Runtime resume clears cache-only mode, resets the device, and replays cached register state with regcache_sync(), but currently ignores a failed replay. Return the sync error and restore cache-only/dirty state before failing runtime resume. This deliberately leaves the separate system-resume sync call unchanged because the source does not provide an equally strong paired system suspend/cache-only proof for that path. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704040747.56587-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index bccce322ccc3..60da518b0e57 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2646,12 +2646,18 @@ static void max98090_i2c_remove(struct i2c_client *client) static int max98090_runtime_resume(struct device *dev) { struct max98090_priv *max98090 = dev_get_drvdata(dev); + int ret; regcache_cache_only(max98090->regmap, false); max98090_reset(max98090); - regcache_sync(max98090->regmap); + ret = regcache_sync(max98090->regmap); + if (ret < 0) { + regcache_cache_only(max98090->regmap, true); + regcache_mark_dirty(max98090->regmap); + return ret; + } return 0; } From 5f195b1ae6ca401fb372eb2c3a2604f8bee31f9d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Jun 2026 01:53:55 +0000 Subject: [PATCH 163/791] ASoC: amd: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87ldc6hvil.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 6 ++++++ sound/soc/amd/raven/acp3x-i2s.c | 6 ++++++ sound/soc/amd/vangogh/acp5x-i2s.c | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 283a674c7e2c..bb58a9d34993 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } +static const u64 acp_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { .startup = acp_i2s_startup, .hw_params = acp_i2s_hwparams, @@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { .trigger = acp_i2s_trigger, .set_fmt = acp_i2s_set_fmt, .set_tdm_slot = acp_i2s_set_tdm_slot, + .auto_selectable_formats = &acp_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c index 352485dd98b1..b0147e88ba54 100644 --- a/sound/soc/amd/raven/acp3x-i2s.c +++ b/sound/soc/amd/raven/acp3x-i2s.c @@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static const u64 acp3x_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { .hw_params = acp3x_i2s_hwparams, .trigger = acp3x_i2s_trigger, .set_fmt = acp3x_i2s_set_fmt, .set_tdm_slot = acp3x_i2s_set_tdm_slot, + .auto_selectable_formats = &acp3x_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_component_driver acp3x_dai_component = { diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c index bf719f628617..dbfb87e2fe92 100644 --- a/sound/soc/amd/vangogh/acp5x-i2s.c +++ b/sound/soc/amd/vangogh/acp5x-i2s.c @@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static const u64 acp5x_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { .hw_params = acp5x_i2s_hwparams, .trigger = acp5x_i2s_trigger, .set_fmt = acp5x_i2s_set_fmt, .set_tdm_slot = acp5x_i2s_set_tdm_slot, + .auto_selectable_formats = &acp5x_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_component_driver acp5x_dai_component = { From 946ddf03d0a2ee23f6884a66bfdec3cce07d9011 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Jun 2026 01:54:02 +0000 Subject: [PATCH 164/791] ASoC: codecs: pcm*: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Kirill Marinushkin Link: https://patch.msgid.link/87jyrqhvid.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm1681.c | 7 +++++++ sound/soc/codecs/pcm1754.c | 6 ++++++ sound/soc/codecs/pcm1789.c | 7 +++++++ sound/soc/codecs/pcm179x.c | 6 ++++++ sound/soc/codecs/pcm186x.c | 9 +++++++++ sound/soc/codecs/pcm3060.c | 8 ++++++++ sound/soc/codecs/pcm512x.c | 9 +++++++++ 7 files changed, 52 insertions(+) diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index cb923cecb47f..60fdbe5c4e05 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, return pcm1681_set_deemph(component); } +static const u64 pcm1681_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops pcm1681_dai_ops = { .set_fmt = pcm1681_set_dai_fmt, .hw_params = pcm1681_hw_params, .mute_stream = pcm1681_mute, + .auto_selectable_formats = &pcm1681_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c index b68a528000be..bb15da1c24aa 100644 --- a/sound/soc/codecs/pcm1754.c +++ b/sound/soc/codecs/pcm1754.c @@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; } +static const u64 pcm1754_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; + static const struct snd_soc_dai_ops pcm1754_dai_ops = { .set_fmt = pcm1754_set_dai_fmt, .hw_params = pcm1754_hw_params, .mute_stream = pcm1754_mute_stream, + .auto_selectable_formats = &pcm1754_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_driver pcm1754_dai = { diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 3ab381e9a856..f06e353743aa 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static const u64 pcm1789_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops pcm1789_dai_ops = { .set_fmt = pcm1789_set_dai_fmt, .hw_params = pcm1789_hw_params, .mute_stream = pcm1789_mute, .trigger = pcm1789_trigger, + .auto_selectable_formats = &pcm1789_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index f52ff66b6e64..cb70927872aa 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 pcm179x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; + static const struct snd_soc_dai_ops pcm179x_dai_ops = { .set_fmt = pcm179x_set_dai_fmt, .hw_params = pcm179x_hw_params, .mute_stream = pcm179x_mute, + .auto_selectable_formats = &pcm179x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 0d1103fe4e04..461ec74e8752 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +static const u64 pcm186x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops pcm186x_dai_ops = { .set_sysclk = pcm186x_set_dai_sysclk, .set_tdm_slot = pcm186x_set_tdm_slot, .set_fmt = pcm186x_set_fmt, .hw_params = pcm186x_hw_params, + .auto_selectable_formats = &pcm186x_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver pcm1863_dai = { diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 8974200652e7..f01c46583e89 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -164,10 +164,18 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 pcm3060_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops pcm3060_dai_ops = { .set_sysclk = pcm3060_set_sysclk, .set_fmt = pcm3060_set_fmt, .hw_params = pcm3060_hw_params, + .auto_selectable_formats = &pcm3060_selectable_formats, + .num_auto_selectable_formats = 1, }; #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fdef98ce52f1..12163ca7899a 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1505,12 +1505,21 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) return ret; } +static const u64 pcm512x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops pcm512x_dai_ops = { .startup = pcm512x_dai_startup, .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, .mute_stream = pcm512x_mute, .set_bclk_ratio = pcm512x_set_bclk_ratio, + .auto_selectable_formats = &pcm512x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; From 2b7f2b7a2a36c35fdee1496365ff6a23c22e336f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Jun 2026 01:54:06 +0000 Subject: [PATCH 165/791] ASoC: meson: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/87ik7ahvi9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/t9015.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index da1a93946d67..085f771eb244 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static const u64 t9015_dai_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + static const struct snd_soc_dai_ops t9015_dai_ops = { .set_fmt = t9015_dai_set_fmt, + .auto_selectable_formats = &t9015_dai_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver t9015_dai = { From 5e6f8ad0008a498cbb70a30d4736fd562da73188 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Jun 2026 01:54:10 +0000 Subject: [PATCH 166/791] ASoC: spacemit: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Troy Mitchell Link: https://patch.msgid.link/87h5muhvi6.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/spacemit/k1_i2s.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 8871fc15b29c..cd461f2aa756 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) return 0; } +static const u64 spacemit_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { .probe = spacemit_i2s_dai_probe, .remove = spacemit_i2s_dai_remove, @@ -343,6 +348,8 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { .set_sysclk = spacemit_i2s_set_sysclk, .set_fmt = spacemit_i2s_set_fmt, .trigger = spacemit_i2s_trigger, + .auto_selectable_formats = &spacemit_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver spacemit_i2s_dai = { From 92aa9c7b1d9d318709b3cd769b894844cc6e9a9d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 9 Jul 2026 11:37:40 +0700 Subject: [PATCH 167/791] ASoC: xtensa: Use dev_err_probe() and drop redundant error handling Convert error paths with messages to dev_err_probe(), which combines dev_err() and the return statement while also handling -EPROBE_DEFER for the clock path. Remove the redundant "err:" label and return errors directly. Paths such as platform_get_irq() already log failures in the callee, so they simply return the error code without printing an additional message. Inline the pm_runtime_disable() cleanup at its only call site. No functional change. Signed-off-by: bui duc phuc Reviewed-by: Max Filippov Link: https://patch.msgid.link/20260709043740.329504-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/xtensa/xtfpga-i2s.c | 60 +++++++++++++---------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index 9ad86c54e3ea..fd0990201b40 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) int err, irq; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); - if (!i2s) { - err = -ENOMEM; - goto err; - } + if (!i2s) + return -ENOMEM; + platform_set_drvdata(pdev, i2s); i2s->dev = &pdev->dev; dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); i2s->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(i2s->regs)) { - err = PTR_ERR(i2s->regs); - goto err; - } + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, &xtfpga_i2s_regmap_config); - if (IS_ERR(i2s->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - err = PTR_ERR(i2s->regmap); - goto err; - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); i2s->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(i2s->clk)) { - dev_err(&pdev->dev, "couldn't get clock\n"); - err = PTR_ERR(i2s->clk); - goto err; - } + if (IS_ERR(i2s->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); @@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - err = irq; - goto err; - } + if (irq < 0) + return irq; + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, xtfpga_i2s_threaded_irq_handler, IRQF_SHARED | IRQF_ONESHOT, pdev->name, i2s); - if (err < 0) { - dev_err(&pdev->dev, "request_irq failed\n"); - goto err; - } + if (err < 0) + return err; err = devm_snd_soc_register_component(&pdev->dev, &xtfpga_i2s_component, xtfpga_i2s_dai, ARRAY_SIZE(xtfpga_i2s_dai)); - if (err < 0) { - dev_err(&pdev->dev, "couldn't register component\n"); - goto err; - } + if (err < 0) + return err; + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { err = xtfpga_i2s_runtime_resume(&pdev->dev); - if (err) - goto err_pm_disable; + if (err) { + pm_runtime_disable(&pdev->dev); + return err; + } } - return 0; -err_pm_disable: - pm_runtime_disable(&pdev->dev); -err: - dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); - return err; + return 0; } static void xtfpga_i2s_remove(struct platform_device *pdev) From 14b68141bcffafde22848c18569e821675afe9e6 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 10 Jul 2026 17:21:36 +0700 Subject: [PATCH 168/791] ASoC: xilinx: xlnx_i2s: Use dev_err_probe() and drop redundant error handling Use dev_err_probe() for probe error handling where appropriate to simplify the code and properly handle deferred probe. Also remove redundant error messages when the called helper already reports failures, returning the error directly to avoid duplicate logging. Reviewed-by: Michal Simek Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260710102138.29347-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_i2s.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c index ca915a001ad5..0676da122edd 100644 --- a/sound/soc/xilinx/xlnx_i2s.c +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -185,17 +185,15 @@ static int xlnx_i2s_probe(struct platform_device *pdev) return PTR_ERR(drv_data->base); ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); - if (ret < 0) { - dev_err(dev, "cannot get supported channels\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get supported channels\n"); + drv_data->channels *= 2; ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); - if (ret < 0) { - dev_err(dev, "cannot get data width\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get data width\n"); + switch (drv_data->data_width) { case 16: format = SNDRV_PCM_FMTBIT_S16_LE; @@ -233,10 +231,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, &drv_data->dai_drv, 1); - if (ret) { - dev_err(&pdev->dev, "i2s component registration failed\n"); + if (ret) return ret; - } dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); From cb32e3acb1036b2c2d5a5df1e19986ccd1fff9ed Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 10 Jul 2026 17:21:37 +0700 Subject: [PATCH 169/791] ASoC: xilinx: xlnx_spdif: Preserve devm_request_irq() error codes devm_request_irq() can return various error codes, such as -EINVAL, -ENOTCONN, -ENOMEM, -ENOSYS, and -EBUSY. However, the driver overwrites all of them with -ENODEV, which hides the actual cause of the failure. Also, devm_request_irq() already reports failures internally, so the additional dev_err() call is redundant. Return the original error code and remove the duplicate error message. Signed-off-by: bui duc phuc Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260710102138.29347-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_spdif.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index 017a64ab9f1e..deb7225c1b4b 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -274,10 +274,8 @@ static int xlnx_spdif_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); - if (ret) { - dev_err(dev, "spdif rx irq request failed\n"); - return -ENODEV; - } + if (ret) + return ret; init_waitqueue_head(&ctx->chsts_q); dai_drv = &xlnx_spdif_rx_dai; From f9620327ba7ebe3a3d374bbb0d55a74e4860bc50 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 10 Jul 2026 17:21:38 +0700 Subject: [PATCH 170/791] ASoC: xilinx: xlnx_spdif: Use dev_err_probe() and drop redundant error handling Use dev_err_probe() for probe error handling where appropriate to simplify the code and properly handle deferred probe. Also remove redundant error messages when the called helper already reports failures, returning the error directly to avoid duplicate logging. Signed-off-by: bui duc phuc Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260710102138.29347-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_spdif.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index deb7225c1b4b..ae05818ba064 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -249,21 +249,18 @@ static int xlnx_spdif_probe(struct platform_device *pdev) return -ENOMEM; ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); - if (IS_ERR(ctx->axi_clk)) { - ret = PTR_ERR(ctx->axi_clk); - dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); - return ret; - } + if (IS_ERR(ctx->axi_clk)) + return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), + "failed to get s_axi_aclk\n"); ctx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); - if (ret < 0) { - dev_err(dev, "cannot get SPDIF mode\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); + if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { @@ -282,19 +279,15 @@ static int xlnx_spdif_probe(struct platform_device *pdev) } ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); - if (ret < 0) { - dev_err(dev, "cannot get aud_clk_i value\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); dev_set_drvdata(dev, ctx); ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, dai_drv, 1); - if (ret) { - dev_err(dev, "SPDIF component registration failed\n"); + if (ret) return ret; - } writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); dev_info(dev, "%s DAI registered\n", dai_drv->name); From dd79f1926436ebfebf57b540d21eae5d1773297a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:44:39 +0000 Subject: [PATCH 171/791] ASoC: simple-card: move simple_parse_of() Move simple_parse_of() position. No functional change. This is preparation for code cleanup. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5mixj09.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 76 ++++++++++++++++----------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b4957e025211..456b7ebeb068 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -517,44 +517,6 @@ static int simple_populate_aux(struct simple_util_priv *priv) return simple_ret(priv, ret); } -static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) -{ - struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; - - ret = simple_util_parse_widgets(card, PREFIX); - if (ret < 0) - goto end; - - ret = simple_util_parse_routing(card, PREFIX); - if (ret < 0) - goto end; - - ret = simple_util_parse_pin_switches(card, PREFIX); - if (ret < 0) - goto end; - - /* Single/Muti DAI link(s) & New style of DT node */ - memset(li, 0, sizeof(*li)); - ret = simple_for_each_link(priv, li, - simple_dai_link_of, - simple_dai_link_of_dpcm); - if (ret < 0) - goto end; - - ret = simple_util_parse_card_name(priv, PREFIX); - if (ret < 0) - goto end; - - ret = simple_populate_aux(priv); - if (ret < 0) - goto end; - - ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); -end: - return simple_ret(priv, ret); -} - static int simple_count_noml(struct simple_util_priv *priv, struct device_node *np, struct device_node *codec, @@ -704,6 +666,44 @@ static int simple_soc_probe(struct snd_soc_card *card) return simple_ret(priv, ret); } +static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + int ret; + + ret = simple_util_parse_widgets(card, PREFIX); + if (ret < 0) + goto end; + + ret = simple_util_parse_routing(card, PREFIX); + if (ret < 0) + goto end; + + ret = simple_util_parse_pin_switches(card, PREFIX); + if (ret < 0) + goto end; + + /* Single/Muti DAI link(s) & New style of DT node */ + memset(li, 0, sizeof(*li)); + ret = simple_for_each_link(priv, li, + simple_dai_link_of, + simple_dai_link_of_dpcm); + if (ret < 0) + goto end; + + ret = simple_util_parse_card_name(priv, PREFIX); + if (ret < 0) + goto end; + + ret = simple_populate_aux(priv); + if (ret < 0) + goto end; + + ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); +end: + return simple_ret(priv, ret); +} + static int simple_probe(struct platform_device *pdev) { struct simple_util_priv *priv; From 7f20b9b05b3abb619b6c951dfb7303525efc13c0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:44:48 +0000 Subject: [PATCH 172/791] ASoC: simple-card: merge extra method into simple_parse_of() Current simple_probe() calls many code before/after simple_parse_of(). This is because it had supported platform style probe, but is no longer exist. We can merge all into simple_parse_of(), same as Audio Graph Card/Cars2. No functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87fr22xizz.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 82 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 456b7ebeb068..20e6edd1d7d3 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -666,10 +666,31 @@ static int simple_soc_probe(struct snd_soc_card *card) return simple_ret(priv, ret); } -static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) +static int simple_parse_of(struct simple_util_priv *priv) { struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; + struct device *dev = card->dev; + int ret = -EINVAL; + + if (!dev) + goto end; + + ret = -ENOMEM; + struct link_info *li __free(kfree) = kzalloc_obj(*li); + if (!li) + goto end; + + ret = simple_get_dais_count(priv, li); + if (ret < 0) + goto end; + + ret = -EINVAL; + if (!li->link) + goto end; + + ret = simple_util_init_priv(priv, li); + if (ret < 0) + goto end; ret = simple_util_parse_widgets(card, PREFIX); if (ret < 0) @@ -689,17 +710,30 @@ static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) simple_dai_link_of, simple_dai_link_of_dpcm); if (ret < 0) - goto end; + goto err; ret = simple_util_parse_card_name(priv, PREFIX); if (ret < 0) - goto end; + goto err; ret = simple_populate_aux(priv); if (ret < 0) - goto end; + goto err; ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); + if (ret < 0) + goto err; + + snd_soc_card_set_drvdata(card, priv); + + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +err: + if (ret < 0) { + simple_util_clean_reference(card); + return dev_err_probe(dev, ret, "parse error\n"); + } end: return simple_ret(priv, ret); } @@ -709,7 +743,6 @@ static int simple_probe(struct platform_device *pdev) struct simple_util_priv *priv; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int ret; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -722,42 +755,7 @@ static int simple_probe(struct platform_device *pdev) card->probe = simple_soc_probe; card->driver_name = "simple-card"; - ret = -ENOMEM; - struct link_info *li __free(kfree) = kzalloc_obj(*li); - if (!li) - goto end; - - ret = simple_get_dais_count(priv, li); - if (ret < 0) - goto end; - - ret = -EINVAL; - if (!li->link) - goto end; - - ret = simple_util_init_priv(priv, li); - if (ret < 0) - goto end; - - ret = simple_parse_of(priv, li); - if (ret < 0) { - dev_err_probe(dev, ret, "parse error\n"); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - simple_util_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - simple_util_clean_reference(card); -end: - return dev_err_probe(dev, ret, "parse error\n"); + return simple_parse_of(priv); } static const struct of_device_id simple_of_match[] = { From aa7fe27fda3241d33cd3af5cdeef4094c162cabb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:44:57 +0000 Subject: [PATCH 173/791] ASoC: audio-graph-card2: Tidyup audio_graph2_parse_of() around error audio_graph2_parse_of() have not been calling simple_util_clean_reference(). Call it. And it is already calling dev_err_probe() in error case, no need to call graph_ret() in success case. Tidyup it. Let's keep same style with simple-card/audio-graph-card. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87echmxizq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 0202ed0ee78e..c5ada1f83881 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, struct graph2_custom_hooks *hooks) { struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; + int ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc_obj(*li); if (!li) - return -ENOMEM; + goto end; card->probe = graph_util_card_probe; card->owner = THIS_MODULE; @@ -1316,33 +1316,33 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if ((hooks) && (hooks)->hook_pre) { ret = (hooks)->hook_pre(priv); if (ret < 0) - goto err; + goto end; } ret = graph_for_each_link(priv, hooks, li, graph_count); if (!li->link) ret = -EINVAL; if (ret < 0) - goto err; + goto end; ret = simple_util_init_priv(priv, li); if (ret < 0) - goto err; + goto end; priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); if (IS_ERR(priv->pa_gpio)) { ret = PTR_ERR(priv->pa_gpio); dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - goto err; + goto end; } ret = simple_util_parse_widgets(card, NULL); if (ret < 0) - goto err; + goto end; ret = simple_util_parse_routing(card, NULL); if (ret < 0) - goto err; + goto end; memset(li, 0, sizeof(*li)); ret = graph_for_each_link(priv, hooks, li, graph_link); @@ -1369,9 +1369,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, ret = devm_snd_soc_register_card(dev, card); err: - if (ret < 0) - dev_err_probe(dev, ret, "parse error\n"); - + if (ret < 0) { + simple_util_clean_reference(card); + return dev_err_probe(dev, ret, "parse error\n"); + } +end: return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph2_parse_of); From 30bb98d530d0c04893d516cf65204d0086e08970 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:06 +0000 Subject: [PATCH 174/791] ASoC: audio-graph-card: Tidyup audio_graph_parse_of() around error It is already calling dev_err_probe() in error case, no need to call graph_ret() in success case. Tidyup it. Let's keep same style with simple-card/audio-graph-card2. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87cxx6xizh.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 18ce4ee06350..42e1b77fa65e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -603,14 +603,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) simple_util_debug_info(priv); ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; err: - simple_util_clean_reference(card); + if (ret < 0) { + simple_util_clean_reference(card); + return dev_err_probe(dev, ret, "parse error\n"); + } end: - return dev_err_probe(dev, ret, "parse error\n"); + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); From 7ad9d917e251be70cfa9733f990cfa5f7c49f9b5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:14 +0000 Subject: [PATCH 175/791] ASoC: simple_card_utils: add simple_util_parse_property() We have simple_util_parse_{routing/widgets/pin_switches}(). These are doing almost same things, but has each own implementation. Les't adds new simple_util_parse_property() and share the code. To be more easy cleanup later, change the required parameter from "card" to "priv". Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87bjcqxiza.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 27 +++++++--- sound/soc/generic/audio-graph-card.c | 4 +- sound/soc/generic/audio-graph-card2.c | 4 +- sound/soc/generic/simple-card-utils.c | 72 ++++++++------------------- sound/soc/generic/simple-card.c | 6 +-- 5 files changed, 49 insertions(+), 64 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 915e6ae5f68d..dfa1b5fb7aa8 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -189,12 +189,27 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); int simple_util_get_sample_fmt(struct simple_util_data *data); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property); +static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, + prefix, "routing"); +} + +static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, + prefix, "widgets"); +} + +static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, + prefix, "pin-switches"); +} + int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 42e1b77fa65e..273b0c82ebea 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_routing(priv, NULL); if (ret < 0) goto end; diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index c5ada1f83881..b4ae9bb860a1 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1336,11 +1336,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_routing(priv, NULL); if (ret < 0) goto end; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index e5cb602fd248..522dd1e3c96a 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, } EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct device_node *node = card->dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + snprintf(prop, sizeof(prop), "%s%s", prefix, property); + + /* no property is not error */ + if (!of_property_present(node, prop)) + return 0; + + return func(card, prop); +} +EXPORT_SYMBOL_GPL(simple_util_parse_property); + int simple_util_parse_card_name(struct simple_util_priv *priv, char *prefix) { @@ -747,57 +768,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(simple_util_clean_reference); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - - if (!of_property_present(node, prop)) - return 0; - - return snd_soc_of_parse_audio_routing(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_routing); - -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); - - if (of_property_present(node, prop)) - return snd_soc_of_parse_audio_simple_widgets(card, prop); - - /* no widgets is not error */ - return 0; -} -EXPORT_SYMBOL_GPL(simple_util_parse_widgets); - -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix) -{ - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - - return snd_soc_of_parse_pin_switches(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); - int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 20e6edd1d7d3..6e432733649f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -692,15 +692,15 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto end; - ret = simple_util_parse_widgets(card, PREFIX); + ret = simple_util_parse_widgets(priv, PREFIX); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, PREFIX); + ret = simple_util_parse_routing(priv, PREFIX); if (ret < 0) goto end; - ret = simple_util_parse_pin_switches(card, PREFIX); + ret = simple_util_parse_pin_switches(priv, PREFIX); if (ret < 0) goto end; From d0ab37b02826fc4b2b78cb3f47416cc2dbbd9e7e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:32 +0000 Subject: [PATCH 176/791] ASoC: simple_card_utils: add simple_util_parse_aux_devs() We are using snd_soc_of_parse_aux_devs() directly, but can use simple_util_parse_property(). use it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878q7uxiyr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 5 +++++ sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index dfa1b5fb7aa8..ff9747c6aa49 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -210,6 +210,11 @@ static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, prefix, "pin-switches"); } +static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, + prefix, "aux-devs"); +} int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index b4ae9bb860a1..4dee28276671 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1363,7 +1363,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); - ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + ret = simple_util_parse_aux_devs(priv, NULL); if (ret < 0) goto err; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6e432733649f..4dd05e726213 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -720,7 +720,7 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto err; - ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); + ret = simple_util_parse_aux_devs(priv, PREFIX); if (ret < 0) goto err; From 27ecf4da5ad3845b773508917e71f5a07b149077 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:40 +0000 Subject: [PATCH 177/791] ASoC: simple-card: tidyup simple_util_parse_xxx() in simple_parse_of() simple_parse_of() calls simple_util_parse_xxx(), but are random. Let's gather them all in one place. Let's keep same style with audio-graph-card/audio-graph-card2. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/877bnexiyj.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 4dd05e726213..a67f4aad0188 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -704,6 +704,14 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto end; + ret = simple_util_parse_card_name(priv, PREFIX); + if (ret < 0) + goto err; + + ret = simple_util_parse_aux_devs(priv, PREFIX); + if (ret < 0) + goto err; + /* Single/Muti DAI link(s) & New style of DT node */ memset(li, 0, sizeof(*li)); ret = simple_for_each_link(priv, li, @@ -712,18 +720,10 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto err; - ret = simple_util_parse_card_name(priv, PREFIX); - if (ret < 0) - goto err; - ret = simple_populate_aux(priv); if (ret < 0) goto err; - ret = simple_util_parse_aux_devs(priv, PREFIX); - if (ret < 0) - goto err; - snd_soc_card_set_drvdata(card, priv); simple_util_debug_info(priv); From b8081307f5c9d662ed5afbf42e809273dd9af8be Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:48 +0000 Subject: [PATCH 178/791] ASoC: audio-graph-card: tidyup simple_util_parse_xxx() in audio_graph_parse_of() audio_graph_parse_of() calls simple_util_parse_xxx(), but are random. Let's gather them all in one place. Let's keep same style with simple-card/audio-graph-card2. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/875x2yxiyc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 273b0c82ebea..f5e36c777ac6 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -587,6 +587,10 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto end; + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; + memset(li, 0, sizeof(*li)); ret = graph_for_each_link(priv, li, graph_dai_link_of, @@ -594,10 +598,6 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; - snd_soc_card_set_drvdata(card, priv); simple_util_debug_info(priv); From fa6222d5e1219468301370c8b316f9b729ee600b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:55 +0000 Subject: [PATCH 179/791] ASoC: audio-graph-card2: tidyup simple_util_parse_xxx() in audio_graph2_parse_of() audio_graph2_parse_of() calls simple_util_parse_xxx(), but are random. Let's gather them all in one place. Let's keep same style with simple-card/audio-graph-card. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874iiixiy5.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 4dee28276671..9ef7373a6ca6 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1344,12 +1344,16 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if (ret < 0) goto end; - memset(li, 0, sizeof(*li)); - ret = graph_for_each_link(priv, hooks, li, graph_link); + ret = simple_util_parse_card_name(priv, NULL); if (ret < 0) goto err; - ret = simple_util_parse_card_name(priv, NULL); + ret = simple_util_parse_aux_devs(priv, NULL); + if (ret < 0) + goto err; + + memset(li, 0, sizeof(*li)); + ret = graph_for_each_link(priv, hooks, li, graph_link); if (ret < 0) goto err; @@ -1363,10 +1367,6 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); - ret = simple_util_parse_aux_devs(priv, NULL); - if (ret < 0) - goto err; - ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { From 279bfbb150aef95a3ee281cdd9ebefdddfb43a1e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:46:02 +0000 Subject: [PATCH 180/791] ASoC: simple-card-utils: tidyup simple_util_init_aux_jacks() Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/8733y2xixy.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 +-- sound/soc/generic/simple-card-utils.c | 4 ++-- sound/soc/generic/simple-card.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index ff9747c6aa49..5e2e91578bf5 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -219,8 +219,7 @@ static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, char *pin); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, - char *prefix); +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); int simple_util_init_priv(struct simple_util_priv *priv, struct link_info *li); void simple_util_remove(struct platform_device *pdev); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 522dd1e3c96a..44632759b185 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -824,9 +824,9 @@ int simple_util_init_jack(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(simple_util_init_jack); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) { - struct snd_soc_card *card = simple_priv_to_card(priv); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_component *component; int found_jack_index = 0; int type = 0; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index a67f4aad0188..49289cd655ea 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -661,7 +661,7 @@ static int simple_soc_probe(struct snd_soc_card *card) if (ret < 0) goto end; - ret = simple_util_init_aux_jacks(priv, PREFIX); + ret = simple_util_init_aux_jacks(card, PREFIX); end: return simple_ret(priv, ret); } From 6f5c4f6ffa8bd87803145d03b1ad36d4fa50d562 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:46:09 +0000 Subject: [PATCH 181/791] ASoC: simple-card-utils: tidyup simple_util_clean_reference() Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/871pdmxixq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 +- sound/soc/generic/audio-graph-card.c | 2 +- sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card-utils.c | 6 ++++-- sound/soc/generic/simple-card.c | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 5e2e91578bf5..bd8c3a033577 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, int is_single_links); -void simple_util_clean_reference(struct snd_soc_card *card); +void simple_util_clean_reference(struct simple_util_priv *priv); void simple_util_parse_convert(struct device_node *np, char *prefix, struct simple_util_data *data); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index f5e36c777ac6..73ea562ce52b 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -605,7 +605,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 9ef7373a6ca6..e3e92025b317 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1370,7 +1370,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 44632759b185..42019daa5e04 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -752,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, } EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); -void simple_util_clean_reference(struct snd_soc_card *card) +void simple_util_clean_reference(struct simple_util_priv *priv) { struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link_component *cpu; struct snd_soc_dai_link_component *codec; + struct snd_soc_card *card = simple_priv_to_card(priv); int i, j; for_each_card_prelinks(card, i, dai_link) { @@ -996,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); void simple_util_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - simple_util_clean_reference(card); + simple_util_clean_reference(priv); } EXPORT_SYMBOL_GPL(simple_util_remove); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 49289cd655ea..abfbc9fd7c6d 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -731,7 +731,7 @@ static int simple_parse_of(struct simple_util_priv *priv) ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: From af58a647f9c9674535f62aa1d04b681317da0a26 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Jul 2026 19:23:26 +0200 Subject: [PATCH 182/791] ASoC: renesas: adg: Drop redundant NULL check on clk_get and clk_register_fixed_rate devm_clk_get() and clk_register_fixed_rate() do not return NULL (only valid clock or ERR pointer), so simplify the code to drop redundant IS_ERR_OR_NULL(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260705172325.118926-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/adg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 53efd1be5139..2a74d89ba39d 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, struct clk *clk; clk = clk_register_fixed_rate(dev, name, parent, 0, 0); - if (IS_ERR_OR_NULL(clk)) { + if (IS_ERR(clk)) { dev_err(dev, "create null clk error\n"); return ERR_CAST(clk); } @@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) * No "adg" is not error */ clk = devm_clk_get(dev, "adg"); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) clk = rsnd_adg_null_clk_get(priv); adg->adg = clk; @@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) for (i = 0; i < clkin_size; i++) { clk = devm_clk_get(dev, clkin_name[i]); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) clk = rsnd_adg_null_clk_get(priv); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) goto err; adg->clkin[i] = clk; From 6ef98181eac3ed8f4d96170d7115fc4dc1e0d43d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Jul 2026 19:23:27 +0200 Subject: [PATCH 183/791] ASoC: codecs: es9356: Constify regmap_sdw_mbq_cfg Static 'struct regmap_sdw_mbq_cfg' is not modified so can be changed to const for more safety. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260705172325.118926-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index 1122455aab77..f574b3d6cb3c 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -671,7 +671,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) } } -static struct regmap_sdw_mbq_cfg es9356_mbq_config = { +static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { .mbq_size = es9356_sdca_mbq_size, }; From b9b23e72abef91ab4689f1467cefc2517042ab26 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:11 +0530 Subject: [PATCH 184/791] ASoC: codecs: lpass-tx-macro: switch to PM clock framework for runtime PM Convert the LPASS TX macro codec driver to runtime PM clock management using the PM clock framework. Replace manual macro/dcodec/mclk/npl/fsgen clock toggling with PM clock helpers and runtime PM callbacks. Keep the SWR gate runtime PM reference from SWR clock enable until disable so autosuspend does not gate clocks while SWR is still prepared. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_TX_MACRO since this patch introduces PM clock APIs. Tighten error unwind by checking pm_runtime_put_sync_suspend() in probe and by restoring regcache state if pm_clk_resume()/regcache_sync() fails. Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-1-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 + sound/soc/codecs/lpass-tx-macro.c | 114 ++++++++++++------------------ 2 files changed, 49 insertions(+), 67 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..160c52258d95 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2909,12 +2909,14 @@ config SND_SOC_LPASS_VA_MACRO config SND_SOC_LPASS_RX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_TX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index f7d168f557dd..fc073a556fb5 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -2149,17 +2150,20 @@ static int swclk_gate_enable(struct clk_hw *hw) struct regmap *regmap = tx->regmap; int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_runtime_resume_and_get(tx->dev); + if (ret < 0) + return ret; + + ret = tx_macro_mclk_enable(tx, true); if (ret) { - dev_err(tx->dev, "failed to enable mclk\n"); + pm_runtime_put_autosuspend(tx->dev); return ret; } - tx_macro_mclk_enable(tx, true); - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); + return 0; } @@ -2172,7 +2176,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_TX_SWR_CLK_EN_MASK, 0x0); tx_macro_mclk_enable(tx, false); - clk_disable_unprepare(tx->mclk); + pm_runtime_put_autosuspend(tx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -2318,25 +2322,23 @@ static int tx_macro_probe(struct platform_device *pdev) clk_set_rate(tx->mclk, MCLK_FREQ); clk_set_rate(tx->npl, MCLK_FREQ); - ret = clk_prepare_enable(tx->macro); + ret = devm_pm_clk_create(dev); if (ret) goto err; - ret = clk_prepare_enable(tx->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + goto err; - ret = clk_prepare_enable(tx->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + goto err; - ret = clk_prepare_enable(tx->npl); - if (ret) - goto err_npl; - - ret = clk_prepare_enable(tx->fsgen); - if (ret) - goto err_fsgen; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; /* reset soundwire block */ @@ -2356,30 +2358,21 @@ static int tx_macro_probe(struct platform_device *pdev) tx_macro_dai, ARRAY_SIZE(tx_macro_dai)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = tx_macro_register_mclk_output(tx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(tx->fsgen); -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); -err_mclk: - clk_disable_unprepare(tx->dcodec); -err_dcodec: - clk_disable_unprepare(tx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); err: lpass_macro_pds_exit(tx->pds); @@ -2390,25 +2383,23 @@ static void tx_macro_remove(struct platform_device *pdev) { struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(tx->macro); - clk_disable_unprepare(tx->dcodec); - clk_disable_unprepare(tx->mclk); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->fsgen); - lpass_macro_pds_exit(tx->pds); } static int tx_macro_runtime_suspend(struct device *dev) { struct tx_macro *tx = dev_get_drvdata(dev); + int ret; regcache_cache_only(tx->regmap, true); - regcache_mark_dirty(tx->regmap); - clk_disable_unprepare(tx->fsgen); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(tx->regmap, false); + return ret; + } + + regcache_mark_dirty(tx->regmap); return 0; } @@ -2418,34 +2409,23 @@ static int tx_macro_runtime_resume(struct device *dev) struct tx_macro *tx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); return ret; } - ret = clk_prepare_enable(tx->npl); - if (ret) { - dev_err(dev, "unable to prepare npl\n"); - goto err_npl; - } - - ret = clk_prepare_enable(tx->fsgen); - if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; - } - regcache_cache_only(tx->regmap, false); - regcache_sync(tx->regmap); + ret = regcache_sync(tx->regmap); + if (ret) { + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + pm_clk_suspend(dev); + return ret; + } return 0; -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); - - return ret; } static const struct dev_pm_ops tx_macro_pm_ops = { From b05482e7ce1b110f86b08a99768ac41e4c9e4dfa Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:12 +0530 Subject: [PATCH 185/791] ASoC: codecs: lpass-rx-macro: switch to PM clock framework for runtime PM Convert the LPASS RX macro codec driver to runtime PM clock management using the PM clock framework. Replace manual macro/dcodec/mclk/npl/fsgen clock toggling with PM clock helpers and runtime PM callbacks. Keep the SWR gate runtime PM reference from SWR clock enable until disable so autosuspend does not gate clocks while SWR is still prepared. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_RX_MACRO since this patch introduces PM clock APIs. Tighten error unwind by checking pm_runtime_put_sync_suspend() in probe and by restoring regcache state if pm_clk_resume()/regcache_sync() fails. Drop the now-empty rx_macro_remove() callback since all clock cleanup is handled by PM clock framework and devm. Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-2-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 113 +++++++++++------------------- 1 file changed, 41 insertions(+), 72 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 6233aa9f5bc6..927f75050c0f 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -3671,11 +3672,9 @@ static int swclk_gate_enable(struct clk_hw *hw) struct rx_macro *rx = to_rx_macro(hw); int ret; - ret = clk_prepare_enable(rx->mclk); - if (ret) { - dev_err(rx->dev, "unable to prepare mclk\n"); + ret = pm_runtime_resume_and_get(rx->dev); + if (ret < 0) return ret; - } rx_macro_mclk_enable(rx, true); @@ -3693,7 +3692,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_RX_SWR_CLK_EN_MASK, 0); rx_macro_mclk_enable(rx, false); - clk_disable_unprepare(rx->mclk); + pm_runtime_put_autosuspend(rx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -3867,25 +3866,23 @@ static int rx_macro_probe(struct platform_device *pdev) clk_set_rate(rx->mclk, MCLK_FREQ); clk_set_rate(rx->npl, MCLK_FREQ); - ret = clk_prepare_enable(rx->macro); + ret = devm_pm_clk_create(dev); if (ret) return ret; - ret = clk_prepare_enable(rx->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; - ret = clk_prepare_enable(rx->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + return ret; - ret = clk_prepare_enable(rx->npl); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_npl; - - ret = clk_prepare_enable(rx->fsgen); - if (ret) - goto err_fsgen; + return ret; /* reset swr block */ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, @@ -3902,46 +3899,25 @@ static int rx_macro_probe(struct platform_device *pdev) rx_macro_dai, ARRAY_SIZE(rx_macro_dai)); if (ret) - goto err_clkout; - - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = rx_macro_register_mclk_output(rx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(rx->fsgen); -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); -err_mclk: - clk_disable_unprepare(rx->dcodec); -err_dcodec: - clk_disable_unprepare(rx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); return ret; } -static void rx_macro_remove(struct platform_device *pdev) -{ - struct rx_macro *rx = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(rx->mclk); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->macro); - clk_disable_unprepare(rx->dcodec); -} - static const struct of_device_id rx_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-rx-macro", @@ -3969,13 +3945,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); static int rx_macro_runtime_suspend(struct device *dev) { struct rx_macro *rx = dev_get_drvdata(dev); + int ret; regcache_cache_only(rx->regmap, true); - regcache_mark_dirty(rx->regmap); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(rx->regmap, false); + return ret; + } + + regcache_mark_dirty(rx->regmap); return 0; } @@ -3985,33 +3965,23 @@ static int rx_macro_runtime_resume(struct device *dev) struct rx_macro *rx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(rx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); return ret; } - ret = clk_prepare_enable(rx->npl); - if (ret) { - dev_err(dev, "unable to prepare mclkx2\n"); - goto err_npl; - } - - ret = clk_prepare_enable(rx->fsgen); - if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; - } regcache_cache_only(rx->regmap, false); - regcache_sync(rx->regmap); + ret = regcache_sync(rx->regmap); + if (ret) { + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + pm_clk_suspend(dev); + return ret; + } return 0; -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); - - return ret; } static const struct dev_pm_ops rx_macro_pm_ops = { @@ -4026,7 +3996,6 @@ static struct platform_driver rx_macro_driver = { .pm = pm_ptr(&rx_macro_pm_ops), }, .probe = rx_macro_probe, - .remove = rx_macro_remove, }; module_platform_driver(rx_macro_driver); From b8ca90fafe6adc401601f91d1394ba76bacf67ed Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:13 +0530 Subject: [PATCH 186/791] ASoC: codecs: lpass-{tx,rx}-macro: check clk_set_rate() return value clk_set_rate() returns 0 on success or a negative errno on failure but the TX and RX macro probe functions were ignoring it. Check the return value and bail out of probe on failure. Suggested-by: Konrad Dybcio Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-3-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 9 +++++++-- sound/soc/codecs/lpass-tx-macro.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 927f75050c0f..388cebf461a3 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3863,8 +3863,13 @@ static int rx_macro_probe(struct platform_device *pdev) rx->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(rx->mclk, MCLK_FREQ); - clk_set_rate(rx->npl, MCLK_FREQ); + ret = clk_set_rate(rx->mclk, MCLK_FREQ); + if (ret) + return ret; + + ret = clk_set_rate(rx->npl, MCLK_FREQ); + if (ret) + return ret; ret = devm_pm_clk_create(dev); if (ret) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index fc073a556fb5..b56605639a24 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -2319,8 +2319,13 @@ static int tx_macro_probe(struct platform_device *pdev) tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; /* set MCLK and NPL rates */ - clk_set_rate(tx->mclk, MCLK_FREQ); - clk_set_rate(tx->npl, MCLK_FREQ); + ret = clk_set_rate(tx->mclk, MCLK_FREQ); + if (ret) + goto err; + + ret = clk_set_rate(tx->npl, MCLK_FREQ); + if (ret) + goto err; ret = devm_pm_clk_create(dev); if (ret) From 307835a6eb50e548e58ee604bf91909d4faa4203 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 21:06:24 +0700 Subject: [PATCH 187/791] ALSA: core: Clean up file_operations definitions Align file_operations initializers with tabs to match the standard kernel coding style and improve readability. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708140624.562403-1-phucduc.bui@gmail.com Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 18 +++++++------- sound/core/control.c | 19 +++++++-------- sound/core/hwdep.c | 23 +++++++++--------- sound/core/init.c | 23 +++++++++--------- sound/core/jack.c | 38 ++++++++++++++--------------- sound/core/oss/mixer_oss.c | 13 +++++----- sound/core/oss/pcm_oss.c | 21 ++++++++-------- sound/core/pcm_native.c | 44 +++++++++++++++++----------------- sound/core/rawmidi.c | 16 ++++++------- sound/core/seq/oss/seq_oss.c | 21 ++++++++-------- sound/core/seq/seq_clientmgr.c | 19 +++++++-------- sound/core/sound.c | 9 ++++--- sound/core/timer.c | 25 ++++++++++--------- 13 files changed, 140 insertions(+), 149 deletions(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index ea699491f0c3..23d62fede06e 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1393,17 +1393,17 @@ static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, #endif static const struct file_operations snd_compr_file_ops = { - .owner = THIS_MODULE, - .open = snd_compr_open, - .release = snd_compr_free, - .write = snd_compr_write, - .read = snd_compr_read, - .unlocked_ioctl = snd_compr_ioctl, + .owner = THIS_MODULE, + .open = snd_compr_open, + .release = snd_compr_free, + .write = snd_compr_write, + .read = snd_compr_read, + .unlocked_ioctl = snd_compr_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = snd_compr_ioctl_compat, + .compat_ioctl = snd_compr_ioctl_compat, #endif - .mmap = snd_compr_mmap, - .poll = snd_compr_poll, + .mmap = snd_compr_mmap, + .poll = snd_compr_poll, }; static int snd_compress_dev_register(struct snd_device *device) diff --git a/sound/core/control.c b/sound/core/control.c index 73d7ba0f509f..1116a40d11ae 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -2335,16 +2335,15 @@ EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer); * INIT PART */ -static const struct file_operations snd_ctl_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_ctl_read, - .open = snd_ctl_open, - .release = snd_ctl_release, - .poll = snd_ctl_poll, - .unlocked_ioctl = snd_ctl_ioctl, - .compat_ioctl = snd_ctl_ioctl_compat, - .fasync = snd_ctl_fasync, +static const struct file_operations snd_ctl_f_ops = { + .owner = THIS_MODULE, + .read = snd_ctl_read, + .open = snd_ctl_open, + .release = snd_ctl_release, + .poll = snd_ctl_poll, + .unlocked_ioctl = snd_ctl_ioctl, + .compat_ioctl = snd_ctl_ioctl_compat, + .fasync = snd_ctl_fasync, }; /* call lops under rwsems; called from snd_ctl_dev_*() below() */ diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 352047e0b33e..c6de5345e946 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -323,18 +323,17 @@ static int snd_hwdep_control_ioctl(struct snd_card *card, */ -static const struct file_operations snd_hwdep_f_ops = -{ - .owner = THIS_MODULE, - .llseek = snd_hwdep_llseek, - .read = snd_hwdep_read, - .write = snd_hwdep_write, - .open = snd_hwdep_open, - .release = snd_hwdep_release, - .poll = snd_hwdep_poll, - .unlocked_ioctl = snd_hwdep_ioctl, - .compat_ioctl = snd_hwdep_ioctl_compat, - .mmap = snd_hwdep_mmap, +static const struct file_operations snd_hwdep_f_ops = { + .owner = THIS_MODULE, + .llseek = snd_hwdep_llseek, + .read = snd_hwdep_read, + .write = snd_hwdep_write, + .open = snd_hwdep_open, + .release = snd_hwdep_release, + .poll = snd_hwdep_poll, + .unlocked_ioctl = snd_hwdep_ioctl, + .compat_ioctl = snd_hwdep_ioctl_compat, + .mmap = snd_hwdep_mmap, }; static void snd_hwdep_free(struct snd_hwdep *hwdep) diff --git a/sound/core/init.c b/sound/core/init.c index 56dde5bd73c4..8c5850ce08a0 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -466,20 +466,19 @@ static int snd_disconnect_fasync(int fd, struct file *file, int on) return -ENODEV; } -static const struct file_operations snd_shutdown_f_ops = -{ - .owner = THIS_MODULE, - .llseek = snd_disconnect_llseek, - .read = snd_disconnect_read, - .write = snd_disconnect_write, - .release = snd_disconnect_release, - .poll = snd_disconnect_poll, - .unlocked_ioctl = snd_disconnect_ioctl, +static const struct file_operations snd_shutdown_f_ops = { + .owner = THIS_MODULE, + .llseek = snd_disconnect_llseek, + .read = snd_disconnect_read, + .write = snd_disconnect_write, + .release = snd_disconnect_release, + .poll = snd_disconnect_poll, + .unlocked_ioctl = snd_disconnect_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = snd_disconnect_ioctl, + .compat_ioctl = snd_disconnect_ioctl, #endif - .mmap = snd_disconnect_mmap, - .fasync = snd_disconnect_fasync + .mmap = snd_disconnect_mmap, + .fasync = snd_disconnect_fasync }; /** diff --git a/sound/core/jack.c b/sound/core/jack.c index 96e0733ede77..0f04018181ac 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -303,41 +303,41 @@ static ssize_t jack_type_read(struct file *file, } static const struct file_operations jack_type_fops = { - .open = simple_open, - .read = jack_type_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_type_read, + .llseek = default_llseek, }; #endif static const struct file_operations sw_inject_enable_fops = { - .open = simple_open, - .read = sw_inject_enable_read, - .write = sw_inject_enable_write, - .llseek = default_llseek, + .open = simple_open, + .read = sw_inject_enable_read, + .write = sw_inject_enable_write, + .llseek = default_llseek, }; static const struct file_operations jackin_inject_fops = { - .open = simple_open, - .write = jackin_inject_write, - .llseek = default_llseek, + .open = simple_open, + .write = jackin_inject_write, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_id_fops = { - .open = simple_open, - .read = jack_kctl_id_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_id_read, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_mask_bits_fops = { - .open = simple_open, - .read = jack_kctl_mask_bits_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_mask_bits_read, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_status_fops = { - .open = simple_open, - .read = jack_kctl_status_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_status_read, + .llseek = default_llseek, }; static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 3a1dd1edd26d..ff9d7fd60a7e 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -405,13 +405,12 @@ static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd, * REGISTRATION PART */ -static const struct file_operations snd_mixer_oss_f_ops = -{ - .owner = THIS_MODULE, - .open = snd_mixer_oss_open, - .release = snd_mixer_oss_release, - .unlocked_ioctl = snd_mixer_oss_ioctl, - .compat_ioctl = snd_mixer_oss_ioctl_compat, +static const struct file_operations snd_mixer_oss_f_ops = { + .owner = THIS_MODULE, + .open = snd_mixer_oss_open, + .release = snd_mixer_oss_release, + .unlocked_ioctl = snd_mixer_oss_ioctl, + .compat_ioctl = snd_mixer_oss_ioctl_compat, }; /* diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 8ae43755fb9b..0924f1ff1ae7 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -3127,17 +3127,16 @@ static inline void snd_pcm_oss_proc_done(struct snd_pcm *pcm) * ENTRY functions */ -static const struct file_operations snd_pcm_oss_f_reg = -{ - .owner = THIS_MODULE, - .read = snd_pcm_oss_read, - .write = snd_pcm_oss_write, - .open = snd_pcm_oss_open, - .release = snd_pcm_oss_release, - .poll = snd_pcm_oss_poll, - .unlocked_ioctl = snd_pcm_oss_ioctl, - .compat_ioctl = snd_pcm_oss_ioctl_compat, - .mmap = snd_pcm_oss_mmap, +static const struct file_operations snd_pcm_oss_f_reg = { + .owner = THIS_MODULE, + .read = snd_pcm_oss_read, + .write = snd_pcm_oss_write, + .open = snd_pcm_oss_open, + .release = snd_pcm_oss_release, + .poll = snd_pcm_oss_poll, + .unlocked_ioctl = snd_pcm_oss_ioctl, + .compat_ioctl = snd_pcm_oss_ioctl_compat, + .mmap = snd_pcm_oss_mmap, }; static void register_oss_dsp(struct snd_pcm *pcm, int index) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4fe5fab8d096..07f61e3386c7 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -4236,29 +4236,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, const struct file_operations snd_pcm_f_ops[2] = { { - .owner = THIS_MODULE, - .write = snd_pcm_write, - .write_iter = snd_pcm_writev, - .open = snd_pcm_playback_open, - .release = snd_pcm_release, - .poll = snd_pcm_poll, - .unlocked_ioctl = snd_pcm_ioctl, - .compat_ioctl = snd_pcm_ioctl_compat, - .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, - .get_unmapped_area = snd_pcm_get_unmapped_area, + .owner = THIS_MODULE, + .write = snd_pcm_write, + .write_iter = snd_pcm_writev, + .open = snd_pcm_playback_open, + .release = snd_pcm_release, + .poll = snd_pcm_poll, + .unlocked_ioctl = snd_pcm_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, }, { - .owner = THIS_MODULE, - .read = snd_pcm_read, - .read_iter = snd_pcm_readv, - .open = snd_pcm_capture_open, - .release = snd_pcm_release, - .poll = snd_pcm_poll, - .unlocked_ioctl = snd_pcm_ioctl, - .compat_ioctl = snd_pcm_ioctl_compat, - .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, - .get_unmapped_area = snd_pcm_get_unmapped_area, + .owner = THIS_MODULE, + .read = snd_pcm_read, + .read_iter = snd_pcm_readv, + .open = snd_pcm_capture_open, + .release = snd_pcm_release, + .poll = snd_pcm_poll, + .unlocked_ioctl = snd_pcm_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, } }; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 789254a88e53..1d55da2dcb01 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1782,14 +1782,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, */ static const struct file_operations snd_rawmidi_f_ops = { - .owner = THIS_MODULE, - .read = snd_rawmidi_read, - .write = snd_rawmidi_write, - .open = snd_rawmidi_open, - .release = snd_rawmidi_release, - .poll = snd_rawmidi_poll, - .unlocked_ioctl = snd_rawmidi_ioctl, - .compat_ioctl = snd_rawmidi_ioctl_compat, + .owner = THIS_MODULE, + .read = snd_rawmidi_read, + .write = snd_rawmidi_write, + .open = snd_rawmidi_open, + .release = snd_rawmidi_release, + .poll = snd_rawmidi_poll, + .unlocked_ioctl = snd_rawmidi_ioctl, + .compat_ioctl = snd_rawmidi_ioctl_compat, }; static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 021cd70f90db..2835576040ed 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -206,17 +206,16 @@ odev_poll(struct file *file, poll_table * wait) * registration of sequencer minor device */ -static const struct file_operations seq_oss_f_ops = -{ - .owner = THIS_MODULE, - .read = odev_read, - .write = odev_write, - .open = odev_open, - .release = odev_release, - .poll = odev_poll, - .unlocked_ioctl = odev_ioctl, - .compat_ioctl = odev_ioctl_compat, - .llseek = noop_llseek, +static const struct file_operations seq_oss_f_ops = { + .owner = THIS_MODULE, + .read = odev_read, + .write = odev_write, + .open = odev_open, + .release = odev_release, + .poll = odev_poll, + .unlocked_ioctl = odev_ioctl, + .compat_ioctl = odev_ioctl_compat, + .llseek = noop_llseek, }; static int __init diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 8fe872367568..23ec239640c3 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -2663,16 +2663,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, * REGISTRATION PART */ -static const struct file_operations snd_seq_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_seq_read, - .write = snd_seq_write, - .open = snd_seq_open, - .release = snd_seq_release, - .poll = snd_seq_poll, - .unlocked_ioctl = snd_seq_ioctl, - .compat_ioctl = snd_seq_ioctl_compat, +static const struct file_operations snd_seq_f_ops = { + .owner = THIS_MODULE, + .read = snd_seq_read, + .write = snd_seq_write, + .open = snd_seq_open, + .release = snd_seq_release, + .poll = snd_seq_poll, + .unlocked_ioctl = snd_seq_ioctl, + .compat_ioctl = snd_seq_ioctl_compat, }; static struct device *seq_dev; diff --git a/sound/core/sound.c b/sound/core/sound.c index 17c380d8d8be..24f40763a20c 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -167,11 +167,10 @@ static int snd_open(struct inode *inode, struct file *file) return err; } -static const struct file_operations snd_fops = -{ - .owner = THIS_MODULE, - .open = snd_open, - .llseek = noop_llseek, +static const struct file_operations snd_fops = { + .owner = THIS_MODULE, + .open = snd_open, + .llseek = noop_llseek, }; #ifdef CONFIG_SND_DYNAMIC_MINORS diff --git a/sound/core/timer.c b/sound/core/timer.c index 937d7996f7ab..13d73788324e 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -2158,9 +2158,9 @@ static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned lon } static const struct file_operations snd_utimer_fops = { - .llseek = noop_llseek, - .release = snd_utimer_release, - .unlocked_ioctl = snd_utimer_ioctl, + .llseek = noop_llseek, + .release = snd_utimer_release, + .unlocked_ioctl = snd_utimer_ioctl, }; static int snd_utimer_start(struct snd_timer *t) @@ -2512,16 +2512,15 @@ static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) #define snd_timer_user_ioctl_compat NULL #endif -static const struct file_operations snd_timer_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_timer_user_read, - .open = snd_timer_user_open, - .release = snd_timer_user_release, - .poll = snd_timer_user_poll, - .unlocked_ioctl = snd_timer_user_ioctl, - .compat_ioctl = snd_timer_user_ioctl_compat, - .fasync = snd_timer_user_fasync, +static const struct file_operations snd_timer_f_ops = { + .owner = THIS_MODULE, + .read = snd_timer_user_read, + .open = snd_timer_user_open, + .release = snd_timer_user_release, + .poll = snd_timer_user_poll, + .unlocked_ioctl = snd_timer_user_ioctl, + .compat_ioctl = snd_timer_user_ioctl_compat, + .fasync = snd_timer_user_fasync, }; /* unregister the system timer */ From cc15c329663e3ef1aeed0b68e49a5d5ce4ae0d5c Mon Sep 17 00:00:00 2001 From: Evgenii Burenchev Date: Wed, 8 Jul 2026 17:11:44 +0300 Subject: [PATCH 188/791] ALSA: hpi: Check transport errors during HPI6000 adapter initialization create_adapter_obj() retrieves adapter information by calling hpi6000_message_response_sequence(). This function reports transport-level errors through its return value and DSP-reported errors via hr0.error. The current code only checks hr0.error, causing transport-level errors to be ignored. As a result, adapter initialization may continue with an invalid response. Check the return value of hpi6000_message_response_sequence() before examining hr0.error. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 719f82d3987a ("ALSA: Add support of AudioScience ASI boards") Signed-off-by: Evgenii Burenchev Link: https://patch.msgid.link/20260708141147.18253-1-evg28bur@yandex.ru Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpi6000.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index c8d1518ee3e7..fd7fe9dba0b8 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -537,6 +537,11 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, hr1.size = sizeof(hr1); error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0); + if (error) { + HPI_DEBUG_LOG(ERROR, "message transport error %d\n", + error); + return error; + } if (hr0.error) { HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error); return hr0.error; From cccd721e5aab03e92234faee72b363c9ba60611c Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sat, 11 Jul 2026 11:11:19 +1000 Subject: [PATCH 189/791] ASoC: apple: mca: increase SERDES reset delay The SERDES clusters in this peripheral take a long time to warm up. We tried polling the reset bit until cleared, however this is not a reliable signal of readiness to be configured. Only waiting ~25 us to give the cluster a chance to settle makes it work reliably. Increase the 2 us delay to 25 us and hope we never have to do this again. Fixes: d8b3e396088d ("ASoC: apple: mca: Fix SERDES reset sequence") Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-1-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 492165c0e1ea..ebe116f32661 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -210,10 +210,10 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, SERDES_STATUS_EN | SERDES_STATUS_RST, SERDES_STATUS_RST); /* - * Experiments suggest that it takes at most ~1 us - * for the bit to clear, so wait 2 us for good measure. + * The SERDES cluster needs a bit of time to reset itself + * and settle before we start poking it. This is... slow... */ - udelay(2); + udelay(25); WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & SERDES_STATUS_RST); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, From 108d4b654d82c04c4b74db2cf9b1a947b19a5e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 11 Jul 2026 11:11:20 +1000 Subject: [PATCH 190/791] ASoC: apple: mca: Separate data & clock port setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now FEs were always the clock providers -- feeding the clocks on any ports (BEs) they are attached to. This will soon change and FEs will be allowed to be clock consumers. Once that happens, the routing of clocks and data will to some degree decouple. In advance of the change, make preparations: * Narrow down semantics of what was formerly the 'port_driver' field to refer to clocks only. * On 'startup' of BEs, separate the clock and data aspects of the port setup. Signed-off-by: Martin Povišer Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-2-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 67 +++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index ebe116f32661..55cdb4e0f0b4 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -133,8 +133,8 @@ struct mca_cluster { struct clk *clk_parent; struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; - bool port_started[SNDRV_PCM_STREAM_LAST + 1]; - int port_driver; /* The cluster driving this cluster's port */ + bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; + int port_clk_driver; /* The cluster driving this cluster's port */ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; struct device_link *pd_link; @@ -157,7 +157,7 @@ struct mca_data { struct reset_control *rstc; struct device_link *pd_link; - /* Mutex for accessing port_driver of foreign clusters */ + /* Mutex for accessing port_clk_driver of foreign clusters */ struct mutex port_mutex; int nclusters; @@ -311,7 +311,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) for (i = 0; i < mca->nclusters; i++) { be_cl = &mca->clusters[i]; - if (be_cl->port_driver != cl->no) + if (be_cl->port_clk_driver != cl->no) continue; for_each_pcm_streams(stream) { @@ -331,10 +331,10 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, struct mca_cluster *fe_cl; int ret; - if (cl->port_driver < 0) + if (cl->port_clk_driver < 0) return -EINVAL; - fe_cl = &mca->clusters[cl->port_driver]; + fe_cl = &mca->clusters[cl->port_clk_driver]; /* * Typically the CODECs we are paired with will require clocks @@ -360,7 +360,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, struct mca_data *mca = cl->host; struct mca_cluster *fe_cl; - if (cl->port_driver < 0) + if (cl->port_clk_driver < 0) return -EINVAL; /* @@ -368,7 +368,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, * belong to the same PCM, accesses should have been * synchronized at ASoC level. */ - fe_cl = &mca->clusters[cl->port_driver]; + fe_cl = &mca->clusters[cl->port_clk_driver]; if (!mca_fe_clocks_in_use(fe_cl)) return 0; /* Nothing to do */ @@ -708,12 +708,15 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .trigger = mca_fe_trigger, }; -static bool mca_be_started(struct mca_cluster *cl) +/* + * Is there a FE attached which will be feeding this port's clocks? + */ +static bool mca_be_clk_started(struct mca_cluster *cl) { int stream; for_each_pcm_streams(stream) - if (cl->port_started[stream]) + if (cl->port_clk_started[stream]) return true; return false; } @@ -744,28 +747,38 @@ static int mca_be_startup(struct snd_pcm_substream *substream, fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - if (mca_be_started(cl)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), + cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, + PORT_ENABLES_TX_DATA); + } + + if (mca_be_clk_started(cl)) { /* * Port is already started in the other direction. * Make sure there isn't a conflict with another cluster - * driving the port. + * driving the port clocks. */ - if (cl->port_driver != fe_cl->no) + if (cl->port_clk_driver != fe_cl->no) return -EINVAL; - cl->port_started[substream->stream] = true; + cl->port_clk_started[substream->stream] = true; return 0; } - writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, - cl->base + REG_PORT_ENABLES); writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), cl->base + REG_PORT_CLOCK_SEL); + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), cl->base + REG_PORT_DATA_SEL); + + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, + PORT_ENABLES_CLOCKS); + scoped_guard(mutex, &mca->port_mutex) - cl->port_driver = fe_cl->no; - cl->port_started[substream->stream] = true; + cl->port_clk_driver = fe_cl->no; + cl->port_clk_started[substream->stream] = true; return 0; } @@ -776,17 +789,21 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, struct mca_cluster *cl = mca_dai_to_cluster(dai); struct mca_data *mca = cl->host; - cl->port_started[substream->stream] = false; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); + writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + } - if (!mca_be_started(cl)) { + cl->port_clk_started[substream->stream] = false; + if (!mca_be_clk_started(cl)) { /* * Were we the last direction to shutdown? - * Turn off the lights. + * Turn off the lights (clocks). */ - writel_relaxed(0, cl->base + REG_PORT_ENABLES); - writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); + writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); scoped_guard(mutex, &mca->port_mutex) - cl->port_driver = -1; + cl->port_clk_driver = -1; } } @@ -1092,7 +1109,7 @@ static int apple_mca_probe(struct platform_device *pdev) cl->host = mca; cl->no = i; cl->base = base + CLUSTER_STRIDE * i; - cl->port_driver = -1; + cl->port_clk_driver = -1; cl->clk_parent = of_clk_get(pdev->dev.of_node, i); if (IS_ERR(cl->clk_parent)) { dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", From 2aab6230f8d81c47cd3cc0617d1ef93b7f66f974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 11 Jul 2026 11:11:21 +1000 Subject: [PATCH 191/791] ASoC: apple: mca: Factor out mca_be_get_fe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a function that we also want to use from within mca_be_shutdown, so factor it out. Signed-off-by: Martin Povišer Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-3-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 55cdb4e0f0b4..1e9a5c9cfc54 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -721,30 +721,35 @@ static bool mca_be_clk_started(struct mca_cluster *cl) return false; } -static int mca_be_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, + int stream) { - struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *fe; - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; + struct snd_soc_pcm_runtime *fe = NULL; struct snd_soc_dpcm *dpcm; - fe = NULL; - - for_each_dpcm_fe(be, substream->stream, dpcm) { + for_each_dpcm_fe(be, stream, dpcm) { if (fe && dpcm->fe != fe) { - dev_err(mca->dev, "many FE per one BE unsupported\n"); - return -EINVAL; + dev_err(be->dev, "many FE per one BE unsupported\n"); + return NULL; } fe = dpcm->fe; } + return fe; +} + +static int mca_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; + if (!fe) return -EINVAL; - fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { From 00309bfb1f54558a06b8aeeaedb2e54956b5182a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 11 Jul 2026 11:11:22 +1000 Subject: [PATCH 192/791] ASoC: apple: mca: Support FEs being clock consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support FEs being I2S clock consumers. This does not mean we support accepting clocks from outside the SoC (although it paves the way for that support in the future), but it means multiple FEs can attach to one BE, one being clock producer and the rest clock consumers. This is useful for grabbing I/V sense data on some machines, since in such a scenario the format of the sense data on the I2S bus differs from that of the audio data (the two formats differing in slot width). With two FEs attached to the bus, we can split the responsibilities and command different slot widths to the two. Signed-off-by: Martin Povišer Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-4-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 124 +++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 21 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 1e9a5c9cfc54..92c9f8df50d6 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -133,12 +133,17 @@ struct mca_cluster { struct clk *clk_parent; struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; + bool clk_provider; + bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; int port_clk_driver; /* The cluster driving this cluster's port */ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; struct device_link *pd_link; + /* In case of clock consumer FE */ + int syncgen_in_use; + unsigned int bclk_ratio; /* Masks etc. picked up via the set_tdm_slot method */ @@ -256,11 +261,32 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int mca_fe_get_port(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *be; + struct snd_soc_dpcm *dpcm; + + be = NULL; + for_each_dpcm_be(fe, substream->stream, dpcm) { + be = dpcm->be; + break; + } + + if (!be) + return -EINVAL; + + return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; +} + static int mca_fe_enable_clocks(struct mca_cluster *cl) { struct mca_data *mca = cl->host; int ret; + if (!cl->clk_provider) + return -EINVAL; + ret = clk_prepare_enable(cl->clk_parent); if (ret) { dev_err(mca->dev, @@ -332,7 +358,7 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, int ret; if (cl->port_clk_driver < 0) - return -EINVAL; + return 0; fe_cl = &mca->clusters[cl->port_clk_driver]; @@ -380,6 +406,56 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, return 0; } +static int mca_fe_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_data *mca = cl->host; + + if (cl->clk_provider) + return 0; + + /* Turn on the cluster power domain if not already in use */ + if (!cl->syncgen_in_use) { + int port = mca_fe_get_port(substream); + + cl->pd_link = device_link_add(mca->dev, cl->pd_dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!cl->pd_link) { + dev_err(mca->dev, + "cluster %d: unable to prop-up power domain\n", cl->no); + return -EINVAL; + } + + mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, + SYNCGEN_STATUS_EN); + } + cl->syncgen_in_use |= 1 << substream->stream; + + return 0; +} + +static int mca_fe_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + + if (cl->clk_provider) + return 0; + + cl->syncgen_in_use &= ~(1 << substream->stream); + if (cl->syncgen_in_use) + return 0; + + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); + if (cl->pd_link) + device_link_del(cl->pd_link); + + return 0; +} + static unsigned int mca_crop_mask(unsigned int mask, int nchans) { while (hweight32(mask) > nchans) @@ -505,9 +581,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) u32 serdes_conf = 0; u32 bitstart; - if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != - SND_SOC_DAIFMT_BP_FP) + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + cl->clk_provider = true; + break; + + case SND_SOC_DAIFMT_BC_FC: + cl->clk_provider = false; + break; + + default: goto err; + } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -564,24 +649,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) return 0; } -static int mca_fe_get_port(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *be; - struct snd_soc_dpcm *dpcm; - - be = NULL; - for_each_dpcm_be(fe, substream->stream, dpcm) { - be = dpcm->be; - break; - } - - if (!be) - return -EINVAL; - - return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; -} - static int mca_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -706,6 +773,8 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .set_tdm_slot = mca_fe_set_tdm_slot, .hw_params = mca_fe_hw_params, .trigger = mca_fe_trigger, + .prepare = mca_fe_prepare, + .hw_free = mca_fe_hw_free, }; /* @@ -759,6 +828,9 @@ static int mca_be_startup(struct snd_pcm_substream *substream, PORT_ENABLES_TX_DATA); } + if (!fe_cl->clk_provider) + return 0; + if (mca_be_clk_started(cl)) { /* * Port is already started in the other direction. @@ -791,14 +863,24 @@ static int mca_be_startup(struct snd_pcm_substream *substream, static void mca_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; struct mca_data *mca = cl->host; + if (!fe) + return; + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); } + if (!fe_cl->clk_provider) + return; + cl->port_clk_started[substream->stream] = false; if (!mca_be_clk_started(cl)) { /* From 108a14b20f958ad5cbb8f8bfaf4932dfd69bb0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 11 Jul 2026 11:11:23 +1000 Subject: [PATCH 193/791] ASoC: apple: mca: Support capture on multiples BEs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple BEs are linked to a FE, the former behavior was to source the data line from the DIN pin of the first BE only. Change this to ORing the DIN inputs of all linked BEs. As long as the unused slots on each BE's line are zeroed out and the slots on the BEs don't overlap, this will work out well. Signed-off-by: Martin Povišer Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-5-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 92c9f8df50d6..d8973e5611dd 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -261,22 +261,19 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int mca_fe_get_port(struct snd_pcm_substream *substream) +static int mca_fe_get_portmask(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; + int mask = 0; - be = NULL; for_each_dpcm_be(fe, substream->stream, dpcm) { - be = dpcm->be; - break; + int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; + + mask |= 1 << no; } - if (!be) - return -EINVAL; - - return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; + return mask; } static int mca_fe_enable_clocks(struct mca_cluster *cl) @@ -417,7 +414,7 @@ static int mca_fe_prepare(struct snd_pcm_substream *substream, /* Turn on the cluster power domain if not already in use */ if (!cl->syncgen_in_use) { - int port = mca_fe_get_port(substream); + int port = ffs(mca_fe_get_portmask(substream)); cl->pd_link = device_link_add(mca->dev, cl->pd_dev, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | @@ -466,7 +463,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, unsigned int mask, int slots, int nchans, - int slot_width, bool is_tx, int port) + int slot_width, bool is_tx, int portmask) { __iomem void *serdes_base = cl->base + serdes_unit; u32 serdes_conf, serdes_conf_mask; @@ -525,7 +522,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, serdes_base + REG_RX_SERDES_SLOTMASK); writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); - writel_relaxed(1 << port, + writel_relaxed(portmask, serdes_base + REG_RX_SERDES_PORT); } @@ -662,7 +659,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, unsigned long bclk_ratio; unsigned int tdm_slots, tdm_slot_width, tdm_mask; u32 regval, pad; - int ret, port, nchans_ceiled; + int ret, portmask, nchans_ceiled; if (!cl->tdm_slot_width) { /* @@ -711,13 +708,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, tdm_mask = (1 << tdm_slots) - 1; } - port = mca_fe_get_port(substream); - if (port < 0) - return port; + portmask = mca_fe_get_portmask(substream); + if (!portmask) + return -EINVAL; ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, tdm_mask, tdm_slots, params_channels(params), - tdm_slot_width, is_tx, port); + tdm_slot_width, is_tx, portmask); if (ret) return ret; From 889c26cfb98dd40f8e7bab721bc11a82dd592c33 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 11 Jul 2026 11:11:24 +1000 Subject: [PATCH 194/791] ASoC: apple: mca: Do not mark clocks in use for non-providers On the speakers PCM, this sequence: 1. Open playback 2. Open sense 3. Close playback 4. Close sense would result in the sense FE being marked as clocks in use at (2), since there is a clock provider (playback FE). Then at (4) this would WARN since there is no driver any more when closing the in use clocks. If (1) and (2) are reversed this does not happen, since the sense PCM is not marked as using the clocks when there is no provider yet. So, check explicitly whether the substream FE is a clock provider in be_prepare, and skip everything if it isn't. Signed-off-by: Hector Martin Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-6-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 67 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index d8973e5611dd..322dd8771111 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -346,36 +346,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) return false; } -static int mca_be_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - int ret; - - if (cl->port_clk_driver < 0) - return 0; - - fe_cl = &mca->clusters[cl->port_clk_driver]; - - /* - * Typically the CODECs we are paired with will require clocks - * to be present at time of unmute with the 'mute_stream' op - * or at time of DAPM widget power-up. We need to enable clocks - * here at the latest (frontend prepare would be too late). - */ - if (!mca_fe_clocks_in_use(fe_cl)) { - ret = mca_fe_enable_clocks(fe_cl); - if (ret < 0) - return ret; - } - - cl->clocks_in_use[substream->stream] = true; - - return 0; -} - static int mca_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -805,6 +775,43 @@ static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, return fe; } +static int mca_be_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_data *mca = cl->host; + struct mca_cluster *fe_cl, *fe_clk_cl; + int ret; + + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + + if (!fe_cl->clk_provider) + return 0; + + if (cl->port_clk_driver < 0) + return 0; + + fe_clk_cl = &mca->clusters[cl->port_clk_driver]; + + /* + * Typically the CODECs we are paired with will require clocks + * to be present at time of unmute with the 'mute_stream' op + * or at time of DAPM widget power-up. We need to enable clocks + * here at the latest (frontend prepare would be too late). + */ + if (!mca_fe_clocks_in_use(fe_clk_cl)) { + ret = mca_fe_enable_clocks(fe_clk_cl); + if (ret < 0) + return ret; + } + + cl->clocks_in_use[substream->stream] = true; + + return 0; +} + static int mca_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { From 8dd595bca51a4c1f775cfcca8c10753c58b7a969 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 11 Jul 2026 11:11:25 +1000 Subject: [PATCH 195/791] ASoC: apple: mca: Add delay after configuring clock Right after the early FE setup, ADMAC gets told to start the DMA. This can end up in a weird "slip" state with the channels transposed. Waiting a bit fixes this; presumably this allows the clock to stabilize. Signed-off-by: Hector Martin Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260711-apple-audio-redux-v4-7-2994d87c2f24@gmail.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 322dd8771111..afb3754a8883 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -225,6 +225,12 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); + /* + * ADMAC gets started right after this. This delay seems + * to be needed for that to be reliable, e.g. ensure the + * clock is stable? + */ + udelay(100); break; default: break; From a9a2c8c44b30f15596d5b1a0982d13b0c79911f4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Jul 2026 17:04:04 +0100 Subject: [PATCH 196/791] ASoC: simple-card: Fix clang build Jumping over the allocation of the link_info for a missing dev breaks the build: /tmp/next/build/sound/soc/generic/simple-card.c:676:3: error: cannot jump from this goto statement to its label 676 | goto end; | ^ /tmp/next/build/sound/soc/generic/simple-card.c:679:20: note: jump bypasses initialization of variable with __attribute__((cleanup)) 679 | struct link_info *li __free(kfree) = kzalloc_obj(*li); | ^ Fixes: 7f20b9b05b3a ("ASoC: simple-card: merge extra method into simple_parse_of()") Link: https://patch.msgid.link/20260713-asoc-fix-simple-card-build-v1-1-671ad44ad1a0@kernel.org Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index abfbc9fd7c6d..6e98e973e525 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -673,7 +673,7 @@ static int simple_parse_of(struct simple_util_priv *priv) int ret = -EINVAL; if (!dev) - goto end; + return simple_ret(priv, ret); ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc_obj(*li); From 552d9559cae2b6344228da1b8f7205d6e537ba38 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 13 Jul 2026 16:46:49 +0800 Subject: [PATCH 197/791] ASoC: SOF: Intel: reset spib_addr before disabling SPIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spib_addr register will indicate to the host DMA where the position is in the buffer currently processed by host SW. The register is ignored by the host DMA if SPIB is disabled. Reset it to 0 before disabling SPIB. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260713084650.4138172-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 5c1f3b427cdb..4a46e35c3eac 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, mask = (1 << hstream->index); + /* Reset the spib_addr before disabling SPIB */ + if (!enable) + sof_io_write(sdev, hstream->spib_addr, 0); + /* enable/disable SPIB for the stream */ snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, enable << hstream->index); /* set the SPIB value */ - sof_io_write(sdev, hstream->spib_addr, size); + if (enable) + sof_io_write(sdev, hstream->spib_addr, size); return 0; } From 0507d956c4ce91f2bca63ef99d2f0a30817ea7db Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 13 Jul 2026 16:46:50 +0800 Subject: [PATCH 198/791] ASoC: SOF: Intel: Disable SPIB in non ICCMAX stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code disable SPIB in the playback direction only because previously the hda data stream is only used for SOF firmware download and we prepare capture stream for ICCMAX and prepare playback stream for non ICCMAX case. But now the hda data stream is also used for SoundWire BPT which will use both directions. The SPIB is enabled in non ICCMAX cases and we should disable in clean up. Add a is_iccmax flag in the hda_data_stream_cleanup() function to align with the hda_data_stream_prepare() function to enable/disable the SPIB. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260713084650.4138172-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 11 ++++++----- sound/soc/sof/intel/hda-sdw-bpt.c | 2 +- sound/soc/sof/intel/hda-stream.c | 8 +++++--- sound/soc/sof/intel/hda.h | 5 +++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 2b3abcf75d55..4347e71337f9 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream) + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) { - return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); + return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, + is_iccmax, false); } EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) * If the cleanup also fails, we return the initial error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, - persistent_cl_buffer, iccmax_stream); + persistent_cl_buffer, iccmax_stream, true); if (ret1 < 0) { dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); @@ -458,7 +459,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) * If the cleanup also fails, we return the initial error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, - persistent_cl_buffer, hext_stream); + persistent_cl_buffer, hext_stream, false); if (ret1 < 0) { dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); @@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, cleanup: /* clean up even in case of error and return the first error */ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, - hext_stream); + hext_stream, false); if (ret1 < 0) { dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index 728ffe7ae54d..c10648f56f23 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream u32 mask; int ret; - ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); + ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); if (ret < 0) { dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", __func__); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 4a46e35c3eac..0778002e2bd6 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1328,16 +1328,18 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) + bool persistent_buffer, struct hdac_ext_stream *hext_stream, + bool is_iccmax, bool pair) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct hdac_stream *hstream = hdac_stream(hext_stream); int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret = 0; - if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + if (!is_iccmax) ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - else + + if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_SD_CTL_DMA_START, 0); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 3f0966477ace..799e49539b4a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -739,7 +739,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream); + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); #define HDA_CL_STREAM_FORMAT 0x40 @@ -911,7 +911,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si bool is_iccmax, bool pair); int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, - bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); + bool persistent_buffer, struct hdac_ext_stream *hext_stream, + bool is_iccmax, bool pair); /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; From 55d21e0abcd3eea9f1d786f17a82e545888d1138 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 11:03:00 -0700 Subject: [PATCH 199/791] ALSA: ac97: use struct keyword for kernel-doc comments Inform kernel-doc that the comment block is for structs to void warnings: Warning: include/sound/ac97/codec.h:46 cannot understand function prototype: 'struct ac97_codec_device' Warning: include/sound/ac97/codec.h:62 cannot understand function prototype: 'struct ac97_codec_driver' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260713180303.526409-2-rdunlap@infradead.org --- include/sound/ac97/codec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h index 69b404c354f5..69aa7ee15b91 100644 --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -33,7 +33,7 @@ struct ac97_id { }; /** - * ac97_codec_device - a ac97 codec + * struct ac97_codec_device - a ac97 codec * @dev: the core device * @vendor_id: the vendor_id of the codec, as sensed on the AC-link * @num: the codec number, 0 is primary, 1 is first slave, etc ... @@ -53,7 +53,7 @@ struct ac97_codec_device { }; /** - * ac97_codec_driver - a ac97 codec driver + * struct ac97_codec_driver - a ac97 codec driver * @driver: the device driver structure * @probe: the function called when a ac97_codec_device is matched * @remove: the function called when the device is unbound/removed From 30d3886949ce8a74114912322cbb185db3c4fda5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 11:03:01 -0700 Subject: [PATCH 200/791] ALSA: hda: regmap: fix all kernel-doc warnings - add missing function parameter descriptions - drop some incorrect function parameter descriptions - add missing function Return value sections - use the correct function prototype names in the comments These changes avoid many warnings (examples): Warning: include/sound/hda_regmap.h:80 function parameter 'codec' not described in 'snd_hdac_regmap_write' Warning: include/sound/hda_regmap.h:80 function parameter 'verb' not described in 'snd_hdac_regmap_write' Warning: include/sound/hda_regmap.h:80 Excess function parameter 'reg' description in 'snd_hdac_regmap_write' Warning: include/sound/hda_regmap.h:80 No description found for return value of 'snd_hdac_regmap_write' Warning: include/sound/hda_regmap.h:99 function parameter 'codec' not described in 'snd_hdac_regmap_update' Warning: include/sound/hda_regmap.h:99 expecting prototype for snd_hda_regmap_update(). Prototype was for snd_hdac_regmap_update() instead Warning: include/sound/hda_regmap.h:116 expecting prototype for snd_hda_regmap_read(). Prototype was for snd_hdac_regmap_read() instead Warning: include/sound/hda_regmap.h:116 function parameter 'codec' not described in 'snd_hdac_regmap_read' Warning: include/sound/hda_regmap.h:137 function parameter 'dir' not described in 'snd_hdac_regmap_get_amp' Warning: include/sound/hda_regmap.h:137 Excess function parameter 'direction' description in 'snd_hdac_regmap_get_amp' Warning: include/sound/hda_regmap.h:137 No description found for return value of 'snd_hdac_regmap_get_amp' Warning: include/sound/hda_regmap.h:161 function parameter 'dir' not described in 'snd_hdac_regmap_update_amp' Warning: include/sound/hda_regmap.h:161 Excess function parameter 'direction' description in 'snd_hdac_regmap_update_amp' Warning: include/sound/hda_regmap.h:161 No description found for return value of 'snd_hdac_regmap_update_amp' Warning: include/sound/hda_regmap.h:182 function parameter 'dir' not described in 'snd_hdac_regmap_get_amp_stereo' Warning: include/sound/hda_regmap.h:182 Excess function parameter 'ch' description in 'snd_hdac_regmap_get_amp_stereo' Warning: include/sound/hda_regmap.h:182 No description found for return value of 'snd_hdac_regmap_get_amp_stereo' Warning: include/sound/hda_regmap.h:206 function parameter 'dir' not described in 'snd_hdac_regmap_update_amp_stereo' Warning: include/sound/hda_regmap.h:206 Excess function parameter 'direction' description in 'snd_hdac_regmap_update_amp_stereo' Warning: include/sound/hda_regmap.h:206 No description found for return value of 'snd_hdac_regmap_update_amp_stereo' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260713180303.526409-3-rdunlap@infradead.org --- include/sound/hda_regmap.h | 42 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h index 4c1b9bebbd60..e06e4847ed28 100644 --- a/include/sound/hda_regmap.h +++ b/include/sound/hda_regmap.h @@ -69,11 +69,14 @@ void snd_hdac_regmap_sync(struct hdac_device *codec); /** * snd_hdac_regmap_write - Write a verb with caching + * @codec: HD-audio codec base device * @nid: codec NID - * @reg: verb to write + * @verb: verb to write * @val: value to write * * For writing an amp value, use snd_hdac_regmap_update_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, @@ -85,13 +88,16 @@ snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, } /** - * snd_hda_regmap_update - Update a verb value with caching + * snd_hdac_regmap_update - Update a verb value with caching + * @codec: HD-audio codec * @nid: codec NID * @verb: verb to update * @mask: bit mask to update * @val: value to update * * For updating an amp value, use snd_hdac_regmap_update_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, @@ -104,12 +110,15 @@ snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, } /** - * snd_hda_regmap_read - Read a verb with caching + * snd_hdac_regmap_read - Read a verb with caching + * @codec: HD-audio codec * @nid: codec NID * @verb: verb to read * @val: pointer to store the value * * For reading an amp value, use snd_hda_regmap_get_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, @@ -125,12 +134,12 @@ snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * @val: the pointer to store the value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) * * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. - * Returns the value or a negative error. + * + * Returns: the value or a negative error. */ static inline int snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, @@ -148,13 +157,14 @@ snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT + * @dir: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + * + * Returns: 0 if the value is unchanged, 1 if changed, or a negative error. */ static inline int snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, @@ -169,13 +179,12 @@ snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values * @codec: HD-audio codec * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * @val: the pointer to store the value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) * * Read stereo AMP values. The lower byte is left, the upper byte is right. - * Returns the value or a negative error. + * + * Returns: the value or a negative error. */ static inline int snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, @@ -192,14 +201,15 @@ snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value * @codec: HD-audio codec * @nid: NID to read the AMP value - * @direction: #HDA_INPUT or #HDA_OUTPUT + * @dir: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the stereo AMP value with a bit mask. * The lower byte is left, the upper byte is right. - * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + * + * Returns: 0 if the value is unchanged, 1 if changed, or a negative error. */ static inline int snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid, From 6dad8dc4b1d0382a0c193676d5c9d576f42b3114 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 11:03:02 -0700 Subject: [PATCH 201/791] ALSA: firewire: fix all kernel-doc warnings Add missing comment for struct member @messages. Use the struct keyword for a struct's kernel-doc heading. Add missing comments for nested aggregate structs. Repair some typos. Warning: include/uapi/sound/firewire.h:97 struct member 'messages' not described in 'snd_firewire_event_ff400_message' Warning: ../include/uapi/sound/firewire.h:220 cannot understand function prototype: 'struct snd_firewire_motu_register_dsp_parameter' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260713180303.526409-4-rdunlap@infradead.org --- include/uapi/sound/firewire.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 1e86872c151f..1235d89d2bff 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -79,11 +79,12 @@ struct snd_firewire_event_motu_register_dsp_change { * * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE. * @message_count: The number of messages. + * @messages: Array of @message_count messages * @messages.message: The messages expressing hardware knob operation. * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous - * transaction was sent to deliver the message. It has 16 bit unsigned integer + * transaction was sent to deliver the message. It has 16-bit unsigned integer * value. The higher 3 bits of value expresses the lower three bits of second - * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle + * field in the format of CYCLE_TIME, up to 7. The remaining 13 bits express cycle * field up to 7999. * * The structure expresses message transmitted by Fireface 400 when operating hardware knob. @@ -191,8 +192,9 @@ struct snd_firewire_motu_register_dsp_meter { #define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_ALIGNED_INPUT_COUNT (SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT + 2) /** - * snd_firewire_motu_register_dsp_parameter - the container for parameters of DSP controlled - * by register access. + * struct snd_firewire_motu_register_dsp_parameter - the container for parameters + * of DSP controlled by register access. + * @mixer: aggregate of @mixer.source and @mixer.output * @mixer.source.gain: The gain of source to mixer. * @mixer.source.pan: The L/R balance of source to mixer. * @mixer.source.flag: The flag of source to mixer, including mute, solo. @@ -200,21 +202,25 @@ struct snd_firewire_motu_register_dsp_meter { * Audio Express. * @mixer.source.paired_width: The width of paired source to mixer, only for 4 pre and * Audio Express. + * @mixer.output: FIXME * @mixer.output.paired_volume: The volume of paired output from mixer. * @mixer.output.paired_flag: The flag of paired output from mixer. + * @output: output parameters * @output.main_paired_volume: The volume of paired main output. * @output.hp_paired_volume: The volume of paired hp output. * @output.hp_paired_assignment: The source assigned to paired hp output. - * @output.reserved: Padding for 32 bit alignment for future extension. + * @output.reserved: Padding for 32-bit alignment for future extension. + * @line_input: line input parameters * @line_input.boost_flag: The flags of boost for line inputs, only for 828mk2 and Traveler. * @line_input.nominal_level_flag: The flags of nominal level for line inputs, only for 828mk2 and * Traveler. - * @line_input.reserved: Padding for 32 bit alignment for future extension. + * @line_input.reserved: Padding for 32-bit alignment for future extension. + * @input: input parameters * @input.gain_and_invert: The value including gain and invert for input, only for Ultralite, 4 pre * and Audio Express. * @input.flag: The flag of input; e.g. jack detection, phantom power, and pad, only for Ultralite, * 4 pre and Audio express. - * @reserved: Padding so that the size of structure is kept to 512 byte, but for future extension. + * @reserved: Padding so that the size of structure is kept to 512 bytes, but for future extension. * * The structure expresses the set of parameters for DSP controlled by register access. */ @@ -272,8 +278,8 @@ struct snd_firewire_motu_register_dsp_parameter { * controlled by command * @data: Signal level meters. The mapping between position and signal channel is model-dependent. * - * The structure expresses the part of DSP status for hardware meter. The 32 bit storage is - * estimated to include IEEE 764 32 bit single precision floating point (binary32) value. It is + * The structure expresses the part of DSP status for hardware meter. The 32-bit storage is + * estimated to include IEEE 764 32-bit single precision floating point (binary32) value. It is * expected to be linear value (not logarithm) for audio signal level between 0.0 and +1.0. */ struct snd_firewire_motu_command_dsp_meter { From 6873ab65f45106b0dffb6ae38ece7c2be0830ab6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 11:03:03 -0700 Subject: [PATCH 202/791] ALSA: usb-audio: um144mkii: use "var" keyword for data Use the "var" keyword when describing data definitions to avoid kernel-doc warnings: Warning: sound/usb/usx2y/us144mkii_pcm.h:14 cannot understand function prototype: 'const struct snd_pcm_hardware tascam_pcm_hw;' Warning: sound/usb/usx2y/us144mkii_pcm.h:21 cannot understand function prototype: 'const struct snd_pcm_ops tascam_playback_ops;' Warning: sound/usb/usx2y/us144mkii_pcm.h:28 cannot understand function prototype: 'const struct snd_pcm_ops tascam_capture_ops;' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260713180303.526409-5-rdunlap@infradead.org --- sound/usb/usx2y/us144mkii_pcm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/usx2y/us144mkii_pcm.h b/sound/usb/usx2y/us144mkii_pcm.h index 74da8564431b..a130678405e3 100644 --- a/sound/usb/usx2y/us144mkii_pcm.h +++ b/sound/usb/usx2y/us144mkii_pcm.h @@ -7,7 +7,7 @@ #include "us144mkii.h" /** - * tascam_pcm_hw - Hardware capabilities for TASCAM US-144MKII PCM. + * var tascam_pcm_hw - Hardware capabilities for TASCAM US-144MKII PCM. * * Defines the supported PCM formats, rates, channels, and buffer/period sizes * for the TASCAM US-144MKII audio interface. @@ -15,14 +15,14 @@ extern const struct snd_pcm_hardware tascam_pcm_hw; /** - * tascam_playback_ops - ALSA PCM operations for playback. + * var tascam_playback_ops - ALSA PCM operations for playback. * * This structure defines the callback functions for playback stream operations. */ extern const struct snd_pcm_ops tascam_playback_ops; /** - * tascam_capture_ops - ALSA PCM operations for capture. + * var tascam_capture_ops - ALSA PCM operations for capture. * * This structure defines the callback functions for capture stream operations. */ From 06b6f1245567a4be862c3e1cc74577922ceb05fb Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 4 Jul 2026 21:28:57 +0200 Subject: [PATCH 203/791] ASoC: codecs: aw88261: only check PLL and clock state at power-up The SYSST check performed during device start requires SWS (amplifier switching, bit 8) and BSTS (boost finished, bit 9) on top of PLL lock and clock stability. Those bits cannot be asserted at this point in the sequence: the check runs after amppd release but before the hmute/ULS-hmute release, and the amplifier neither switches nor finishes ramping its boost converter while it is still muted. With the Fairphone (Gen. 6) firmware profile, aw88261_dev_start() therefore always fails with check sysst fail, reg_val=0x0011, check:0x311 and playback aborts, even though the amplifier is fine and PLL lock and stable clocks are present. Check only PLL lock and clock stability, for which a definition already exists; this still re-validates the clocks after amppd release (aw88261_dev_check_syspll() checked them before it). This matches the vendor aw882xx driver, which only validates PLL lock and clock stability at this stage, and the in-tree aw88399 driver, which skips the SWS check whenever the amplifier may legitimately not be switching (AW88399_BIT_SYSST_NOSWS_CHECK). Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5 Signed-off-by: Jorijn van der Graaf Link: https://patch.msgid.link/20260704192857.88366-1-jorijnvdgraaf@catcrafts.net Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 6 +++--- sound/soc/codecs/aw88261.h | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 549783d3e75e..dbdf51188cf5 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -206,10 +206,10 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev) return ret; check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) - & AW88261_BIT_SYSST_CHECK; - if (check_val != AW88261_BIT_SYSST_CHECK) { + & AW88261_BIT_PLL_CHECK; + if (check_val != AW88261_BIT_PLL_CHECK) { dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", - reg_val, AW88261_BIT_SYSST_CHECK); + reg_val, AW88261_BIT_PLL_CHECK); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); } else { return 0; diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 270ccf375f36..f16f9f8c40a2 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -194,12 +194,6 @@ AW88261_OTHS_OT_VALUE | \ AW88261_PLLS_LOCKED_VALUE)) -#define AW88261_BIT_SYSST_CHECK \ - (AW88261_BSTS_FINISHED_VALUE | \ - AW88261_SWS_SWITCHING_VALUE | \ - AW88261_CLKS_STABLE_VALUE | \ - AW88261_PLLS_LOCKED_VALUE) - #define AW88261_ULS_HMUTE_START_BIT (14) #define AW88261_ULS_HMUTE_BITS_LEN (1) #define AW88261_ULS_HMUTE_MASK \ From 350b7eae8e8b2bd3885a3e2424381494d6bde038 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:38 +0700 Subject: [PATCH 204/791] ASoC: codecs: ab8500: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 6e8ef9cd1b31..2cf96cbdd294 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -14,6 +14,7 @@ * for ST-Ericsson. */ +#include #include #include #include @@ -989,9 +990,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - mutex_lock(&drvdata->ctrl_lock); + guard(mutex)(&drvdata->ctrl_lock); ucontrol->value.enumerated.item[0] = drvdata->sid_status; - mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1014,7 +1014,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, return -EIO; } - mutex_lock(&drvdata->ctrl_lock); + guard(mutex)(&drvdata->ctrl_lock); sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { @@ -1025,7 +1025,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, } else { status = -EBUSY; } - goto out; + dev_dbg(component->dev, "%s: Exit\n", __func__); + return status; } snd_soc_component_write(component, AB8500_SIDFIRADR, 0); @@ -1043,9 +1044,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, drvdata->sid_status = SID_FIR_CONFIGURED; -out: - mutex_unlock(&drvdata->ctrl_lock); - dev_dbg(component->dev, "%s: Exit\n", __func__); return status; From da41b1716fad3ce1b4b617fd2155fb0347d7a1ba Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:39 +0700 Subject: [PATCH 205/791] ASoC: codecs: ak4613: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 77 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 3e0696b5abf5..be5306d07d40 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -95,6 +95,7 @@ * see * AK4613_ENABLE_TDM_TEST */ +#include #include #include #include @@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); struct device *dev = component->dev; - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); priv->cnt--; if (priv->cnt < 0) { dev_err(dev, "unexpected counter error\n"); @@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, } if (!priv->cnt) priv->ctrl1 = 0; - mutex_unlock(&priv->lock); } static void ak4613_hw_constraints(struct ak4613_priv *priv, @@ -507,10 +507,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); ak4613_hw_constraints(priv, substream); priv->cnt++; - mutex_unlock(&priv->lock); return 0; } @@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, */ ret = -EINVAL; - mutex_lock(&priv->lock); - if (priv->cnt > 1) { - /* - * If it was already working, use current priv->ctrl1 - */ - ret = 0; - } else { - /* - * It is not yet working, - */ - unsigned int channel = params_channels(params); - u8 tdm; + scoped_guard(mutex, &priv->lock) { + if (priv->cnt > 1) { + /* + * If it was already working, use current priv->ctrl1 + */ + ret = 0; + } else { + /* + * It is not yet working, + */ + unsigned int channel = params_channels(params); + u8 tdm; - /* STEREO or TDM */ - if (channel == 2) - tdm = AK4613_CONFIG_MODE_STEREO; - else - tdm = AK4613_CONFIG_GET(priv, MODE); + /* STEREO or TDM */ + if (channel == 2) + tdm = AK4613_CONFIG_MODE_STEREO; + else + tdm = AK4613_CONFIG_GET(priv, MODE); - for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { - const struct ak4613_interface *iface = ak4613_iface + i; + for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { + const struct ak4613_interface *iface = ak4613_iface + i; - if ((iface->fmt == fmt) && (iface->width == width)) { - /* - * Ctrl1 - * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | - * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| - * < tdm > < iface->dif > - */ - priv->ctrl1 = (tdm << 6) | (iface->dif << 3); - ret = 0; - break; + if (iface->fmt == fmt && iface->width == width) { + /* + * Ctrl1 + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| + * < tdm > < iface->dif > + */ + priv->ctrl1 = (tdm << 6) | (iface->dif << 3); + ret = 0; + break; + } } } } - mutex_unlock(&priv->lock); - if (ret < 0) - goto hw_params_end; + if (ret < 0) { + dev_warn(dev, "unsupported data width/format combination\n"); + return ret; + } snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); @@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); -hw_params_end: - if (ret < 0) - dev_warn(dev, "unsupported data width/format combination\n"); - return ret; } From d46421aeac5a923ea76ad8922e285280fddc16da Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:40 +0700 Subject: [PATCH 206/791] ASoC: codecs: arizona-jack: Use guard() cleanup helpers Clean up the code using guard() helpers for mutex locking and PM runtime management. No functional change intended. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/arizona-jack.c | 121 +++++++++++++++----------------- 1 file changed, 55 insertions(+), 66 deletions(-) diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index a9063bac2752..819d080b1188 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -5,6 +5,7 @@ * Copyright (C) 2012-2014 Wolfson Microelectronics plc */ +#include #include #include #include @@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) struct arizona_priv, micd_timeout_work.work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); info->detecting = false; arizona_identify_headphone(info); - - mutex_unlock(&info->lock); } static int arizona_micd_adc_read(struct arizona_priv *info) @@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) cancel_delayed_work_sync(&info->micd_timeout_work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); /* If the cable was removed while measuring ignore the result */ if (!(info->jack->status & SND_JACK_MECHANICAL)) { dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); - mutex_unlock(&info->lock); return; } @@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) arizona_button_reading(info); pm_runtime_mark_last_busy(arizona->dev); - mutex_unlock(&info->lock); } static irqreturn_t arizona_micdet(int irq, void *data) @@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) cancel_delayed_work_sync(&info->micd_detect_work); cancel_delayed_work_sync(&info->micd_timeout_work); - mutex_lock(&info->lock); - if (!info->detecting) - debounce = 0; - mutex_unlock(&info->lock); + scoped_guard(mutex, &info->lock) { + if (!info->detecting) + debounce = 0; + } if (debounce) queue_delayed_work(system_power_efficient_wq, @@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) struct arizona_priv, hpdet_work.work); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); arizona_start_hpdet_acc_id(info); - mutex_unlock(&info->lock); } static int arizona_hpdet_wait(struct arizona_priv *info) @@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); - pm_runtime_get_sync(arizona->dev); + guard(pm_runtime_active_auto)(arizona->dev); - mutex_lock(&info->lock); + guard(mutex)(&info->lock); if (info->micd_clamp) { mask = ARIZONA_MICD_CLAMP_STS; @@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); if (ret) { dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); - mutex_unlock(&info->lock); - pm_runtime_put_autosuspend(arizona->dev); return IRQ_NONE; } @@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) &info->micd_timeout_work, msecs_to_jiffies(micd_timeout)); } + } else { + info->last_jackdet = val; - goto out; - } - info->last_jackdet = val; + if (info->last_jackdet == present) { + dev_dbg(arizona->dev, "Detected jack\n"); + snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); - if (info->last_jackdet == present) { - dev_dbg(arizona->dev, "Detected jack\n"); - snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + info->detecting = true; + info->mic = false; + info->jack_flips = 0; - info->detecting = true; - info->mic = false; - info->jack_flips = 0; + if (!arizona->pdata.hpdet_acc_id) { + arizona_start_mic(info); + } else { + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); + } - if (!arizona->pdata.hpdet_acc_id) { - arizona_start_mic(info); + if (info->micd_clamp || !arizona->pdata.jd_invert) + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB | + ARIZONA_JD1_DB, 0); } else { - queue_delayed_work(system_power_efficient_wq, - &info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); - } + dev_dbg(arizona->dev, "Detected jack removal\n"); + + arizona_stop_mic(info); + + info->num_hpdet_res = 0; + for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) + info->hpdet_res[i] = 0; + info->mic = false; + info->hpdet_done = false; + info->hpdet_retried = false; + + snd_soc_jack_report(info->jack, 0, + ARIZONA_JACK_MASK | info->micd_button_mask); + + /* + * If the jack was removed during a headphone detection we + * need to wait for the headphone detection to finish, as + * it can not be aborted. We don't want to be able to start + * a new headphone detection from a fresh insert until this + * one is finished. + */ + arizona_hpdet_wait(info); - if (info->micd_clamp || !arizona->pdata.jd_invert) regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, - ARIZONA_MICD_CLAMP_DB | - ARIZONA_JD1_DB, 0); - } else { - dev_dbg(arizona->dev, "Detected jack removal\n"); - - arizona_stop_mic(info); - - info->num_hpdet_res = 0; - for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) - info->hpdet_res[i] = 0; - info->mic = false; - info->hpdet_done = false; - info->hpdet_retried = false; - - snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); - - /* - * If the jack was removed during a headphone detection we - * need to wait for the headphone detection to finish, as - * it can not be aborted. We don't want to be able to start - * a new headphone detection from a fresh insert until this - * one is finished. - */ - arizona_hpdet_wait(info); - - regmap_update_bits(arizona->regmap, - ARIZONA_JACK_DETECT_DEBOUNCE, - ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, - ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); + } } -out: /* Clear trig_sts to make sure DCVDD is not forced up */ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, ARIZONA_MICD_CLAMP_FALL_TRIG_STS | @@ -1119,10 +1112,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ARIZONA_JD1_FALL_TRIG_STS | ARIZONA_JD1_RISE_TRIG_STS); - mutex_unlock(&info->lock); - - pm_runtime_put_autosuspend(arizona->dev); - return IRQ_HANDLED; } From fa58edfcce1c9f86353960700905dbeae512b419 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:41 +0700 Subject: [PATCH 207/791] ASoC: codecs: arizona: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8c683b0bb74c..4411165c3734 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -7,6 +7,7 @@ * Author: Mark Brown */ +#include #include #include #include @@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) struct arizona_priv *priv = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); if (!priv->dvfs_cached && !priv->dvfs_reqs) { ret = arizona_dvfs_enable(component); if (ret) - goto err; + return ret; } priv->dvfs_reqs |= flags; -err: - mutex_unlock(&priv->dvfs_lock); + return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_up); @@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) unsigned int old_reqs; int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); old_reqs = priv->dvfs_reqs; priv->dvfs_reqs &= ~flags; @@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) ret = arizona_dvfs_disable(component); - mutex_unlock(&priv->dvfs_lock); return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_down); @@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, struct arizona_priv *priv = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&priv->dvfs_lock); + guard(mutex)(&priv->dvfs_lock); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&priv->dvfs_lock); return ret; } EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); @@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, { 0x80, 0x0 }, }; - mutex_lock(&arizona->dac_comp_lock); - - dac_comp[1].def = arizona->dac_comp_coeff; - if (rate >= 176400) - dac_comp[2].def = arizona->dac_comp_enabled; - - mutex_unlock(&arizona->dac_comp_lock); + scoped_guard(mutex, &arizona->dac_comp_lock) { + dac_comp[1].def = arizona->dac_comp_coeff; + if (rate >= 176400) + dac_comp[2].def = arizona->dac_comp_enabled; + } regmap_multi_reg_write(arizona->regmap, dac_comp, From 95cc462fe4212e0cf64546da1ecfbd88d7f6cac4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:42 +0700 Subject: [PATCH 208/791] ASoC: codecs: aw87390: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw87390.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 020213e0ca4b..8150670fde2d 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -7,6 +7,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw87390->lock); + guard(mutex)(&aw87390->lock); ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change\n"); - mutex_unlock(&aw87390->lock); return 0; } @@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, aw87390_power_on(aw87390->aw_pa); } - mutex_unlock(&aw87390->lock); - return 1; } @@ -280,14 +278,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) return ret; } - mutex_lock(&aw87390->lock); + guard(mutex)(&aw87390->lock); ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); if (ret) dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); - mutex_unlock(&aw87390->lock); - return ret; } From 9c3035088593abcf44900c5f218f5f8bd290be63 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:43 +0700 Subject: [PATCH 209/791] ASoC: codecs: aw88081: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88081.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index d5e886a8f106..a3cc027de606 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -7,6 +7,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) struct aw88081 *aw88081 = container_of(work, struct aw88081, start_work.work); - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); aw88081_start_pa(aw88081); - mutex_unlock(&aw88081->lock); } static void aw88081_start(struct aw88081 *aw88081, bool sync_start) @@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88081->lock); return 0; } @@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, aw88081_start(aw88081, AW88081_SYNC_START); } - mutex_unlock(&aw88081->lock); - return 1; } @@ -1165,11 +1162,9 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) if (ret) return ret; - mutex_lock(&aw88081->lock); - ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); - mutex_unlock(&aw88081->lock); + guard(mutex)(&aw88081->lock); - return ret; + return aw88081_dev_init(aw88081, aw88081->aw_cfg); } static int aw88081_playback_event(struct snd_soc_dapm_widget *w, @@ -1178,7 +1173,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88081->lock); + guard(mutex)(&aw88081->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88081_start(aw88081, AW88081_ASYNC_START); @@ -1189,7 +1184,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88081->lock); return 0; } From a0c8cd0ecfe7b017a442943f23989d01c4dcc6a7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:44 +0700 Subject: [PATCH 210/791] ASoC: codecs: aw88166: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88166.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index 3f15f4ac51f7..b72f87f677dd 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -7,6 +7,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) struct aw88166 *aw88166 = container_of(work, struct aw88166, start_work.work); - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); aw88166_start_pa(aw88166); - mutex_unlock(&aw88166->lock); } static void aw88166_start(struct aw88166 *aw88166, bool sync_start) @@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88166->lock); return 0; } @@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, aw88166_start(aw88166, AW88166_SYNC_START); } - mutex_unlock(&aw88166->lock); - return 1; } @@ -1607,12 +1604,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) return ret; } - mutex_lock(&aw88166->lock); - /* aw device init */ - ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); - if (ret) - dev_err(aw88166->aw_pa->dev, "dev init failed\n"); - mutex_unlock(&aw88166->lock); + scoped_guard(mutex, &aw88166->lock) { + /* aw device init */ + ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); + if (ret) + dev_err(aw88166->aw_pa->dev, "dev init failed\n"); + } return ret; } @@ -1639,7 +1636,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88166->lock); + guard(mutex)(&aw88166->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88166_start(aw88166, AW88166_ASYNC_START); @@ -1650,7 +1647,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88166->lock); return 0; } From 9d8e98091f5a9aee81b8f758f31cd70f5c948b7b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:45 +0700 Subject: [PATCH 211/791] ASoC: codecs: aw88261: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 549783d3e75e..b931b8602566 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -8,6 +8,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88261->lock); + guard(mutex)(&aw88261->lock); ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88261->lock); return 0; } @@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, aw88261_start(aw88261); } - mutex_unlock(&aw88261->lock); - return 1; } @@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88261->lock); + guard(mutex)(&aw88261->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88261_start(aw88261); @@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88261->lock); return 0; } @@ -1188,12 +1185,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) return ret; } - mutex_lock(&aw88261->lock); - /* aw device init */ - ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); - if (ret) - dev_err(aw88261->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88261->lock); + scoped_guard(mutex, &aw88261->lock) { + /* aw device init */ + ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); + if (ret) + dev_err(aw88261->aw_pa->dev, "dev init failed"); + } return ret; } From 6699b73f0080a87af8d1e052e912bef06587c426 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:46 +0700 Subject: [PATCH 212/791] ASoC: codecs: aw88395: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88395/aw88395.c | 24 ++++++-------- sound/soc/codecs/aw88395/aw88395_device.c | 40 ++++++++--------------- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index ee0e8bd8c54c..e9ff2c79ac15 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -8,6 +8,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) struct aw88395 *aw88395 = container_of(work, struct aw88395, start_work.work); - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); aw88395_start_pa(aw88395); - mutex_unlock(&aw88395->lock); } static void aw88395_start(struct aw88395 *aw88395, bool sync_start) @@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, int ret; /* pa stop or stopping just set profile */ - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); if (ret < 0) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88395->lock); return 0; } @@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, aw88395_start(aw88395, AW88395_SYNC_START); } - mutex_unlock(&aw88395->lock); - return 1; } @@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88395->lock); + guard(mutex)(&aw88395->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88395_start(aw88395, AW88395_ASYNC_START); @@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88395->lock); return 0; } @@ -495,12 +491,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); - mutex_lock(&aw88395->lock); - /* aw device init */ - ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); - if (ret < 0) - dev_err(aw88395->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88395->lock); + scoped_guard(mutex, &aw88395->lock) { + /* aw device init */ + ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) + dev_err(aw88395->aw_pa->dev, "dev init failed"); + } return ret; } diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c index 79c3135a4110..12d42d27560d 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.c +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -8,6 +8,7 @@ // Author: Ben Yi // +#include #include #include #include @@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, u32 reg_value; int ret; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); switch (data_type) { case AW_DSP_16_DATA: ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); @@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, /* clear dsp chip select state*/ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); - mutex_unlock(&aw_dev->dsp_lock); return ret; } @@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, u32 reg_value; int ret; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); switch (data_type) { case AW_DSP_16_DATA: ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); @@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, /* clear dsp chip select state*/ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); - mutex_unlock(&aw_dev->dsp_lock); return ret; } @@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, #ifdef AW88395_DSP_I2C_WRITES u32 tmp_len; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); if (ret) - goto error_operation; + return ret; for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, &data[i], tmp_len); if (ret) - goto error_operation; + return ret; } - mutex_unlock(&aw_dev->dsp_lock); #else __be16 reg_val; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); /* i2c write */ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); if (ret) - goto error_operation; + return ret; for (i = 0; i < len; i += 2) { reg_val = cpu_to_be16p((u16 *)(data + i)); ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)reg_val); if (ret) - goto error_operation; + return ret; } - mutex_unlock(&aw_dev->dsp_lock); #endif return 0; - -error_operation: - mutex_unlock(&aw_dev->dsp_lock); - return ret; } static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, @@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) { unsigned int reg_val; - mutex_lock(&aw_dev->dsp_lock); + guard(mutex)(&aw_dev->dsp_lock); /* check the odd bits of reg 0x40 */ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check the even bits of reg 0x40 */ @@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check dsp_fw_base_addr */ @@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); - goto error; + return -EPERM; } /* check dsp_cfg_base_addr */ @@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); - goto error; + return -EPERM; } - mutex_unlock(&aw_dev->dsp_lock); return 0; - -error: - mutex_unlock(&aw_dev->dsp_lock); - return -EPERM; } int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) From 49204ed803502f13f63ec515144ad656b3381d38 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:47 +0700 Subject: [PATCH 213/791] ASoC: codecs: aw88399: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88399.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index b2ec3503f7e2..67ea073abdb7 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -7,6 +7,7 @@ // Author: Weidong Wang // +#include #include #include #include @@ -1140,9 +1141,8 @@ static void aw88399_startup_work(struct work_struct *work) struct aw88399 *aw88399 = container_of(work, struct aw88399, start_work.work); - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); aw88399_start_pa(aw88399); - mutex_unlock(&aw88399->lock); } static void aw88399_start(struct aw88399 *aw88399, bool sync_start) @@ -1702,11 +1702,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); int ret; - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); if (ret) { dev_dbg(codec->dev, "profile index does not change"); - mutex_unlock(&aw88399->lock); return 0; } @@ -1715,8 +1714,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, aw88399_start(aw88399, AW88399_SYNC_START); } - mutex_unlock(&aw88399->lock); - return 1; } @@ -1939,12 +1936,11 @@ static int aw88399_request_firmware_file(struct aw88399 *aw88399) return ret; } - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); /* aw device init */ ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); if (ret) dev_err(aw88399->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88399->lock); return ret; } @@ -1975,7 +1971,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); - mutex_lock(&aw88399->lock); + guard(mutex)(&aw88399->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: aw88399_start(aw88399, AW88399_ASYNC_START); @@ -1986,7 +1982,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, default: break; } - mutex_unlock(&aw88399->lock); return 0; } From 35150d26cb41ac4f5de6480ee468745e39fbf114 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:48 +0700 Subject: [PATCH 214/791] ASoC: codecs: cros_ec_codec: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Tzung-Bi Shih Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-12-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cros_ec_codec.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 7dc5a7c3ca96..fd2d5d7f9276 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -608,10 +609,10 @@ static void wov_copy_work(struct work_struct *w) container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); int ret; - mutex_lock(&priv->wov_dma_lock); + guard(mutex)(&priv->wov_dma_lock); if (!priv->wov_substream) { dev_warn(priv->dev, "no pcm substream\n"); - goto leave; + return; } if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) @@ -624,8 +625,6 @@ static void wov_copy_work(struct work_struct *w) msecs_to_jiffies(10)); else if (ret) dev_err(priv->dev, "failed to read audio data\n"); -leave: - mutex_unlock(&priv->wov_dma_lock); } static int wov_enable_get(struct snd_kcontrol *kcontrol, @@ -895,12 +894,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->wov_dma_lock); + guard(mutex)(&priv->wov_dma_lock); priv->wov_substream = substream; priv->wov_rp = priv->wov_wp = 0; priv->wov_dma_offset = 0; priv->wov_burst_read = true; - mutex_unlock(&priv->wov_dma_lock); return 0; } @@ -911,10 +909,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->wov_dma_lock); - wov_queue_dequeue(priv, wov_queue_size(priv)); - priv->wov_substream = NULL; - mutex_unlock(&priv->wov_dma_lock); + scoped_guard(mutex, &priv->wov_dma_lock) { + wov_queue_dequeue(priv, wov_queue_size(priv)); + priv->wov_substream = NULL; + } cancel_delayed_work_sync(&priv->wov_copy_work); From 53651103db9291c240f0b48e16004c63075215ec Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:49 +0700 Subject: [PATCH 215/791] ASoC: codecs: cs-amp-lib: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-13-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 371e99205b58..41a9a5b005c6 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { - mutex_lock(&dsp->pwr_lock); - cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); - mutex_unlock(&dsp->pwr_lock); + scoped_guard(mutex, &dsp->pwr_lock) { + cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, + controls->mem_region, + controls->alg_id); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); + } if (ret < 0) { dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); From fa08036d5d5b4777d9ef7942bf13b3071ee5ffe7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:50 +0700 Subject: [PATCH 216/791] ASoC: codecs: cs35l56: Use guard() and PM runtime scope helpers Convert the interrupt handler to use guard(mutex) for automatic mutex unlocking and PM_RUNTIME_ACQUIRE_IF_ENABLED() to manage the runtime PM reference through scope-based cleanup. This removes the explicit cleanup paths while preserving the existing behavior. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-14-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index f14e2eaaa4ee..d5817e420847 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -640,23 +640,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) unsigned int val; int rv; - irqreturn_t ret = IRQ_NONE; - if (!cs35l56_base->init_done) return IRQ_NONE; - mutex_lock(&cs35l56_base->irq_lock); + guard(mutex)(&cs35l56_base->irq_lock); - rv = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); + rv = PM_RUNTIME_ACQUIRE_ERR(&pm); if (rv < 0) { dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); - goto err_unlock; + return IRQ_NONE; } regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); if ((val & CS35L56_IRQ1_STS_MASK) == 0) { dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); - goto err; + return IRQ_NONE; } /* Ack interrupts */ @@ -680,7 +679,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) /* Check to see if unmasked bits are active */ if (!status1 && !status8 && !status20) - goto err; + return IRQ_NONE; if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) dev_crit(cs35l56_base->dev, "Amp short error\n"); @@ -688,14 +687,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) dev_crit(cs35l56_base->dev, "Overtemp error\n"); - ret = IRQ_HANDLED; - -err: - pm_runtime_put(cs35l56_base->dev); -err_unlock: - mutex_unlock(&cs35l56_base->irq_lock); - - return ret; + return IRQ_HANDLED; } EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); From 6e982e8f5a23e027e757712b750be4e4dd9d4950 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:51 +0700 Subject: [PATCH 217/791] ASoC: codecs: cs42l42: Use guard() cleanup helpers Clean up the code using guard() helpers for mutex locking and PM runtime management. No functional change intended. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-15-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 56 +++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2652a639a79a..fadf68860601 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -9,6 +9,7 @@ * Author: Michael White */ +#include #include #include #include @@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); /* Prevent race with interrupt handler */ - mutex_lock(&cs42l42->irq_lock); + guard(mutex)(&cs42l42->irq_lock); cs42l42->jack = jk; if (jk) { @@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ break; } } - mutex_unlock(&cs42l42->irq_lock); return 0; } @@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) unsigned int current_button_status; unsigned int i; - pm_runtime_get_sync(cs42l42->dev); - mutex_lock(&cs42l42->irq_lock); - if (cs42l42->suspended || !cs42l42->init_done) { - mutex_unlock(&cs42l42->irq_lock); - pm_runtime_put_autosuspend(cs42l42->dev); + guard(pm_runtime_active_auto)(cs42l42->dev); + guard(mutex)(&cs42l42->irq_lock); + if (cs42l42->suspended || !cs42l42->init_done) return IRQ_NONE; - } /* Read sticky registers to clear interurpt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { @@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) } } - mutex_unlock(&cs42l42->irq_lock); - pm_runtime_put_autosuspend(cs42l42->dev); - return IRQ_HANDLED; } EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); @@ -2163,23 +2157,23 @@ int cs42l42_suspend(struct device *dev) * future interrupts. This ensures a safe disable if the interrupt * is shared. */ - mutex_lock(&cs42l42->irq_lock); - cs42l42->suspended = true; + scoped_guard(mutex, &cs42l42->irq_lock) { + cs42l42->suspended = true; - /* Save register values that will be overwritten by shutdown sequence */ - for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { - regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); - save_regs[i] = (u8)reg; + /* Save register values that will be overwritten by shutdown sequence */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { + regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); + save_regs[i] = (u8)reg; + } + + /* Shutdown codec */ + regmap_multi_reg_write(cs42l42->regmap, + cs42l42_shutdown_seq, + ARRAY_SIZE(cs42l42_shutdown_seq)); + + /* All interrupt sources are now disabled */ } - /* Shutdown codec */ - regmap_multi_reg_write(cs42l42->regmap, - cs42l42_shutdown_seq, - ARRAY_SIZE(cs42l42_shutdown_seq)); - - /* All interrupt sources are now disabled */ - mutex_unlock(&cs42l42->irq_lock); - /* Wait for power-down complete */ msleep(CS42L42_PDN_DONE_TIME_MS); ret = regmap_read_poll_timeout(cs42l42->regmap, @@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) regcache_cache_only(cs42l42->regmap, false); regcache_mark_dirty(cs42l42->regmap); - mutex_lock(&cs42l42->irq_lock); - /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ - regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); - regcache_sync(cs42l42->regmap); + scoped_guard(mutex, &cs42l42->irq_lock) { + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); - cs42l42->suspended = false; - mutex_unlock(&cs42l42->irq_lock); + cs42l42->suspended = false; + } dev_dbg(dev, "System resumed\n"); } From d1b5d20f53df626521a90e3aa63876d47518253c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:52 +0700 Subject: [PATCH 218/791] ASoC: codecs: cs42l43: Use guard() and PM runtime scope helpers Convert mutex locking to guard(mutex) and replace explicit runtime PM handling with runtime PM scope helpers. This simplifies the control flow by removing explicit cleanup paths and unnecessary 'goto' labels. Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-16-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 138 ++++++++++++++------------------ sound/soc/codecs/cs42l43.c | 16 ++-- 2 files changed, 65 insertions(+), 89 deletions(-) diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 934666295ee3..1f0a5c5cac8a 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include #include #include #include @@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro return -EINVAL; } +static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, + unsigned int autocontrol, + unsigned int pdncntl) +{ + struct cs42l43 *cs42l43 = priv->core; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | + CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); + regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, + CS42L43_RING_SENSE_EN_MASK, pdncntl); + + dev_dbg(priv->dev, "Successfully configured accessory detect\n"); +} + int cs42l43_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *d) { @@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, dev_dbg(priv->dev, "Configure accessory detect\n"); - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); return ret; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); priv->jack_hp = jack; - if (!jack) - goto done; + if (!jack) { + cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); + return 0; + } ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); if (ret != -EINVAL) { if (ret < 0) { dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", ret); - goto error; + return ret; } if (ret > CS42L43_N_BUTTONS) { ret = -EINVAL; dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); - goto error; + return ret; } ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", @@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, if (ret < 0) { dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", ret); - goto error; + return ret; } } else { priv->buttons[0] = 70; @@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); if (ret < 0) - goto error; + return ret; hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; @@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, ARRAY_SIZE(cs42l43_accdet_ramp_ms)); if (ret < 0) - goto error; + return ret; hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; @@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->bias_sense_ua, cs42l43_accdet_bias_sense, ARRAY_SIZE(cs42l43_accdet_bias_sense)); if (ret < 0) - goto error; + return ret; if (priv->bias_sense_ua) autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; @@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_debounce_ms); if (ret < 0 && ret != -EINVAL) { dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); - goto error; + return ret; } /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ @@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; @@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; @@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, NULL, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; @@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, NULL, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) - goto error; + return ret; ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; pdncntl |= CS42L43_RING_SENSE_EN_MASK; @@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | CS42L43_AUTO_HSDET_TIME_MASK, hs2); -done: - ret = 0; + cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); - regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, - CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | - CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); - regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, - CS42L43_RING_SENSE_EN_MASK, pdncntl); - - dev_dbg(priv->dev, "Successfully configured accessory detect\n"); - -error: - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); - - return ret; + return 0; } static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) @@ -369,22 +374,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) { struct cs42l43_codec *priv = data; struct cs42l43 *cs42l43 = priv->core; - irqreturn_t iret = IRQ_NONE; unsigned int buttons = 0; unsigned int val = 0; int i, ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); - return iret; + return IRQ_NONE; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); if (!priv->button_detect_running) { dev_dbg(priv->dev, "Spurious button press IRQ\n"); - goto error; + return IRQ_NONE; } // Wait for 2 full cycles of comb filter to ensure good reading @@ -395,12 +400,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) /* Bail if jack removed, the button is irrelevant and likely invalid */ if (!cs42l43_jack_present(priv)) { dev_dbg(priv->dev, "Button ignored due to removal\n"); - goto error; + return IRQ_NONE; } if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { dev_dbg(priv->dev, "Button ignored due to bias sense\n"); - goto error; + return IRQ_NONE; } val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; @@ -423,45 +428,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); - iret = IRQ_HANDLED; - -error: - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); - - return iret; + return IRQ_HANDLED; } irqreturn_t cs42l43_button_release(int irq, void *data) { struct cs42l43_codec *priv = data; - irqreturn_t iret = IRQ_NONE; int ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); - return iret; + return IRQ_NONE; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); - if (priv->button_detect_running) { - dev_dbg(priv->dev, "Button release IRQ\n"); - - snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); - - iret = IRQ_HANDLED; - } else { + if (!priv->button_detect_running) { dev_dbg(priv->dev, "Spurious button release IRQ\n"); + return IRQ_NONE; } - mutex_unlock(&priv->jack_lock); + dev_dbg(priv->dev, "Button release IRQ\n"); + snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); - pm_runtime_put_autosuspend(priv->dev); - - return iret; + return IRQ_HANDLED; } void cs42l43_bias_sense_timeout(struct work_struct *work) @@ -471,13 +463,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) struct cs42l43 *cs42l43 = priv->core; int ret; - ret = pm_runtime_resume_and_get(priv->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) { dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); return; } - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); if (cs42l43_jack_present(priv) && priv->button_detect_running) { dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); @@ -490,10 +483,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); } - - mutex_unlock(&priv->jack_lock); - - pm_runtime_put_autosuspend(priv->dev); } static const struct reg_sequence cs42l43_3pole_patch[] = { @@ -895,9 +884,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); ucontrol->value.integer.value[0] = priv->jack_override; - mutex_unlock(&priv->jack_lock); return 0; } @@ -913,17 +901,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u if (override >= e->items) return -EINVAL; - mutex_lock(&priv->jack_lock); + guard(mutex)(&priv->jack_lock); - if (!cs42l43_jack_present(priv)) { - mutex_unlock(&priv->jack_lock); + if (!cs42l43_jack_present(priv)) return -EBUSY; - } - if (override == priv->jack_override) { - mutex_unlock(&priv->jack_lock); + if (override == priv->jack_override) return 0; - } priv->jack_override = override; @@ -983,7 +967,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u cs42l43_jack_override_modes[override].report); } - mutex_unlock(&priv->jack_lock); - return 1; } diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 1d133577702e..47308cd870fb 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; - mutex_lock(&priv->spk_vu_lock); + guard(mutex)(&priv->spk_vu_lock); regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, CS42L43_AMP1_2_VU_MASK, 0); - - mutex_unlock(&priv->spk_vu_lock); } static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) @@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, struct cs42l43 *cs42l43 = priv->core; int ret; - mutex_lock(&cs42l43->pll_lock); + guard(mutex)(&cs42l43->pll_lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&cs42l43->pll_lock); - return ret; } @@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, { struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); struct cs42l43 *cs42l43 = priv->core; - int ret; - mutex_lock(&cs42l43->pll_lock); - ret = cs42l43_set_pll(priv, src, freq); - mutex_unlock(&cs42l43->pll_lock); + guard(mutex)(&cs42l43->pll_lock); - return ret; + return cs42l43_set_pll(priv, src, freq); } static int cs42l43_component_probe(struct snd_soc_component *component) From 2f952d2ff5f2c23d1f0083f6d2196bdc12a73820 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:53 +0700 Subject: [PATCH 219/791] ASoC: codecs: cs42l84: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-17-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l84.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index f2a58163de0e..36533a13319d 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); /* Prevent race with interrupt handler */ - mutex_lock(&cs42l84->irq_lock); + guard(mutex)(&cs42l84->irq_lock); cs42l84->jack = jk; snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); - mutex_unlock(&cs42l84->irq_lock); return 0; } @@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) u8 current_ring_state; int i; - mutex_lock(&cs42l84->irq_lock); + guard(mutex)(&cs42l84->irq_lock); /* Read sticky registers to clear interrupt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, @@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) break; } - mutex_unlock(&cs42l84->irq_lock); - return IRQ_HANDLED; } @@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) } } - mutex_unlock(&cs42l84->irq_lock); - return IRQ_HANDLED; } From e6eb5e269d88d2d8622c070c865a12d65d494b43 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:54 +0700 Subject: [PATCH 220/791] ASoC: codecs: cs43130: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Charles Keepax Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-18-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs43130.c | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index e7b06f962790..48d88dad4ceb 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -6,6 +6,7 @@ * * Authors: Li Xu */ +#include #include #include #include @@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, unsigned int required_clk; u8 dsd_speed; - mutex_lock(&cs43130->clk_mutex); - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - if (!(CS43130_MCLK_22M % params_rate(params))) - required_clk = CS43130_MCLK_22M; - else - required_clk = CS43130_MCLK_24M; + scoped_guard(mutex, &cs43130->clk_mutex) { + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; - cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); - if (cs43130->pll_bypass) - cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); - else - cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } + + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); } - cs43130->clk_req++; - if (cs43130->clk_req == 2) - cs43130_pcm_dsd_mix(true, cs43130->regmap); - mutex_unlock(&cs43130->clk_mutex); - switch (params_rate(params)) { case 176400: dsd_speed = 0; @@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, unsigned int required_clk; u8 dsd_speed; - mutex_lock(&cs43130->clk_mutex); - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - if (!(CS43130_MCLK_22M % params_rate(params))) - required_clk = CS43130_MCLK_22M; - else - required_clk = CS43130_MCLK_24M; + scoped_guard(mutex, &cs43130->clk_mutex) { + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; - cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); - if (cs43130->pll_bypass) - cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); - else - cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } + + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); } - cs43130->clk_req++; - if (cs43130->clk_req == 2) - cs43130_pcm_dsd_mix(true, cs43130->regmap); - mutex_unlock(&cs43130->clk_mutex); - switch (dai->id) { case CS43130_ASP_DOP_DAI: case CS43130_XSP_DOP_DAI: @@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); - mutex_lock(&cs43130->clk_mutex); + guard(mutex)(&cs43130->clk_mutex); cs43130->clk_req--; if (!cs43130->clk_req) { /* no DAI is currently using clk */ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); cs43130_pcm_dsd_mix(false, cs43130->regmap); } - mutex_unlock(&cs43130->clk_mutex); return 0; } From a97c200c2fed53e698e8f99cb2cf584f33246696 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:55 +0700 Subject: [PATCH 221/791] ASoC: codecs: cs47l15: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-19-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l15.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index da64e0a1db28..2f30dd834709 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); struct madera *madera = cs47l15->core.madera; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); } From db698e37213b29ccd680950593484652ae9f527a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:56 +0700 Subject: [PATCH 222/791] ASoC: codecs: cs47l35: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-20-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l35.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index a8fe5a99a8bb..e77ac7bc8466 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l35->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L35_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); From cea70877a2a191a6149544da7efeb12da2509cc8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:57 +0700 Subject: [PATCH 223/791] ASoC: codecs: cs47l85: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-21-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l85.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index 42fafb0b392c..9ee60616c04c 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l85->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L85_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); From 1b681fda5e51d4ff8b7c39a30e7069e780ec304f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:58 +0700 Subject: [PATCH 224/791] ASoC: codecs: cs47l90: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-22-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l90.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index 77e8aabb241a..6ce603bfd500 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) struct madera *madera = cs47l90->core.madera; int i; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; for (i = 0; i < CS47L90_NUM_ADSP; i++) wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); From f452b00f97ced4ad14913a3e6de4f3086a3f6c4c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:49:59 +0700 Subject: [PATCH 225/791] ASoC: codecs: cs47l92: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-23-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l92.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index 868237bd6d91..07bc303d9797 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, madera->regmap); - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = snd_soc_component_to_dapm(component); - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = snd_soc_component_to_dapm(component); ret = madera_init_inputs(component); if (ret) @@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); struct madera *madera = cs47l92->core.madera; - mutex_lock(&madera->dapm_ptr_lock); - madera->dapm = NULL; - mutex_unlock(&madera->dapm_ptr_lock); + scoped_guard(mutex, &madera->dapm_ptr_lock) + madera->dapm = NULL; wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); } From 6ced86b1e21b951d33031910606219fb6a496d4f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:50:00 +0700 Subject: [PATCH 226/791] ASoC: codecs: cs48l32: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-24-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs48l32.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c index 086ed0f57a85..a9967771124e 100644 --- a/sound/soc/codecs/cs48l32.c +++ b/sound/soc/codecs/cs48l32.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v int ret; /* Prevent any mixer mux changes while we do this */ - mutex_lock(&cs48l32_codec->rate_lock); + guard(mutex)(&cs48l32_codec->rate_lock); /* The write must be guarded by a number of SYSCLK cycles */ cs48l32_spin_sysclk(cs48l32_codec); ret = snd_soc_put_enum_double(kcontrol, ucontrol); cs48l32_spin_sysclk(cs48l32_codec); - mutex_unlock(&cs48l32_codec->rate_lock); - return ret; } @@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; unsigned int base = dai->driver->base; unsigned int current_asp_rate, target_asp_rate; - bool change_rate_domain = false; int ret; if (clk_id == dai_priv->clk) @@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != (target_asp_rate & CS48L32_ASP_RATE_MASK)) { - change_rate_domain = true; - - mutex_lock(&cs48l32_codec->rate_lock); /* Guard the rate change with SYSCLK cycles */ - cs48l32_spin_sysclk(cs48l32_codec); - } - - snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, - CS48L32_ASP_RATE_MASK, target_asp_rate); - - if (change_rate_domain) { - cs48l32_spin_sysclk(cs48l32_codec); - mutex_unlock(&cs48l32_codec->rate_lock); + scoped_guard(mutex, &cs48l32_codec->rate_lock) { + cs48l32_spin_sysclk(cs48l32_codec); + snd_soc_component_update_bits(component, + base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_RATE_MASK, + target_asp_rate); + cs48l32_spin_sysclk(cs48l32_codec); + } } } From 1b302f955ecb2831f7fcc626fa6e35dc8d80d9d7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 8 Jul 2026 19:50:01 +0700 Subject: [PATCH 227/791] ASoC: codecs: cx2072x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260708125002.202515-25-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cx2072x.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 83c6cbd40804..908344ee5892 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -9,6 +9,7 @@ // #include +#include #include #include #include @@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) unsigned int type = 0; int state = 0; - mutex_lock(&cx2072x->lock); + guard(mutex)(&cx2072x->lock); regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); jack = jack >> 24; @@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) /* clear interrupt */ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); - mutex_unlock(&cx2072x->lock); - dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", type, state); return state; From 72fe2ef11df581a36b4134107eb792eedd4bc35e Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Thu, 2 Jul 2026 23:31:46 +0200 Subject: [PATCH 228/791] ASoC: meson: aiu-formatter-i2s: remove pipeline reset from prepare 'aiu-fifo-i2s' (DAI FE) already resets the I2S pipeline in 'aiu_fifo_i2s_trigger' for all relevant trigger scenarios, right before starting the FIFO. Since the DAI triggering order is the default one (FE before BE), the reset performed in 'aiu_formatter_i2s_prepare' happens after the FIFO has already been reset and started, which corrupts playback in 24-bit mode. Remove the duplicated reset from the formatter. Signed-off-by: Valerio Setti Link: https://patch.msgid.link/20260702-fix-24-bit-i2s-playback-v2-1-9c008ff0b211@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-formatter-i2s.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c index b4604734fe88..cb554c2e7ce4 100644 --- a/sound/soc/meson/aiu-formatter-i2s.c +++ b/sound/soc/meson/aiu-formatter-i2s.c @@ -13,7 +13,6 @@ #define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) #define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) #define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) -#define AIU_RST_SOFT_I2S_FAST BIT(0) #define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) @@ -55,11 +54,11 @@ static int aiu_formatter_i2s_prepare(struct regmap *map, { /* Always operate in split (classic interleaved) mode */ unsigned int desc = 0; - unsigned int tmp; - /* Reset required to update the pipeline */ - regmap_write(map, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); - regmap_read(map, AIU_I2S_SYNC, &tmp); + /* + * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at + * trigger time. + */ switch (ts->physical_width) { case 16: /* Nothing to do */ From eceeb7a564f8422a6b8fbfdcc34f6f1c340bc247 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 14 Jul 2026 23:19:49 +0800 Subject: [PATCH 229/791] ASoC: sun4i-codec: Sort sound related #include statements Some of the sound related #include statements are not ordered in alphabetic order. Sort them. This results in no functional change. Signed-off-by: Chen-Yu Tsai Acked-by: Jernej Skrabec Link: https://patch.msgid.link/20260714151950.316035-1-wens@kernel.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index f4e22af594fa..b45010875518 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -22,13 +22,13 @@ #include #include +#include +#include #include #include #include #include #include -#include -#include /* Codec DAC digital controls and FIFO registers */ #define SUN4I_CODEC_DAC_DPC (0x00) From 4392569d95e6a794780fc33ce168744c67cb162e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Jul 2026 17:33:16 +0100 Subject: [PATCH 230/791] ALSA: aloop: make read-only array texts static const Don't populate the read-only const array texts on the stack at run time, instead make it static. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20260714163316.183165-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 236f49a7fb8b..7ebe8e5c9303 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1668,7 +1668,7 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol, static int loopback_access_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - const char * const texts[] = {"Interleaved", "Non-interleaved"}; + static const char * const texts[] = {"Interleaved", "Non-interleaved"}; return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } From 70d0d7b24d8606effdcbc0b0dec6955759ed36c7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 12:11:12 +0700 Subject: [PATCH 231/791] ASoC: tegra: tegra20_das: Use dev_err_probe() for error handling Replace the existing dev_err() and PTR_ERR() sequence with dev_err_probe(), preserving the original error code while simplifying the error handling. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260715051115.17385-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_das.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index b48cc4a6967b..ea8bc9bcfa67 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &tegra20_das_regmap_config); - if (IS_ERR(das->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - return PTR_ERR(das->regmap); - } + if (IS_ERR(das->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), + "regmap init failed\n"); tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, TEGRA20_DAS_DAP_SEL_DAC1); From b82384bffb27467e2e3966831d83eff3a6e1fbbb Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 12:11:13 +0700 Subject: [PATCH 232/791] ASoC: tegra: tegra210_adx: Return the original error directly snd_soc_add_component_controls() already reports failures internally. Return its error code directly instead of logging the error again in the component probe callback. Also remove the now unnecessary local error variable. Signed-off-by: bui duc phuc Reviewed-by: Thierry Reding Link: https://patch.msgid.link/20260715051115.17385-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_adx.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 15a94196ee1a..e75bf50f8fb0 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { static int tegra210_adx_component_probe(struct snd_soc_component *component) { struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); - int err = 0; - if (adx->soc_data->num_controls) { - err = snd_soc_add_component_controls(component, adx->soc_data->controls, - adx->soc_data->num_controls); - if (err) - dev_err(component->dev, "can't add ADX controls, err: %d\n", err); - } + if (adx->soc_data->num_controls) + return snd_soc_add_component_controls(component, adx->soc_data->controls, + adx->soc_data->num_controls); - return err; + return 0; } static const struct snd_soc_component_driver tegra210_adx_cmpnt = { From f7cbc51424cd22098b0e6c09cbe338d7cd6137a7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 12:11:14 +0700 Subject: [PATCH 233/791] ASoC: tegra: tegra210_amx: Return the original error directly snd_soc_add_component_controls() already reports failures internally. Return its error code directly instead of logging the error again in the component probe callback. Also remove the now unnecessary local error variable. Signed-off-by: bui duc phuc Reviewed-by: Thierry Reding Link: https://patch.msgid.link/20260715051115.17385-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_amx.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c index cc1f9c158191..6a67ce6128b4 100644 --- a/sound/soc/tegra/tegra210_amx.c +++ b/sound/soc/tegra/tegra210_amx.c @@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { static int tegra210_amx_component_probe(struct snd_soc_component *component) { struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); - int err = 0; - if (amx->soc_data->num_controls) { - err = snd_soc_add_component_controls(component, amx->soc_data->controls, - amx->soc_data->num_controls); - if (err) - dev_err(component->dev, "can't add AMX controls, err: %d\n", err); - } + if (amx->soc_data->num_controls) + return snd_soc_add_component_controls(component, amx->soc_data->controls, + amx->soc_data->num_controls); - return err; + return 0; } static const struct snd_soc_component_driver tegra210_amx_cmpnt = { From 226791fa6dbb1e1c6d4c8ac4d10b6c73f0dc32e1 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 12:11:15 +0700 Subject: [PATCH 234/791] ASoC: tegra: tegra210: Use devm_clk_get_optional() for sync_input clock The existing comment states that the sync_input clock is only needed when another I/O is configured to use the current I2S instance as its input clock, and the current code does not treat its absence as an error. Use devm_clk_get_optional() to match the existing behaviour while still reporting real failures via dev_err_probe(). Update the comment to describe the optional nature of the clock rather than the previous error handling. Signed-off-by: bui duc phuc Reviewed-by: Thierry Reding Link: https://patch.msgid.link/20260715051115.17385-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_i2s.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index ff8c72fc38c5..e7e26e917291 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -1078,13 +1078,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) "can't retrieve I2S bit clock\n"); /* - * Not an error, as this clock is needed only when some other I/O - * requires input clock from current I2S instance, which is - * configurable from DT. + * This clock is optional and is only needed when another I/O uses + * the current I2S instance as its input clock, as configured in DT. */ - i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); + i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); if (IS_ERR(i2s->clk_sync_input)) - dev_dbg(dev, "can't retrieve I2S sync input clock\n"); + return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), + "can't retrieve I2S sync input clock\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) From 3330b5f6d8dea66df51c07362f01a39ad9845aad Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Wed, 15 Jul 2026 13:01:33 +0100 Subject: [PATCH 235/791] ASoC: cs35l56: Add support for CS35L62 for SoundWire CS35L62 uses the same control and regmap interface as CS35L63 so support for it can be added into the CS35L56 driver. Signed-off-by: Stefan Binding Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260715120135.939280-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 2 ++ sound/soc/codecs/cs35l56-shared.c | 4 ++++ sound/soc/codecs/cs35l56.c | 1 + 3 files changed, 7 insertions(+) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 0a55b93b96f9..1e442f43b306 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -457,6 +457,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi regmap_config = &cs35l56_regmap_sdw; break; case 0x3563: + case 0x3562: regmap_config = &cs35l63_regmap_sdw; break; default: @@ -510,6 +511,7 @@ static const struct sdw_device_id cs35l56_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), + SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index f14e2eaaa4ee..ab811f95f935 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) ARRAY_SIZE(cs35l56_patch_fw)); break; case 0x63: + case 0x62: ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, ARRAY_SIZE(cs35l63_patch_fw)); break; @@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) } break; case 0x63: + case 0x62: cs35l56_base->fw_reg = &cs35l63_fw_reg; break; } @@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) } break; case 0x63: + case 0x62: regmap_multi_reg_write_bypassed(cs35l56_base->regmap, cs35l63_system_reset_seq, ARRAY_SIZE(cs35l63_system_reset_seq)); @@ -1470,6 +1473,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) cs35l56_base->calibration_controls = &cs35l56_calibration_controls; break; case 0x35A630: + case 0x35A620: cs35l56_base->calibration_controls = &cs35l63_calibration_controls; devid = devid >> 4; break; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 570a68829ccd..f365f76ce56c 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1402,6 +1402,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) ARRAY_SIZE(cs35l56_controls)); break; case 0x63: + case 0x62: ret = snd_soc_add_component_controls(component, cs35l63_controls, ARRAY_SIZE(cs35l63_controls)); break; From 0306d211558196e051b83e067ee4c30e1b6f94a8 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Wed, 15 Jul 2026 13:01:34 +0100 Subject: [PATCH 236/791] ASoC: sdw_utils: Add codec info for CS35L62 CS35L62 is very similar to CS35L63, and uses the same driver, so we can use the same configuration. Signed-off-by: Stefan Binding Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260715120135.939280-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..e490f37448b1 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, .dai_num = 2, }, + { + .vendor_id = 0x01fa, + .part_id = 0x3562, + .name_prefix = "AMP", + .is_amp = true, + .dais = { + { + .direction = {true, false}, + .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .init = asoc_sdw_cs_amp_init, + .rtd_init = asoc_sdw_cs_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), + .widgets = generic_spk_widgets, + .num_widgets = ARRAY_SIZE(generic_spk_widgets), + }, + { + .direction = {false, true}, + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, + }, { .vendor_id = 0x01fa, .part_id = 0x3563, From 73080a7976edde1c61f3654308ffe77c428ad6a2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 15 Jul 2026 02:13:15 +0000 Subject: [PATCH 237/791] ASoC: generic: Card name parsing should be called after xxx_for_each_link() "tidyup simple_util_parse_xxx() in xxx_parse_of()" commit changed the function call order. But simple_util_parse_card_name() should be called after dai_link settings, because it might use dai_link->name as card->name. Fixes: fa6222d5e121 ("ASoC: audio-graph-card2: tidyup simple_util_parse_xxx() in audio_graph2_parse_of()") Fixes: b8081307f5c9 ("ASoC: audio-graph-card: tidyup simple_util_parse_xxx() in audio_graph_parse_of()") Fixes: 27ecf4da5ad3 ("ASoC: simple-card: tidyup simple_util_parse_xxx() in simple_parse_of()") Reported-by: Mark Brown Link: https://lore.kernel.org/r/b81ebfa2-6a35-4ff0-9d04-b867233eda4d@sirena.org.uk Reported-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20260714103428.2318895-1-geert+renesas@glider.be Signed-off-by: Kuninori Morimoto Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/87zezt0zlw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 9 +++++---- sound/soc/generic/audio-graph-card2.c | 9 +++++---- sound/soc/generic/simple-card.c | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 73ea562ce52b..0a8a6d891d1f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -587,10 +587,6 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto end; - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; - memset(li, 0, sizeof(*li)); ret = graph_for_each_link(priv, li, graph_dai_link_of, @@ -598,6 +594,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; + /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; + snd_soc_card_set_drvdata(card, priv); simple_util_debug_info(priv); diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index e3e92025b317..9fb3d3df5cf6 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1344,10 +1344,6 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if (ret < 0) goto end; - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; - ret = simple_util_parse_aux_devs(priv, NULL); if (ret < 0) goto err; @@ -1357,6 +1353,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if (ret < 0) goto err; + /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; + snd_soc_card_set_drvdata(card, priv); if ((hooks) && (hooks)->hook_post) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6e98e973e525..e7ad7af04714 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -704,10 +704,6 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto end; - ret = simple_util_parse_card_name(priv, PREFIX); - if (ret < 0) - goto err; - ret = simple_util_parse_aux_devs(priv, PREFIX); if (ret < 0) goto err; @@ -720,6 +716,11 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto err; + /* Card name should be set after simple_for_each_link() */ + ret = simple_util_parse_card_name(priv, PREFIX); + if (ret < 0) + goto err; + ret = simple_populate_aux(priv); if (ret < 0) goto err; From 6173e18dd47cce430506c2f642e0b8d8db51ff1e Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Wed, 15 Jul 2026 17:25:13 +0530 Subject: [PATCH 238/791] ASoC: codecs: lpass-wsa-macro: check clk_set_rate() return value clk_set_rate() returns 0 on success or a negative errno on failure but the WSA macro probe function is ignoring it. Check the return value and bail out of probe on failure. Reviewed-by: Konrad Dybcio Signed-off-by: Ajay Kumar Nandam Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-xo-sd-codec-wsa-va-clk-set-rate-v2-1-16ca64c2b929@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index f511816aa4a0..fc9e0a37c042 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2773,8 +2773,13 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); - clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); + ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); + if (ret) + return ret; + + ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); + if (ret) + return ret; ret = devm_pm_clk_create(dev); if (ret) From 4fddda16f939b6ee53c6946ad71a2ea1fabf43c5 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Wed, 15 Jul 2026 17:25:14 +0530 Subject: [PATCH 239/791] ASoC: codecs: lpass-va-macro: check clk_set_rate() return value clk_set_rate() returns 0 on success or a negative errno on failure but the VA macro probe function is ignoring it. Check the return value and bail out of probe on failure. Reviewed-by: Konrad Dybcio Signed-off-by: Ajay Kumar Nandam Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-xo-sd-codec-wsa-va-clk-set-rate-v2-2-16ca64c2b929@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 946051698420..dbc5795b9273 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1605,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) va->has_npl_clk = data->has_npl_clk; /* mclk rate */ - clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + if (ret) + goto err; if (va->has_npl_clk) { va->npl = devm_clk_get(dev, "npl"); @@ -1614,7 +1616,9 @@ static int va_macro_probe(struct platform_device *pdev) goto err; } - clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + if (ret) + goto err; } ret = devm_pm_clk_create(dev); From fcf2a365da23994f47af0f1203cf245d5e467b6a Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Fri, 17 Jul 2026 17:15:40 +0800 Subject: [PATCH 240/791] ASoC: mediatek: mt8189: Remove redundant else-if branch with identical body Fix a compiler warning about a condition with no effect: sound/mediatek/mt8189/mt8189-dai-adda.c:388:7-9: WARNING: possible condition with no effect (if == else) The MTKAIF_PROTOCOL_2 branch and the else branch both write the same value 0xB0 to AFE_AUD_PAD_TOP_CFG0, making the else-if condition meaningless. Remove the redundant branch. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260717091542.721877-2-wangdich9700@163.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8189/mt8189-dai-adda.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c index ad5b9546ff63..5c41a6386204 100644 --- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c @@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, if (event == SND_SOC_DAPM_PRE_PMU) { if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); - else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); else regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); } From a46ccc71877e962783e0fffa105e41615904c511 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Fri, 17 Jul 2026 17:15:42 +0800 Subject: [PATCH 241/791] ASoC: fsl_easrc: Use div64_u64 for 64-by-64 division Fix a coccinelle warning about do_div() truncating a 64-bit divisor: sound/soc/fsl/fsl_easrc.c:2061:2-8: WARNING: do_div() does a 64-by-32 division, please consider using div64_u64 instead. In fsl_easrc_m2m_calc_out_len(), val1 is computed as: val1 = (u64)in_rate << frac_bits; // frac_bits up to 39 do_div(val1, out_rate); val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31); val1 = val1 >> 12; In the worst case (in_rate=384000, out_rate=8000, frac_bits=39): val1 = 384000 << 39 / 8000 = 26,388,279,068,672 val1 >> 12 = 6,440,497,829 (33 bits, exceeds 32-bit range) val1 is then used as the divisor in do_div(val2, val1), where do_div() silently truncates it to 32 bits, producing incorrect results. Use div64_u64() to perform a proper 64-by-64 division. Fixes: 955ac624058f ("ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers") Cc: stable@vger.kernel.org Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260717091542.721877-4-wangdich9700@163.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index edfd943197a0..15a342496760 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2050,7 +2050,7 @@ static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buff /* right shift 12 bit to make ratio in 32bit space */ val2 = (u64)in_samples << (frac_bits - 12); val1 = val1 >> 12; - do_div(val2, val1); + val2 = div64_u64(val2, val1); out_samples = val2; out_length = out_samples * out_width * channels; From c462b2e14589780c2aba13156c117d90b2381157 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 17 Jul 2026 10:26:55 +0100 Subject: [PATCH 242/791] ASoC: cs35l56: Remove unnecessary goto in cs35l56_runtime_resume_common() The 'goto out_sync' in cs35l56_runtime_resume_common() is unnecessary because it only skips a single if-statement and function call. It can be replaced by inverting the conditional and merging it with the next if-statement. This was a legacy of an early version of the function, where more code was skipped by the goto. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260717092655.1730484-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index f20bcdfd35af..b40bd3a8d27b 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -811,14 +811,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou if (!cs35l56_base->init_done) return 0; - if (!cs35l56_base->can_hibernate) - goto out_sync; - - /* Must be done before releasing cache-only */ - if (!is_soundwire) + /* Hibernate wake must be done before releasing cache-only */ + if (cs35l56_base->can_hibernate && !is_soundwire) cs35l56_issue_wake_event(cs35l56_base); -out_sync: ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) { dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); From d542c74cac07fdc6416fac04cf96c7e90c6b90f7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Jul 2026 19:22:47 +0200 Subject: [PATCH 243/791] ASoC: SOF: Intel: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260717172318.1784073-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/icl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index c1018893750c..549bb4ca73e1 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -9,10 +9,14 @@ * Hardware interface for audio DSP on IceLake. */ -#include -#include -#include +#include #include +#include +#include +#include +#include +#include + #include "../ipc4-priv.h" #include "../ops.h" #include "hda.h" From 50b2e4bfb4d4b7eee61a53a1524898b3f0512956 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Jul 2026 21:28:55 -0700 Subject: [PATCH 244/791] ASoC: wcd9335: switch to using sleeping variants of gpiod API The driver does not use gpiod API calls in an atomic context. Switch to gpiod_set_value_cansleep() calls to allow using the driver with GPIO controllers that might need process context to operate. Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/alsAsTcQrpnnR46d@google.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index e3ca5ca6de3d..40de279a9750 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) */ usleep_range(600, 650); - gpiod_set_value(wcd->reset_gpio, 1); + gpiod_set_value_cansleep(wcd->reset_gpio, 1); msleep(20); - gpiod_set_value(wcd->reset_gpio, 0); + gpiod_set_value_cansleep(wcd->reset_gpio, 0); msleep(20); return 0; From c89b22faa9ba8ffaed2c696a360528844b5ba069 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 10 Jul 2026 18:02:49 +0700 Subject: [PATCH 245/791] ASoC: uniphier: Drop redundant error messages in probe Return the error directly when the helper already reports the failure, avoiding duplicate log messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260710110249.31830-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/uniphier/aio-cpu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index d3dba21b2d04..78c82bf42a8f 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, chip->chip_spec->dais, chip->chip_spec->num_dais); - if (ret) { - dev_err(dev, "Register component failed.\n"); + if (ret) goto err_out_reset; - } ret = uniphier_aiodma_soc_register_platform(pdev); - if (ret) { - dev_err(dev, "Register platform failed.\n"); + if (ret) goto err_out_reset; - } return 0; From 4797f267159029a6ac93f3d25c22eed3d07fda42 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Fri, 17 Jul 2026 17:15:41 +0800 Subject: [PATCH 246/791] ALSA: sparc/dbri: Fix "possible condition with no effect" warning Fix a compiler warning about a condition with no effect: sound/sparc/dbri.c:1843:1-3: WARNING: possible condition with no effect (if == else) When DBRI_DEBUG is not defined, dprintk expands to an empty do-while statement, making both branches of the if-else no-ops. Guard the entire debug block with #ifdef DBRI_DEBUG to eliminate the warning and keep the rval reference consistent with its declaration scope. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260717091542.721877-3-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/sparc/dbri.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 2f5f62079fa4..ccaa36525f8d 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -1840,6 +1840,7 @@ static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x) int rval = D_INTR_GETRVAL(x); #endif +#ifdef DBRI_DEBUG if (channel == D_INTR_CMD) { dprintk(D_CMD, "INTR: Command: %-5s Value:%d\n", cmds[command], val); @@ -1847,6 +1848,7 @@ static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x) dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n", channel, code, rval); } +#endif switch (code) { case D_INTR_CMDI: From 2aaa41cf974f83a6fb105422bac4e2f107150774 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 17 Jul 2026 09:24:33 +0800 Subject: [PATCH 247/791] ASoC: meson: Keep link pointers valid on realloc failure meson_card_reallocate_links() grows the DAI link and private data arrays with two consecutive krealloc() calls and updates the owner pointers only after both calls have succeeded. A successful krealloc() may move the data: it frees the old block and returns a new one. When that happens for the link array and the second krealloc() then fails, card->dai_link still points to the block that krealloc() already freed, and the error path frees the new block too. The probe error path then calls meson_card_clean_references(), which dereferences card->dai_link and kfree()s it again, resulting in a use-after-free and a double free. Commit card->dai_link and card->num_links right after the first krealloc() succeeds, so the pointer always refers to a valid allocation that meson_card_clean_references() can walk and free. krealloc() with __GFP_ZERO zero-initializes the added entries, so walking them on the error path is safe. With both failure paths reduced to a plain return, drop the goto labels and the error message. Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") Signed-off-by: Linmao Li Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260717012433.1432285-1-lilinmao@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/meson/meson-card-utils.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index cdb759b466ad..8617a4661a33 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -50,25 +50,20 @@ int meson_card_reallocate_links(struct snd_soc_card *card, num_links * sizeof(*priv->card.dai_link), GFP_KERNEL | __GFP_ZERO); if (!links) - goto err_links; + return -ENOMEM; + + priv->card.dai_link = links; + priv->card.num_links = num_links; ldata = krealloc(priv->link_data, num_links * sizeof(*priv->link_data), GFP_KERNEL | __GFP_ZERO); + /* meson_card_clean_references() will free the links on this error path */ if (!ldata) - goto err_ldata; + return -ENOMEM; - priv->card.dai_link = links; priv->link_data = ldata; - priv->card.num_links = num_links; return 0; - -err_ldata: - kfree(links); -err_links: - dev_err(priv->card.dev, "failed to allocate links\n"); - return -ENOMEM; - } EXPORT_SYMBOL_GPL(meson_card_reallocate_links); From 2d0769387594eb26df33c72c08c6429e4fddf5fd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 17:09:29 -0700 Subject: [PATCH 248/791] ALSA: sis7019: Use pcim_iomap_region() for MMIO BAR Replace the open-coded pcim_request_all_regions() + devm_ioremap() pair with per-BAR pcim helpers: reserve BAR0 (the I/O port region, used via inl/outl) with pcim_request_region(), and reserve + iomap BAR1 (MMIO) with a single pcim_iomap_region() call. This folds the BAR1 reserve and iomap into one managed call. The error check moves from a NULL test to IS_ERR(), since pcim_iomap_region() returns an IOMEM_ERR_PTR on failure. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260720000929.1432533-1-rosenp@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/sis7019.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 4be085d27712..55c1b11a2900 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1260,16 +1260,16 @@ static int sis_chip_create(struct snd_card *card, sis->irq = -1; sis->ioport = pci_resource_start(pci, 0); - rc = pcim_request_all_regions(pci, "SiS7019"); + rc = pcim_request_region(pci, 0, "SiS7019"); if (rc) { - dev_err(&pci->dev, "unable request regions\n"); + dev_err(&pci->dev, "unable request I/O region\n"); return rc; } - sis->ioaddr = devm_ioremap(&pci->dev, pci_resource_start(pci, 1), 0x4000); - if (!sis->ioaddr) { + sis->ioaddr = pcim_iomap_region(pci, 1, "SiS7019"); + if (IS_ERR(sis->ioaddr)) { dev_err(&pci->dev, "unable to remap MMIO, aborting\n"); - return -EIO; + return PTR_ERR(sis->ioaddr); } rc = sis_alloc_suspend(sis); From acc414eda8e9b2e33c7afcd04549616b973704c2 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Sun, 19 Jul 2026 17:15:02 -0500 Subject: [PATCH 249/791] ASoC: codecs: ES8389: Remove redundant comparison The comparison (target_hz < 0) will always be false because the variable 'target_hz' is of the u32 type. Remove redundant comparison and simply code around it. Fixes: 87592da1a490a ("ASoC: codecs: ES8389: Add private members about HPF") Signed-off-by: Ethan Tidmore Link: https://patch.msgid.link/20260719221502.536804-1-ethantidmore06@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 0c7567e2ffc2..80efce3e0a22 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -106,7 +106,7 @@ static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) u32 f, diff; int i, j; - if ((target_hz > 1020) | (target_hz < 0)) + if (target_hz > 1020) return false; for (i = 0; i < 10; i++) { From b9dada6bb7eb0bdd1fd9b1f319fc524b48193f8e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jul 2026 15:53:52 +0200 Subject: [PATCH 250/791] ALSA: dummy: Use the standard PCM sync_stop callback for hrtimer The hrtimer backend code in ALSA dummy driver calls explicitly the synchronization of hrtimer cancel from prepare and free callbacks, and this is exactly what the standard PCM sync_stop callback serves for. Replace the open-code with the standard PCM sync_stop callback. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260720135356.1779857-2-tiwai@suse.de --- sound/drivers/dummy.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 7283f0f18813..ddd4da058693 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -100,6 +100,7 @@ struct dummy_timer_ops { int (*prepare)(struct snd_pcm_substream *); int (*start)(struct snd_pcm_substream *); int (*stop)(struct snd_pcm_substream *); + int (*sync_stop)(struct snd_pcm_substream *); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); }; @@ -407,9 +408,12 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) return 0; } -static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) +static int dummy_hrtimer_sync_stop(struct snd_pcm_substream *substream) { + struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; + hrtimer_cancel(&dpcm->timer); + return 0; } static snd_pcm_uframes_t @@ -435,7 +439,6 @@ static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) long sec; unsigned long nsecs; - dummy_hrtimer_sync(dpcm); period = runtime->period_size; rate = runtime->rate; sec = period / rate; @@ -463,7 +466,7 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream) static void dummy_hrtimer_free(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; - dummy_hrtimer_sync(dpcm); + kfree(dpcm); } @@ -473,6 +476,7 @@ static const struct dummy_timer_ops dummy_hrtimer_ops = { .prepare = dummy_hrtimer_prepare, .start = dummy_hrtimer_start, .stop = dummy_hrtimer_stop, + .sync_stop = dummy_hrtimer_sync_stop, .pointer = dummy_hrtimer_pointer, }; @@ -495,6 +499,13 @@ static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } +static int dummy_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + if (get_dummy_ops(substream)->sync_stop) + return get_dummy_ops(substream)->sync_stop(substream); + return 0; +} + static int dummy_pcm_prepare(struct snd_pcm_substream *substream) { return get_dummy_ops(substream)->prepare(substream); @@ -646,6 +657,7 @@ static const struct snd_pcm_ops dummy_pcm_ops = { .hw_params = dummy_pcm_hw_params, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, + .sync_stop = dummy_pcm_sync_stop, .pointer = dummy_pcm_pointer, }; @@ -655,6 +667,7 @@ static const struct snd_pcm_ops dummy_pcm_ops_no_buf = { .hw_params = dummy_pcm_hw_params, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, + .sync_stop = dummy_pcm_sync_stop, .pointer = dummy_pcm_pointer, .copy = dummy_pcm_copy, .fill_silence = dummy_pcm_silence, From ac3335d7b4fde73f04fefecc280b20663706f600 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jul 2026 15:53:53 +0200 Subject: [PATCH 251/791] ALSA: dummy: Implement sync_stop for systimer, too The systimer backend invokes timer_delete() at stopping the PCM, but it misses its synchronization, which might lead to concurrent changes or releases at PCM prepare or free. Use the sync_stop callback to assure the synchronization of timer deletion. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260720135356.1779857-3-tiwai@suse.de --- sound/drivers/dummy.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index ddd4da058693..3bc0289e3518 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -286,6 +286,14 @@ static int dummy_systimer_stop(struct snd_pcm_substream *substream) return 0; } +static int dummy_systimer_sync_stop(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + + timer_delete_sync(&dpcm->timer); + return 0; +} + static int dummy_systimer_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -351,6 +359,7 @@ static const struct dummy_timer_ops dummy_systimer_ops = { .prepare = dummy_systimer_prepare, .start = dummy_systimer_start, .stop = dummy_systimer_stop, + .sync_stop = dummy_systimer_sync_stop, .pointer = dummy_systimer_pointer, }; From 260fc7a0fe660fc5b3c44541757f2399c981986f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jul 2026 15:53:54 +0200 Subject: [PATCH 252/791] ALSA: dummy: Properly shutdown the systimer at freeing Add timer_shutdown_sync() call at the free callback for systimer backend, in order to make sure that we can release the resources. This is only for hardening, and there shouldn't be any actual issue that requires this change for now. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260720135356.1779857-4-tiwai@suse.de --- sound/drivers/dummy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 3bc0289e3518..8836799727ea 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -350,7 +350,10 @@ static int dummy_systimer_create(struct snd_pcm_substream *substream) static void dummy_systimer_free(struct snd_pcm_substream *substream) { - kfree(substream->runtime->private_data); + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + + timer_shutdown_sync(&dpcm->timer); + kfree(dpcm); } static const struct dummy_timer_ops dummy_systimer_ops = { From df27e4dc80c4a30ad206432cb848aab00de22ca7 Mon Sep 17 00:00:00 2001 From: Bob Song Date: Fri, 17 Jul 2026 10:49:48 +0800 Subject: [PATCH 253/791] ALSA: hda: Check snd_hda_power_pm construct error before executing verb When userspace writes 1 to /sys/bus/pci/devices/XX/remove to remove HDA PCI device, the HDA hardware control path is shut down and devres unmaps the BAR virtual address bus->remap_addr automatically during driver removal. If a delayed HDA verb command arrives after the MMIO region is unmapped, the driver will access invalid virtual addresses and trigger a page fault splat. So add an error check right after constructing snd_hda_power_pm. Signed-off-by: Bob Song Link: https://patch.msgid.link/20260717024948.506335-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/common/codec.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index b9ded149ea11..641083c2376f 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -39,6 +39,12 @@ static int call_exec_verb(struct hda_bus *bus, struct hda_codec *codec, int err; CLASS(snd_hda_power_pm, pm)(codec); + if (pm.err < 0 && !bus->core.chip_init) { + codec_warn(codec, + "Failed to send cmd 0x%x ret=[%d], hda control stopped\n", + cmd, pm.err); + return pm.err; + } guard(mutex)(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; From 0f23ed0e117cbdb735be2d77b225cb92d0c6dff1 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Mon, 13 Jul 2026 18:17:11 +0530 Subject: [PATCH 254/791] ASoC: dt-bindings: ti,omap4-dmic: Convert to DT schema Convert the TI OMAP4+ dmic bindings from txt to DT schema. Following changes are introduced during converson: - Drop ti,hwmods property as it is not needed since the sysc conversion no existing DTS uses it. - Add dma, dma-names, reg-names properties to match the DTS. - Update example node to match existing DTS. Signed-off-by: Bhargav Joshi Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260713-ti-omap4-dmic-v1-1-3cc6e13decec@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/omap-dmic.txt | 20 ------- .../bindings/sound/ti,omap4-dmic.yaml | 58 +++++++++++++++++++ 2 files changed, 58 insertions(+), 20 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/omap-dmic.txt create mode 100644 Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml diff --git a/Documentation/devicetree/bindings/sound/omap-dmic.txt b/Documentation/devicetree/bindings/sound/omap-dmic.txt deleted file mode 100644 index 418e30e72e89..000000000000 --- a/Documentation/devicetree/bindings/sound/omap-dmic.txt +++ /dev/null @@ -1,20 +0,0 @@ -* Texas Instruments OMAP4+ Digital Microphone Module - -Required properties: -- compatible: "ti,omap4-dmic" -- reg: Register location and size as an array: - , - ; -- interrupts: Interrupt number for DMIC -- ti,hwmods: Name of the hwmod associated with OMAP dmic IP - -Example: - -dmic: dmic@4012e000 { - compatible = "ti,omap4-dmic"; - reg = <0x4012e000 0x7f>, /* MPU private access */ - <0x4902e000 0x7f>; /* L3 Interconnect */ - interrupts = <0 114 0x4>; - interrupt-parent = <&gic>; - ti,hwmods = "dmic"; -}; diff --git a/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml b/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml new file mode 100644 index 000000000000..773b9bf2f1c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,omap4-dmic.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,omap4-dmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP4+ Digital Microphone Module + +maintainers: + - Peter Ujfalusi + +properties: + compatible: + const: ti,omap4-dmic + + reg: + items: + - description: MPU access base address + - description: L3 interconnect address + + reg-names: + items: + - const: mpu + - const: dma + + interrupts: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + items: + - const: up_link + +required: + - compatible + - reg + - reg-names + - interrupts + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + #include + + dmic@0 { + compatible = "ti,omap4-dmic"; + reg = <0x0 0x7f>, /* MPU private access */ + <0x4902e000 0x7f>; /* L3 Interconnect */ + reg-names = "mpu", "dma"; + interrupts = ; + dmas = <&sdma 67>; + dma-names = "up_link"; + }; From 2462bdc608e6522413d5f0f103534c806aeb424f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:48:02 +0000 Subject: [PATCH 255/791] ASoC: amd: acp-sdw-legacy-mach: use &pdev->dev instead of card->dev acp-sdw-legacy-mach.c will be updated when Card capsuling. To makes its review easy, use &pdev->dev instead of card->dev in mc_probe(). There is no diff, because static int mc_probe(...) { ... card->dev = &pdev->dev; ... } No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87a4rli04u.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 9726a9d33ec6..3a2034bd382b 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -521,15 +521,15 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(soc_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", soc_sdw_quirk, quirk_override); soc_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = soc_sdw_quirk; - dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); + dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ for (i = 0; i < ctx->codec_info_list_count; i++) codec_info_list[i].amp_num = 0; @@ -546,12 +546,12 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s mic:acp-dmic cfg-mics:%d", card->components, 1); @@ -560,9 +560,9 @@ static int mc_probe(struct platform_device *pdev) } /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } From a429f382c0b00152c165f8563ed5136bce191123 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:48:05 +0000 Subject: [PATCH 256/791] ASoC: amd: acp-sdw-sof-mach: use &pdev->dev instead of card->dev acp-sdw-sof-mach.c will be updated when Card capsuling. To makes its review easy, use &pdev->dev instead of card->dev in mc_probe(). There is no diff, because static int mc_probe(...) { ... card->dev = &pdev->dev; ... } No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/878q75i04q.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-sof-mach.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index 963ce6fd4012..a1c5e97ad0b5 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -388,12 +388,12 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = sof_sdw_quirk; /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ @@ -412,15 +412,15 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } From a846f5f62a8b5f7e1558bfc1805c898b7e4ec5fe Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:39:46 +0000 Subject: [PATCH 257/791] ASoC: qcom: common: use dev qcom_snd_parse_of() already have dev. Let's use it. No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/87pl0hi0il.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index edc4611691f7..e4ff247ea47f 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -91,7 +91,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); + dev_err(dev, "error getting codec dai_link name\n"); return ret; } @@ -109,7 +109,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: error getting cpu dai name\n", link->name); return ret; } @@ -126,7 +126,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) "sound-dai", 0); if (!link->platforms->of_node) { - dev_err(card->dev, "%s: platform dai not found\n", link->name); + dev_err(dev, "%s: platform dai not found\n", link->name); return -EINVAL; } } else { @@ -136,7 +136,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: codec dai not found\n", link->name); return ret; } From af6111f7aa9a2433c6ccd1c1710345e2c81f8564 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:40:01 +0000 Subject: [PATCH 258/791] ASoC: qcom: sdm845: remove unused card It is not used. Let's remove. Signed-off-by: Kuninori Morimoto Reviewed-by: Srinivas Kandagatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/87o6g1i0i6.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 0ce9dff4dc52..6843ab8ba017 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -36,7 +36,6 @@ struct sdm845_snd_data { bool jack_setup; bool slim_port_setup; bool stream_prepared[AFE_PORT_MAX]; - struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; uint32_t sec_mi2s_clk_count; uint32_t quat_tdm_clk_count; @@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) if (ret) return ret; - data->card = card; snd_soc_card_set_drvdata(card, data); sdm845_add_ops(card); From 3c39af873af037149cdd8c57f9cc98b802fd27ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:40:10 +0000 Subject: [PATCH 259/791] ASoC: qcom: storm: use dev instead of card on storm_parse_of() storm_parse_of() can be processed without using *card. No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/87mrvli0hx.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/storm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index 1e0eda8c24c4..6b42fbfd27ca 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { SND_SOC_DAILINK_REG(hifi), }; -static int storm_parse_of(struct snd_soc_card *card) +static int storm_parse_of(struct device *dev) { - struct snd_soc_dai_link *dai_link = card->dai_link; - struct device_node *np = card->dev->of_node; + struct snd_soc_dai_link *dai_link = &storm_dai_link; + struct device_node *np = dev->of_node; dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); if (!dai_link->cpus->of_node) { - dev_err(card->dev, "error getting cpu phandle\n"); + dev_err(dev, "error getting cpu phandle\n"); return -EINVAL; } dai_link->platforms->of_node = dai_link->cpus->of_node; dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); if (!dai_link->codecs->of_node) { - dev_err(card->dev, "error getting codec phandle\n"); + dev_err(dev, "error getting codec phandle\n"); return -EINVAL; } @@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) card->dai_link = &storm_dai_link; card->num_links = 1; - ret = storm_parse_of(card); + ret = storm_parse_of(&pdev->dev); if (ret) { dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); return ret; From 7fa44519a2a3a9da715669a7f359f08e49911e2f Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:03 +0200 Subject: [PATCH 260/791] ASoC: aw88399: extract shared device library Extract the device-level functions from aw88399.c into a new shared library module (aw88399-lib.c) with a shared header at include/sound/aw88399.h, following the pattern established by CS35L41 (cs35l41-lib.c / include/sound/cs35l41.h) for chips that need both ASoC and HDA drivers. The shared header at include/sound/aw88399.h contains the register definitions, bit-field masks, hardware constants, device enums, the struct aw88399 definition, and the library function declarations. The ASoC-private header at sound/soc/codecs/aw88399.h is reduced to ASoC-specific definitions (PCM formats/rates, ALSA kcontrol helpers, calibration constants) and includes the shared header. The library contains the chip initialization, firmware loading, playback start/stop sequences, and all their internal dependencies (PLL checks, DSP management, volume control, calibration, CRC verification, etc.). The ASoC codec driver retains the ALSA controls, DAPM widgets, codec probe/remove, calibration service, and I2C bus driver registration. A new Kconfig symbol SND_SOC_AW88399_LIB is introduced. SND_SOC_AW88399 (the existing ASoC codec) selects it, ensuring no change for current users. The HDA side codec driver (introduced later in this series) selects the library without pulling in the full ASoC codec module. This avoids a build-time dependency on the full ASoC driver and follows the established pattern used by CS35L41 (SND_SOC_CS35L41_LIB) for chips with both ASoC and HDA drivers. Some library functions (DSP control, volume setting, mute, calibration updates, profile management, and status helpers) are used internally by the library's start/stop sequences but are also called directly by the ASoC driver's remaining code. These are exported from the library so the ASoC module can access them. This is a pure code movement with no functional changes. The moved functions are identical to their originals in aw88399.c. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB772415C485FAF74297673FD7FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 618 ++++++++++++++ sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw88399-lib.c | 1381 ++++++++++++++++++++++++++++++++ sound/soc/codecs/aw88399.c | 1349 ------------------------------- sound/soc/codecs/aw88399.h | 582 +------------- 6 files changed, 2008 insertions(+), 1929 deletions(-) create mode 100644 include/sound/aw88399.h create mode 100644 sound/soc/codecs/aw88399-lib.c diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h new file mode 100644 index 000000000000..3a2153f0ee92 --- /dev/null +++ b/include/sound/aw88399.h @@ -0,0 +1,618 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * linux/sound/aw88399.h -- Platform data for AW88399 + * + * Copyright (c) 2023 AWINIC Technology CO., LTD + * + * Author: Weidong Wang + */ + +#ifndef __SOUND_AW88399_H +#define __SOUND_AW88399_H + +#include +#include + +/* registers list */ +#define AW88399_ID_REG (0x00) +#define AW88399_SYSST_REG (0x01) +#define AW88399_SYSINT_REG (0x02) +#define AW88399_SYSINTM_REG (0x03) +#define AW88399_SYSCTRL_REG (0x04) +#define AW88399_SYSCTRL2_REG (0x05) +#define AW88399_I2SCTRL1_REG (0x06) +#define AW88399_I2SCTRL2_REG (0x07) +#define AW88399_I2SCTRL3_REG (0x08) +#define AW88399_DACCFG1_REG (0x09) +#define AW88399_DACCFG2_REG (0x0A) +#define AW88399_DACCFG3_REG (0x0B) +#define AW88399_DACCFG4_REG (0x0C) +#define AW88399_DACCFG5_REG (0x0D) +#define AW88399_DACCFG6_REG (0x0E) +#define AW88399_DACCFG7_REG (0x0F) +#define AW88399_MPDCFG1_REG (0x10) +#define AW88399_MPDCFG2_REG (0x11) +#define AW88399_MPDCFG3_REG (0x12) +#define AW88399_MPDCFG4_REG (0x13) +#define AW88399_PWMCTRL1_REG (0x14) +#define AW88399_PWMCTRL2_REG (0x15) +#define AW88399_PWMCTRL3_REG (0x16) +#define AW88399_I2SCFG1_REG (0x17) +#define AW88399_DBGCTRL_REG (0x18) +#define AW88399_HAGCST_REG (0x20) +#define AW88399_VBAT_REG (0x21) +#define AW88399_TEMP_REG (0x22) +#define AW88399_PVDD_REG (0x23) +#define AW88399_ISNDAT_REG (0x24) +#define AW88399_VSNDAT_REG (0x25) +#define AW88399_I2SINT_REG (0x26) +#define AW88399_I2SCAPCNT_REG (0x27) +#define AW88399_ANASTA1_REG (0x28) +#define AW88399_ANASTA2_REG (0x29) +#define AW88399_ANASTA3_REG (0x2A) +#define AW88399_TESTDET_REG (0x2B) +#define AW88399_DSMCFG1_REG (0x30) +#define AW88399_DSMCFG2_REG (0x31) +#define AW88399_DSMCFG3_REG (0x32) +#define AW88399_DSMCFG4_REG (0x33) +#define AW88399_DSMCFG5_REG (0x34) +#define AW88399_DSMCFG6_REG (0x35) +#define AW88399_DSMCFG7_REG (0x36) +#define AW88399_DSMCFG8_REG (0x37) +#define AW88399_TESTIN_REG (0x38) +#define AW88399_TESTOUT_REG (0x39) +#define AW88399_MEMTEST_REG (0x3A) +#define AW88399_VSNCTRL1_REG (0x3B) +#define AW88399_ISNCTRL1_REG (0x3C) +#define AW88399_ISNCTRL2_REG (0x3D) +#define AW88399_DSPMADD_REG (0x40) +#define AW88399_DSPMDAT_REG (0x41) +#define AW88399_WDT_REG (0x42) +#define AW88399_ACR1_REG (0x43) +#define AW88399_ACR2_REG (0x44) +#define AW88399_ASR1_REG (0x45) +#define AW88399_ASR2_REG (0x46) +#define AW88399_DSPCFG_REG (0x47) +#define AW88399_ASR3_REG (0x48) +#define AW88399_ASR4_REG (0x49) +#define AW88399_DSPVCALB_REG (0x4A) +#define AW88399_CRCCTRL_REG (0x4B) +#define AW88399_DSPDBG1_REG (0x4C) +#define AW88399_DSPDBG2_REG (0x4D) +#define AW88399_DSPDBG3_REG (0x4E) +#define AW88399_PLLCTRL1_REG (0x50) +#define AW88399_PLLCTRL2_REG (0x51) +#define AW88399_PLLCTRL3_REG (0x52) +#define AW88399_CDACTRL1_REG (0x53) +#define AW88399_CDACTRL2_REG (0x54) +#define AW88399_CDACTRL3_REG (0x55) +#define AW88399_SADCCTRL1_REG (0x56) +#define AW88399_SADCCTRL2_REG (0x57) +#define AW88399_BOPCTRL1_REG (0x58) +#define AW88399_BOPCTRL2_REG (0x5A) +#define AW88399_BOPCTRL3_REG (0x5B) +#define AW88399_BOPCTRL4_REG (0x5C) +#define AW88399_BOPCTRL5_REG (0x5D) +#define AW88399_BOPCTRL6_REG (0x5E) +#define AW88399_BOPCTRL7_REG (0x5F) +#define AW88399_BSTCTRL1_REG (0x60) +#define AW88399_BSTCTRL2_REG (0x61) +#define AW88399_BSTCTRL3_REG (0x62) +#define AW88399_BSTCTRL4_REG (0x63) +#define AW88399_BSTCTRL5_REG (0x64) +#define AW88399_BSTCTRL6_REG (0x65) +#define AW88399_BSTCTRL7_REG (0x66) +#define AW88399_BSTCTRL8_REG (0x67) +#define AW88399_BSTCTRL9_REG (0x68) +#define AW88399_BSTCTRL10_REG (0x69) +#define AW88399_CPCTRL_REG (0x6A) +#define AW88399_EFWH_REG (0x6C) +#define AW88399_EFWM2_REG (0x6D) +#define AW88399_EFWM1_REG (0x6E) +#define AW88399_EFWL_REG (0x6F) +#define AW88399_TESTCTRL1_REG (0x70) +#define AW88399_TESTCTRL2_REG (0x71) +#define AW88399_EFCTRL1_REG (0x72) +#define AW88399_EFCTRL2_REG (0x73) +#define AW88399_EFRH4_REG (0x74) +#define AW88399_EFRH3_REG (0x75) +#define AW88399_EFRH2_REG (0x76) +#define AW88399_EFRH1_REG (0x77) +#define AW88399_EFRL4_REG (0x78) +#define AW88399_EFRL3_REG (0x79) +#define AW88399_EFRL2_REG (0x7A) +#define AW88399_EFRL1_REG (0x7B) +#define AW88399_TM_REG (0x7C) +#define AW88399_TM2_REG (0x7D) + +#define AW88399_REG_MAX (0x7E) +#define AW88399_MUTE_VOL (1023) + +#define AW88399_DSP_CFG_ADDR (0x9B00) +#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) +#define AW88399_DSP_FW_ADDR (0x8980) +#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) +#define AW88399_DSP_ROM_CHECK_DATA (0x4638) + +#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) +#define AW88399_CALI_RE_HBITS_SHIFT (16) + +#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) +#define AW88399_CALI_RE_LBITS_SHIFT (0) + +#define AW88399_I2STXEN_START_BIT (9) +#define AW88399_I2STXEN_BITS_LEN (1) +#define AW88399_I2STXEN_MASK \ + (~(((1<> (shift)) +#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) +#define AW88399_CRC_CHECK_PASS_VAL (0x4) + +#define AW88399_CRC_CFG_BASE_ADDR (0xD80) +#define AW88399_CRC_FW_BASE_ADDR (0x4C0) +#define AW88399_ACF_FILE "aw88399_acf.bin" +#define AW88399_DEV_SYSST_CHECK_MAX (10) +#define AW88399_CHIP_ID 0x2183 + +#define AW88399_START_RETRIES (5) +#define AW88399_START_WORK_DELAY_MS (0) + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + +enum { + AW88399_DEV_VDSEL_DAC = 0, + AW88399_DEV_VDSEL_VSENSE = 32, +}; + +enum { + AW88399_DSP_CRC_NA = 0, + AW88399_DSP_CRC_OK = 1, +}; + +enum { + AW88399_DSP_FW_UPDATE_OFF = 0, + AW88399_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88399_FORCE_UPDATE_OFF = 0, + AW88399_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88399_1000_US = 1000, + AW88399_2000_US = 2000, + AW88399_3000_US = 3000, + AW88399_4000_US = 4000, +}; + +enum AW88399_DEV_STATUS { + AW88399_DEV_PW_OFF = 0, + AW88399_DEV_PW_ON, +}; + +enum AW88399_DEV_FW_STATUS { + AW88399_DEV_FW_FAILED = 0, + AW88399_DEV_FW_OK, +}; + +enum AW88399_DEV_MEMCLK { + AW88399_DEV_MEMCLK_OSC = 0, + AW88399_DEV_MEMCLK_PLL = 1, +}; + +enum AW88399_DEV_DSP_CFG { + AW88399_DEV_DSP_WORK = 0, + AW88399_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88399_NOT_RCV_MODE = 0, + AW88399_RCV_MODE = 1, +}; + +enum { + AW88399_SYNC_START = 0, + AW88399_ASYNC_START, +}; + +struct aw_device; +struct aw_container; +struct aw_cali_desc; +struct gpio_desc; +struct i2c_client; +struct regmap; + +struct aw88399 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + unsigned int check_val; + unsigned int crc_init_val; + unsigned int vcalb_init_val; + unsigned int dither_st; +}; + +int aw_dev_check_syspll(struct aw_device *aw_dev); +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable); +int aw_dev_get_dsp_status(struct aw_device *aw_dev); +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value); +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc); +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute); +void aw88399_hw_reset(struct aw88399 *aw88399); +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap); +extern const struct regmap_config aw88399_remap_config; +int aw88399_request_firmware_file(struct aw88399 *aw88399); +void aw88399_start(struct aw88399 *aw88399, bool sync_start); +void aw88399_startup_work(struct work_struct *work); +int aw88399_stop(struct aw_device *aw_dev); + +#endif /* __SOUND_AW88399_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..18e34899566e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -726,6 +726,10 @@ config SND_SOC_AW87390 sound quality, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. +config SND_SOC_AW88399_LIB + tristate + select SND_SOC_AW88395_LIB + config SND_SOC_AW88399 tristate "Soc Audio for awinic aw88399" depends on I2C @@ -733,6 +737,7 @@ config SND_SOC_AW88399 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB + select SND_SOC_AW88399_LIB help This option enables support for aw88399 Smart PA. The awinic AW88399 is an I2S/TDM input, high efficiency diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aa0396e5b575..d2a689006d69 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,6 +55,7 @@ snd-soc-aw88395-y := aw88395/aw88395.o snd-soc-aw88166-y := aw88166.o snd-soc-aw88261-y := aw88261.o snd-soc-aw88399-y := aw88399.o +snd-soc-aw88399-lib-y := aw88399-lib.o snd-soc-bd28623-y := bd28623.o snd-soc-bt-sco-y := bt-sco.o snd-soc-chv3-codec-y := chv3-codec.o @@ -496,6 +497,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o +obj-$(CONFIG_SND_SOC_AW88399_LIB) += snd-soc-aw88399-lib.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c new file mode 100644 index 000000000000..258d6efbf590 --- /dev/null +++ b/sound/soc/codecs/aw88399-lib.c @@ -0,0 +1,1381 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88399-lib.c -- AW88399 Common functions for ASoC and HDA audio drivers +// +// Copyright (c) 2023 AWINIC Technology CO., LTD +// +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include +#include +#include "aw88395/aw88395_device.h" + +const struct regmap_config aw88399_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88399_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; +EXPORT_SYMBOL_GPL(aw88399_remap_config); + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88399_CCO_MUX_MASK); + if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_check_syspll); + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); + if (ret) + return ret; + + if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) + check_val = AW88399_BIT_SYSST_NOSWS_CHECK; + else + check_val = AW88399_BIT_SYSST_SWS_CHECK; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + + if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed\n", __func__); +} +EXPORT_SYMBOL_GPL(aw_dev_dsp_enable); + +static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) +{ + uint16_t icalkh_val, icalkl_val, icalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); + if (ret) + return ret; + icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); + if (ret) + return ret; + icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + icalk_val = icalkh_val & icalkl_val; + else + icalk_val = icalkh_val | icalkl_val; + + if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) + icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; + *icalk = (int16_t)icalk_val; + + return 0; +} + +static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); + if (ret) + return ret; + + vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); + if (ret) + return ret; + + vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = vcalkh_val & vcalkl_val; + else + vcalk_val = vcalkh_val | vcalkl_val; + + if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) + vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); + if (ret) + return ret; + vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); + if (ret) + return ret; + vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + else + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + + if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) + vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; + + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw_dev_set_vcalb(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int vsense_select, vsense_value; + int32_t ical_k, vcal_k, vcalb; + int16_t icalk, vcalk; + uint16_t reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); + if (ret) + return ret; + + vsense_select = vsense_value & (~AW88399_VDSEL_MASK); + + ret = aw88399_dev_get_icalk(aw88399, &icalk); + if (ret) { + dev_err(aw_dev->dev, "get icalk failed\n"); + return ret; + } + + ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; + + switch (vsense_select) { + case AW88399_DEV_VDSEL_VSENSE: + ret = aw88399_dev_get_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * + ical_k / vcal_k * aw88399->vcalb_init_val; + break; + case AW88399_DEV_VDSEL_DAC: + ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / + AW88399_ISCAL_DAC_FACTOR * ical_k / + vcal_k * aw88399->vcalb_init_val; + break; + default: + dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); + ret = -EINVAL; + break; + } + if (ret) + return ret; + + vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; + reg_val = (uint32_t)vcalb; + + regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); + + return 0; +} + +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + uint16_t re_lbits, re_hbits; + u32 cali_re; + int ret; + + if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || + (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) + return -EINVAL; + + cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); + + re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; + re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; + + ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); + if (ret) + dev_err(aw_dev->dev, "set cali re error"); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_update_cali_re); + +static int aw_dev_fw_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, fw_len_val; + unsigned int reg_val; + int ret; + + /* calculate fw_end_addr */ + fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; + + /* write fw_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, fw_len_val); + if (ret) + return ret; + /* enable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); + + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + + /* read crc check result */ + regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", + __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, cfg_len_val; + unsigned int reg_val; + int ret; + + /* calculate cfg end addr */ + cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; + + /* write cfg_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); + if (ret) + return ret; + + /* enable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); + if (ret) + return ret; + + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + /* read crc check result */ + ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", + check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_hw_crc_check(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); + if (ret) + return ret; + + ret = aw_dev_fw_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "fw_crc_check failed\n"); + goto crc_check_failed; + } + + ret = aw_dev_cfg_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "cfg_crc_check failed\n"); + goto crc_check_failed; + } + + ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); + if (ret) + return ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + + return ret; + +crc_check_failed: + regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88399_WDT_CNT_MASK))) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_get_dsp_status); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88399_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88399_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value; + int ret; + + real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); + + ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88399_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } +} +EXPORT_SYMBOL_GPL(aw88399_dev_mute); + +static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + + if (dither) + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); +} + +static int aw88399_dev_start(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + if (aw_dev->status == AW88399_DEV_PW_ON) { + dev_dbg(aw_dev->dev, "already power on"); + return 0; + } + + aw88399_dev_set_dither(aw88399, false); + + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { + ret = aw_dev_hw_crc_check(aw88399); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + aw_dev_dsp_enable(aw_dev, false); + aw_dev_set_vcalb(aw88399); + aw_dev_update_cali_re(&aw_dev->cali_desc); + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) + aw88399_dev_set_dither(aw88399, true); + + /* close mute */ + aw88399_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88399_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88399_DEV_PW_OFF; + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + u32 tmp_len; + int i, ret; + + ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); + if (ret) + return ret; + + for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { + tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); + ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88399_DSP_RE_SHIFT); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); + if (ret) + return ret; + + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_get_ra(&aw_dev->cali_desc); + + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + + aw_dev->dsp_fw_len = len; + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); + + return ret; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + /* read dsp_rom_check_reg */ + aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { + dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", + reg_val, AW88399_DSP_ROM_CHECK_DATA); + return -EPERM; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, + AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); + aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); + return -EPERM; + } + + return 0; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88399_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_DAPHCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88399_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_OSCCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88399_RCV_MODE; + else + profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; +} + +static int aw_dev_update_reg_container(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + u16 read_vol, reg_val; + int data_len, i, ret; + int16_t *reg_data; + u8 reg_addr; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88399_DSPVCALB_REG) { + aw88399->vcalb_init_val = reg_val; + continue; + } + + if (reg_addr == AW88399_SYSCTRL_REG) { + if (reg_val & (~AW88399_DSPBY_MASK)) + aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; + else + aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; + + reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | + AW88399_DSPBY_MASK); + reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | + AW88399_DSPBY_BYPASS_VALUE); + } + + if (reg_addr == AW88399_I2SCTRL3_REG) { + reg_val &= AW88399_I2STXEN_MASK; + reg_val |= AW88399_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88399_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88399_VOL_MASK)) >> + AW88399_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + if (reg_addr == AW88399_DBGCTRL_REG) { + if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) + aw88399->check_val = AW_EF_OR_CHECK; + else + aw88399->check_val = AW_EF_AND_CHECK; + + aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); + } + + if (reg_addr == AW88399_CRCCTRL_REG) + aw88399->crc_init_val = reg_val; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + return ret; + } + + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + else + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + + return 0; +} + +static int aw_dev_reg_update(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw88399, data, len); + if (ret) + dev_err(aw88399->aw_pa->dev, "reg update failed"); + + return ret; +} + +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_dev_get_prof_name); + +static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88399_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw88399, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw88399_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + if (up_dsp_fw_en) { + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + return ret; +} + +static void aw88399_start_pa(struct aw88399 *aw88399) +{ + int ret, i; + + for (i = 0; i < AW88399_START_RETRIES; i++) { + ret = aw88399_dev_start(aw88399); + if (ret) { + dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); + ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_dbg(aw88399->aw_pa->dev, "start success\n"); + break; + } + } +} + +void aw88399_startup_work(struct work_struct *work) +{ + struct aw88399 *aw88399 = + container_of(work, struct aw88399, start_work.work); + + mutex_lock(&aw88399->lock); + aw88399_start_pa(aw88399); + mutex_unlock(&aw88399->lock); +} +EXPORT_SYMBOL_GPL(aw88399_startup_work); + +void aw88399_start(struct aw88399 *aw88399, bool sync_start) +{ + int ret; + + if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) + return; + + if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) + return; + + ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start == AW88399_SYNC_START) + aw88399_start_pa(aw88399); + else + queue_delayed_work(system_dfl_wq, + &aw88399->start_work, + AW88399_START_WORK_DELAY_MS); +} +EXPORT_SYMBOL_GPL(aw88399_start); + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + if (reg_val & AW88399_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +int aw88399_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st; + + if (aw_dev->status == AW88399_DEV_PW_OFF) { + dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88399_DEV_PW_OFF; + + aw88399_dev_mute(aw_dev, true); + usleep_range(AW88399_4000_US, AW88399_4000_US + 100); + + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + int_st = aw_dev_check_sysint(aw_dev); + + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_amppd(aw_dev, true); + + if (int_st) { + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + } + + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_stop); + +static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88399_1000_US / 10; + aw_dev->fade_out_time = AW88399_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + aw88399_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +int aw88399_request_firmware_file(struct aw88399 *aw88399) +{ + const struct firmware *cont = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); + if (ret) { + dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", + AW88399_ACF_FILE, cont ? cont->size : 0); + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw88399->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { + dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + mutex_lock(&aw88399->lock); + /* aw device init */ + ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); + if (ret) + dev_err(aw88399->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88399->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aw88399_request_firmware_file); + +void aw88399_hw_reset(struct aw88399 *aw88399) +{ + if (aw88399->reset_gpio) { + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 0); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + } +} +EXPORT_SYMBOL_GPL(aw88399_hw_reset); + +static void aw88399_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + + of_property_read_u32(np, "awinic,audio-channel", &channel_value); + aw_dev->channel = channel_value; +} + +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88399_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + aw88399->aw_pa = aw_dev; + + aw_dev->i2c = i2c; + aw_dev->dev = &i2c->dev; + aw_dev->regmap = regmap; + mutex_init(&aw_dev->dsp_lock); + + aw_dev->chip_id = chip_id; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = AW88399_DEV_DEFAULT_CH; + aw_dev->fw_status = AW88399_DEV_FW_FAILED; + + aw_dev->fade_step = AW88399_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; + + aw88399_parse_channel_dt(aw_dev); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_init); + +MODULE_DESCRIPTION("AW88399 common device library"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index b2ec3503f7e2..13495e71ad0e 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -10,1217 +10,12 @@ #include #include #include -#include -#include #include #include #include #include "aw88399.h" #include "aw88395/aw88395_device.h" -static const struct regmap_config aw88399_remap_config = { - .val_bits = 16, - .reg_bits = 8, - .max_register = AW88399_REG_MAX, - .reg_format_endian = REGMAP_ENDIAN_LITTLE, - .val_format_endian = REGMAP_ENDIAN_BIG, -}; - -static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) -{ - int ret; - - if (pwd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); - if (ret) - dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); - else - *int_status = reg_val; - - dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); -} - -static void aw_dev_clear_int_status(struct aw_device *aw_dev) -{ - u16 int_status; - - /* read int status and clear */ - aw_dev_get_int_status(aw_dev, &int_status); - /* make sure int status is clear */ - aw_dev_get_int_status(aw_dev, &int_status); - if (int_status) - dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); -} - -static int aw_dev_get_iis_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { - dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) -{ - int ret, i; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode1 iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); - if (ret) - return ret; - - reg_val &= (~AW88399_CCO_MUX_MASK); - if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { - dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); - return -EPERM; - } - - /* change mode2 */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); - if (ret) - return ret; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - - /* change mode1 */ - regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); - if (ret == 0) { - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - } - - return ret; -} - -static int aw_dev_check_syspll(struct aw_device *aw_dev) -{ - int ret; - - ret = aw_dev_check_mode1_pll(aw_dev); - if (ret) { - dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); - ret = aw_dev_check_mode2_pll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 check iis failed"); - return ret; - } - } - - return 0; -} - -static int aw_dev_check_sysst(struct aw_device *aw_dev) -{ - unsigned int check_val; - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); - if (ret) - return ret; - - if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) - check_val = AW88399_BIT_SYSST_NOSWS_CHECK; - else - check_val = AW88399_BIT_SYSST_SWS_CHECK; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - - if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { - dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", - i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) -{ - int ret; - - if (amppd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) -{ - int ret; - - if (is_enable) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed\n", __func__); -} - -static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) -{ - uint16_t icalkh_val, icalkl_val, icalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); - if (ret) - return ret; - icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); - if (ret) - return ret; - icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - icalk_val = icalkh_val & icalkl_val; - else - icalk_val = icalkh_val | icalkl_val; - - if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) - icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; - *icalk = (int16_t)icalk_val; - - return 0; -} - -static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); - if (ret) - return ret; - - vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); - if (ret) - return ret; - - vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = vcalkh_val & vcalkl_val; - else - vcalk_val = vcalkh_val | vcalkl_val; - - if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) - vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); - if (ret) - return ret; - vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); - if (ret) - return ret; - vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - else - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - - if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) - vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; - - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw_dev_set_vcalb(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int vsense_select, vsense_value; - int32_t ical_k, vcal_k, vcalb; - int16_t icalk, vcalk; - uint16_t reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); - if (ret) - return ret; - - vsense_select = vsense_value & (~AW88399_VDSEL_MASK); - - ret = aw88399_dev_get_icalk(aw88399, &icalk); - if (ret) { - dev_err(aw_dev->dev, "get icalk failed\n"); - return ret; - } - - ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; - - switch (vsense_select) { - case AW88399_DEV_VDSEL_VSENSE: - ret = aw88399_dev_get_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * - ical_k / vcal_k * aw88399->vcalb_init_val; - break; - case AW88399_DEV_VDSEL_DAC: - ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / - AW88399_ISCAL_DAC_FACTOR * ical_k / - vcal_k * aw88399->vcalb_init_val; - break; - default: - dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); - ret = -EINVAL; - break; - } - if (ret) - return ret; - - vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; - reg_val = (uint32_t)vcalb; - - regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); - - return 0; -} - -static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - uint16_t re_lbits, re_hbits; - u32 cali_re; - int ret; - - if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || - (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) - return -EINVAL; - - cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + - aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); - - re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; - re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; - - ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); - if (ret) { - dev_err(aw_dev->dev, "set cali re error"); - return ret; - } - - ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); - if (ret) - dev_err(aw_dev->dev, "set cali re error"); - - return ret; -} - -static int aw_dev_fw_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, fw_len_val; - unsigned int reg_val; - int ret; - - /* calculate fw_end_addr */ - fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; - - /* write fw_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, fw_len_val); - if (ret) - return ret; - /* enable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); - - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - - /* read crc check result */ - regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", - __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, cfg_len_val; - unsigned int reg_val; - int ret; - - /* calculate cfg end addr */ - cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; - - /* write cfg_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); - if (ret) - return ret; - - /* enable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); - if (ret) - return ret; - - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - /* read crc check result */ - ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", - check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_hw_crc_check(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); - if (ret) - return ret; - - ret = aw_dev_fw_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "fw_crc_check failed\n"); - goto crc_check_failed; - } - - ret = aw_dev_cfg_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "cfg_crc_check failed\n"); - goto crc_check_failed; - } - - ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); - if (ret) - return ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - - return ret; - -crc_check_failed: - regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - return ret; -} - -static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) -{ - int ret; - - if (flag) - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static int aw_dev_get_dsp_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); - if (ret) - return ret; - if (!(reg_val & (~AW88399_WDT_CNT_MASK))) - return -EPERM; - - return 0; -} - -static int aw_dev_dsp_check(struct aw_device *aw_dev) -{ - int ret, i; - - switch (aw_dev->dsp_cfg) { - case AW88399_DEV_DSP_BYPASS: - dev_dbg(aw_dev->dev, "dsp bypass"); - ret = 0; - break; - case AW88399_DEV_DSP_WORK: - aw_dev_dsp_enable(aw_dev, false); - aw_dev_dsp_enable(aw_dev, true); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { - ret = aw_dev_get_dsp_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } - } - break; - default: - dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); - ret = -EINVAL; - break; - } - - return ret; -} - -static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) -{ - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - unsigned int reg_value; - u16 real_value; - int ret; - - real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); - - real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); - - ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); - - return ret; -} - -static void aw_dev_fade_in(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - u16 fade_in_vol = desc->ctl_volume; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_in_time == 0) { - aw_dev_set_volume(aw_dev, fade_in_vol); - return; - } - - for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); - } - - if (i != fade_in_vol) - aw_dev_set_volume(aw_dev, fade_in_vol); -} - -static void aw_dev_fade_out(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_out_time == 0) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - return; - } - - for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } - - if (i != AW88399_MUTE_VOL) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } -} - -static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) -{ - if (is_mute) { - aw_dev_fade_out(aw_dev); - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); - } else { - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); - aw_dev_fade_in(aw_dev); - } -} - -static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - - if (dither) - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); - else - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); -} - -static int aw88399_dev_start(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - if (aw_dev->status == AW88399_DEV_PW_ON) { - dev_dbg(aw_dev->dev, "already power on"); - return 0; - } - - aw88399_dev_set_dither(aw88399, false); - - /* power on */ - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - - ret = aw_dev_check_syspll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "pll check failed cannot start"); - goto pll_check_fail; - } - - /* amppd on */ - aw_dev_amppd(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 50); - - /* check i2s status */ - ret = aw_dev_check_sysst(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "sysst check failed"); - goto sysst_check_fail; - } - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { - ret = aw_dev_hw_crc_check(aw88399); - if (ret) { - dev_err(aw_dev->dev, "dsp crc check failed"); - goto crc_check_fail; - } - aw_dev_dsp_enable(aw_dev, false); - aw_dev_set_vcalb(aw88399); - aw_dev_update_cali_re(&aw_dev->cali_desc); - - ret = aw_dev_dsp_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp status check failed"); - goto dsp_check_fail; - } - } else { - dev_dbg(aw_dev->dev, "start pa with dsp bypass"); - } - - /* enable tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, true); - - if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) - aw88399_dev_set_dither(aw88399, true); - - /* close mute */ - aw88399_dev_mute(aw_dev, false); - /* clear inturrupt */ - aw_dev_clear_int_status(aw_dev); - aw_dev->status = AW88399_DEV_PW_ON; - - return 0; - -dsp_check_fail: -crc_check_fail: - aw_dev_dsp_enable(aw_dev, false); -sysst_check_fail: - aw_dev_clear_int_status(aw_dev); - aw_dev_amppd(aw_dev, true); -pll_check_fail: - aw_dev_pwd(aw_dev, true); - aw_dev->status = AW88399_DEV_PW_OFF; - - return ret; -} - -static int aw_dev_dsp_update_container(struct aw_device *aw_dev, - unsigned char *data, unsigned int len, unsigned short base) -{ - u32 tmp_len; - int i, ret; - - ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); - if (ret) - return ret; - - for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { - tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); - ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, - &data[i], tmp_len); - if (ret) - return ret; - } - - return 0; -} - -static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - u32 dsp_ra; - int ret; - - ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, - &dsp_ra, AW_DSP_32_DATA); - if (ret) { - dev_err(aw_dev->dev, "read ra error"); - return ret; - } - - cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, - AW88399_DSP_RE_SHIFT); - - return 0; -} - -static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp config len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp config data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); - if (ret) - return ret; - - aw_dev->dsp_cfg_len = len; - - ret = aw_dev_get_ra(&aw_dev->cali_desc); - - return ret; -} - -static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); - return -EINVAL; - } - - aw_dev->dsp_fw_len = len; - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); - - return ret; -} - -static int aw_dev_check_sram(struct aw_device *aw_dev) -{ - unsigned int reg_val; - - /* read dsp_rom_check_reg */ - aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { - dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", - reg_val, AW88399_DSP_ROM_CHECK_DATA); - return -EPERM; - } - - /* check dsp_cfg_base_addr */ - aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, - AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); - aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", - reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); - return -EPERM; - } - - return 0; -} - -static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) -{ - int ret; - - switch (flag) { - case AW88399_DEV_MEMCLK_PLL: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_DAPHCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select pll failed"); - break; - case AW88399_DEV_MEMCLK_OSC: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_OSCCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select OSC failed"); - break; - default: - dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); - break; - } -} - -static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) -{ - struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); - if (ret) { - dev_dbg(aw_dev->dev, "%s failed", __func__); - return; - } - if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) - profctrl_desc->cur_mode = AW88399_RCV_MODE; - else - profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; -} - -static int aw_dev_update_reg_container(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - u16 read_vol, reg_val; - int data_len, i, ret; - int16_t *reg_data; - u8 reg_addr; - - reg_data = (int16_t *)data; - data_len = len >> 1; - - if (data_len & 0x1) { - dev_err(aw_dev->dev, "data len:%d unsupported", data_len); - return -EINVAL; - } - - for (i = 0; i < data_len; i += 2) { - reg_addr = reg_data[i]; - reg_val = reg_data[i + 1]; - - if (reg_addr == AW88399_DSPVCALB_REG) { - aw88399->vcalb_init_val = reg_val; - continue; - } - - if (reg_addr == AW88399_SYSCTRL_REG) { - if (reg_val & (~AW88399_DSPBY_MASK)) - aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; - else - aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; - - reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | - AW88399_DSPBY_MASK); - reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | - AW88399_DSPBY_BYPASS_VALUE); - } - - if (reg_addr == AW88399_I2SCTRL3_REG) { - reg_val &= AW88399_I2STXEN_MASK; - reg_val |= AW88399_I2STXEN_DISABLE_VALUE; - } - - if (reg_addr == AW88399_SYSCTRL2_REG) { - read_vol = (reg_val & (~AW88399_VOL_MASK)) >> - AW88399_VOL_START_BIT; - aw_dev->volume_desc.init_volume = read_vol; - } - - if (reg_addr == AW88399_DBGCTRL_REG) { - if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) - aw88399->check_val = AW_EF_OR_CHECK; - else - aw88399->check_val = AW_EF_AND_CHECK; - - aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); - } - - if (reg_addr == AW88399_CRCCTRL_REG) - aw88399->crc_init_val = reg_val; - - ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); - if (ret) - return ret; - } - - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - aw_dev_get_cur_mode_st(aw_dev); - - if (aw_dev->prof_cur != aw_dev->prof_index) - vol_desc->ctl_volume = 0; - else - aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); - - return 0; -} - -static int aw_dev_reg_update(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - int ret; - - if (!len || !data) { - dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_update_reg_container(aw88399, data, len); - if (ret) - dev_err(aw88399->aw_pa->dev, "reg update failed"); - - return ret; -} - -static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) -{ - struct aw_prof_info *prof_info = &aw_dev->prof_info; - struct aw_prof_desc *prof_desc; - - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "index[%d] overflow count[%d]", - index, aw_dev->prof_info.count); - return -EINVAL; - } - - prof_desc = &aw_dev->prof_info.prof_desc[index]; - - *prof_name = prof_info->prof_name_list[prof_desc->id]; - - return 0; -} - -static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, - struct aw_prof_desc **prof_desc) -{ - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", - __func__, index, aw_dev->prof_info.count); - return -EINVAL; - } - - *prof_desc = &aw_dev->prof_info.prof_desc[index]; - - return 0; -} - -static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_prof_desc *prof_index_desc; - struct aw_sec_data_desc *sec_desc; - char *prof_name; - int ret; - - if ((aw_dev->prof_cur == aw_dev->prof_index) && - (force_up_en == AW88399_FORCE_UPDATE_OFF)) { - dev_dbg(aw_dev->dev, "scene no change, not update"); - return 0; - } - - if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { - dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); - return -EPERM; - } - - ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "start update %s", prof_name); - - ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); - if (ret) - return ret; - - /* update reg */ - sec_desc = prof_index_desc->sec_desc; - ret = aw_dev_reg_update(aw88399, sec_desc[AW88395_DATA_TYPE_REG].data, - sec_desc[AW88395_DATA_TYPE_REG].len); - if (ret) { - dev_err(aw_dev->dev, "update reg failed"); - return ret; - } - - aw88399_dev_mute(aw_dev, true); - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - - ret = aw_dev_check_sram(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "check sram failed"); - goto error; - } - - if (up_dsp_fw_en) { - dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); - ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, - sec_desc[AW88395_DATA_TYPE_DSP_FW].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp fw failed"); - goto error; - } - } - - /* update dsp config */ - ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, - sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp cfg failed"); - goto error; - } - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - - aw_dev->prof_cur = aw_dev->prof_index; - - return 0; - -error: - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - return ret; -} - -static void aw88399_start_pa(struct aw88399 *aw88399) -{ - int ret, i; - - for (i = 0; i < AW88399_START_RETRIES; i++) { - ret = aw88399_dev_start(aw88399); - if (ret) { - dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed"); - continue; - } - } else { - dev_dbg(aw88399->aw_pa->dev, "start success\n"); - break; - } - } -} - -static void aw88399_startup_work(struct work_struct *work) -{ - struct aw88399 *aw88399 = - container_of(work, struct aw88399, start_work.work); - - mutex_lock(&aw88399->lock); - aw88399_start_pa(aw88399); - mutex_unlock(&aw88399->lock); -} - -static void aw88399_start(struct aw88399 *aw88399, bool sync_start) -{ - int ret; - - if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) - return; - - if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) - return; - - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed."); - return; - } - - if (sync_start == AW88399_SYNC_START) - aw88399_start_pa(aw88399); - else - queue_delayed_work(system_dfl_wq, - &aw88399->start_work, - AW88399_START_WORK_DELAY_MS); -} - -static int aw_dev_check_sysint(struct aw_device *aw_dev) -{ - u16 reg_val; - - aw_dev_get_int_status(aw_dev, ®_val); - if (reg_val & AW88399_BIT_SYSINT_CHECK) { - dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw88399_stop(struct aw_device *aw_dev) -{ - struct aw_sec_data_desc *dsp_cfg = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; - struct aw_sec_data_desc *dsp_fw = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; - int int_st; - - if (aw_dev->status == AW88399_DEV_PW_OFF) { - dev_dbg(aw_dev->dev, "already power off"); - return 0; - } - - aw_dev->status = AW88399_DEV_PW_OFF; - - aw88399_dev_mute(aw_dev, true); - usleep_range(AW88399_4000_US, AW88399_4000_US + 100); - - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - int_st = aw_dev_check_sysint(aw_dev); - - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_amppd(aw_dev, true); - - if (int_st) { - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); - aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - } - - aw_dev_pwd(aw_dev, true); - - return 0; -} - static struct snd_soc_dai_driver aw88399_dai[] = { { .name = "aw88399-aif", @@ -1869,86 +664,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, return 0; } -static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); - if (ret) { - dev_err(aw_dev->dev, "aw_dev acf parse failed"); - return -EINVAL; - } - aw_dev->fade_in_time = AW88399_1000_US / 10; - aw_dev->fade_out_time = AW88399_1000_US >> 1; - aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; - aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; - - ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); - if (ret) { - dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); - return ret; - } - - aw88399_dev_mute(aw_dev, true); - - /* close tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - /* enable amppd */ - aw_dev_amppd(aw_dev, true); - - /* close dsp */ - aw_dev_dsp_enable(aw_dev, false); - /* set power down */ - aw_dev_pwd(aw_dev, true); - - return 0; -} - -static int aw88399_request_firmware_file(struct aw88399 *aw88399) -{ - const struct firmware *cont = NULL; - int ret; - - aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; - - ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); - if (ret) { - dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", - AW88399_ACF_FILE, cont ? cont->size : 0); - - aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, - struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88399->aw_cfg) { - release_firmware(cont); - return -ENOMEM; - } - aw88399->aw_cfg->len = (int)cont->size; - memcpy(aw88399->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); - if (ret) { - dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - mutex_lock(&aw88399->lock); - /* aw device init */ - ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); - if (ret) - dev_err(aw88399->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88399->lock); - - return ret; -} - static const struct snd_kcontrol_new aw88399_controls[] = { SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG, 6, AW88399_MUTE_VOL, 0, aw88399_volume_get, @@ -2040,70 +755,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { .num_controls = ARRAY_SIZE(aw88399_controls), }; -static void aw88399_hw_reset(struct aw88399 *aw88399) -{ - if (aw88399->reset_gpio) { - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 0); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - } -} - -static void aw88399_parse_channel_dt(struct aw_device *aw_dev) -{ - struct device_node *np = aw_dev->dev->of_node; - u32 channel_value; - - of_property_read_u32(np, "awinic,audio-channel", &channel_value); - aw_dev->channel = channel_value; -} - -static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) -{ - struct aw_device *aw_dev; - unsigned int chip_id; - int ret; - - ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); - if (ret) { - dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); - return ret; - } - if (chip_id != AW88399_CHIP_ID) { - dev_err(&i2c->dev, "unsupported device"); - return -ENXIO; - } - dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); - - aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); - if (!aw_dev) - return -ENOMEM; - aw88399->aw_pa = aw_dev; - - aw_dev->i2c = i2c; - aw_dev->dev = &i2c->dev; - aw_dev->regmap = regmap; - mutex_init(&aw_dev->dsp_lock); - - aw_dev->chip_id = chip_id; - aw_dev->acf = NULL; - aw_dev->prof_info.prof_desc = NULL; - aw_dev->prof_info.count = 0; - aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; - aw_dev->channel = AW88399_DEV_DEFAULT_CH; - aw_dev->fw_status = AW88399_DEV_FW_FAILED; - - aw_dev->fade_step = AW88399_VOLUME_STEP_DB; - aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; - - aw88399_parse_channel_dt(aw_dev); - - return 0; -} - static int aw88399_i2c_probe(struct i2c_client *i2c) { struct aw88399 *aw88399; diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index b386f4836748..04123bf0ad84 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -10,512 +10,10 @@ #ifndef __AW88399_H__ #define __AW88399_H__ -/* registers list */ -#define AW88399_ID_REG (0x00) -#define AW88399_SYSST_REG (0x01) -#define AW88399_SYSINT_REG (0x02) -#define AW88399_SYSINTM_REG (0x03) -#define AW88399_SYSCTRL_REG (0x04) -#define AW88399_SYSCTRL2_REG (0x05) -#define AW88399_I2SCTRL1_REG (0x06) -#define AW88399_I2SCTRL2_REG (0x07) -#define AW88399_I2SCTRL3_REG (0x08) -#define AW88399_DACCFG1_REG (0x09) -#define AW88399_DACCFG2_REG (0x0A) -#define AW88399_DACCFG3_REG (0x0B) -#define AW88399_DACCFG4_REG (0x0C) -#define AW88399_DACCFG5_REG (0x0D) -#define AW88399_DACCFG6_REG (0x0E) -#define AW88399_DACCFG7_REG (0x0F) -#define AW88399_MPDCFG1_REG (0x10) -#define AW88399_MPDCFG2_REG (0x11) -#define AW88399_MPDCFG3_REG (0x12) -#define AW88399_MPDCFG4_REG (0x13) -#define AW88399_PWMCTRL1_REG (0x14) -#define AW88399_PWMCTRL2_REG (0x15) -#define AW88399_PWMCTRL3_REG (0x16) -#define AW88399_I2SCFG1_REG (0x17) -#define AW88399_DBGCTRL_REG (0x18) -#define AW88399_HAGCST_REG (0x20) -#define AW88399_VBAT_REG (0x21) -#define AW88399_TEMP_REG (0x22) -#define AW88399_PVDD_REG (0x23) -#define AW88399_ISNDAT_REG (0x24) -#define AW88399_VSNDAT_REG (0x25) -#define AW88399_I2SINT_REG (0x26) -#define AW88399_I2SCAPCNT_REG (0x27) -#define AW88399_ANASTA1_REG (0x28) -#define AW88399_ANASTA2_REG (0x29) -#define AW88399_ANASTA3_REG (0x2A) -#define AW88399_TESTDET_REG (0x2B) -#define AW88399_DSMCFG1_REG (0x30) -#define AW88399_DSMCFG2_REG (0x31) -#define AW88399_DSMCFG3_REG (0x32) -#define AW88399_DSMCFG4_REG (0x33) -#define AW88399_DSMCFG5_REG (0x34) -#define AW88399_DSMCFG6_REG (0x35) -#define AW88399_DSMCFG7_REG (0x36) -#define AW88399_DSMCFG8_REG (0x37) -#define AW88399_TESTIN_REG (0x38) -#define AW88399_TESTOUT_REG (0x39) -#define AW88399_MEMTEST_REG (0x3A) -#define AW88399_VSNCTRL1_REG (0x3B) -#define AW88399_ISNCTRL1_REG (0x3C) -#define AW88399_ISNCTRL2_REG (0x3D) -#define AW88399_DSPMADD_REG (0x40) -#define AW88399_DSPMDAT_REG (0x41) -#define AW88399_WDT_REG (0x42) -#define AW88399_ACR1_REG (0x43) -#define AW88399_ACR2_REG (0x44) -#define AW88399_ASR1_REG (0x45) -#define AW88399_ASR2_REG (0x46) -#define AW88399_DSPCFG_REG (0x47) -#define AW88399_ASR3_REG (0x48) -#define AW88399_ASR4_REG (0x49) -#define AW88399_DSPVCALB_REG (0x4A) -#define AW88399_CRCCTRL_REG (0x4B) -#define AW88399_DSPDBG1_REG (0x4C) -#define AW88399_DSPDBG2_REG (0x4D) -#define AW88399_DSPDBG3_REG (0x4E) -#define AW88399_PLLCTRL1_REG (0x50) -#define AW88399_PLLCTRL2_REG (0x51) -#define AW88399_PLLCTRL3_REG (0x52) -#define AW88399_CDACTRL1_REG (0x53) -#define AW88399_CDACTRL2_REG (0x54) -#define AW88399_CDACTRL3_REG (0x55) -#define AW88399_SADCCTRL1_REG (0x56) -#define AW88399_SADCCTRL2_REG (0x57) -#define AW88399_BOPCTRL1_REG (0x58) -#define AW88399_BOPCTRL2_REG (0x5A) -#define AW88399_BOPCTRL3_REG (0x5B) -#define AW88399_BOPCTRL4_REG (0x5C) -#define AW88399_BOPCTRL5_REG (0x5D) -#define AW88399_BOPCTRL6_REG (0x5E) -#define AW88399_BOPCTRL7_REG (0x5F) -#define AW88399_BSTCTRL1_REG (0x60) -#define AW88399_BSTCTRL2_REG (0x61) -#define AW88399_BSTCTRL3_REG (0x62) -#define AW88399_BSTCTRL4_REG (0x63) -#define AW88399_BSTCTRL5_REG (0x64) -#define AW88399_BSTCTRL6_REG (0x65) -#define AW88399_BSTCTRL7_REG (0x66) -#define AW88399_BSTCTRL8_REG (0x67) -#define AW88399_BSTCTRL9_REG (0x68) -#define AW88399_BSTCTRL10_REG (0x69) -#define AW88399_CPCTRL_REG (0x6A) -#define AW88399_EFWH_REG (0x6C) -#define AW88399_EFWM2_REG (0x6D) -#define AW88399_EFWM1_REG (0x6E) -#define AW88399_EFWL_REG (0x6F) -#define AW88399_TESTCTRL1_REG (0x70) -#define AW88399_TESTCTRL2_REG (0x71) -#define AW88399_EFCTRL1_REG (0x72) -#define AW88399_EFCTRL2_REG (0x73) -#define AW88399_EFRH4_REG (0x74) -#define AW88399_EFRH3_REG (0x75) -#define AW88399_EFRH2_REG (0x76) -#define AW88399_EFRH1_REG (0x77) -#define AW88399_EFRL4_REG (0x78) -#define AW88399_EFRL3_REG (0x79) -#define AW88399_EFRL2_REG (0x7A) -#define AW88399_EFRL1_REG (0x7B) -#define AW88399_TM_REG (0x7C) -#define AW88399_TM2_REG (0x7D) - -#define AW88399_REG_MAX (0x7E) -#define AW88399_MUTE_VOL (1023) - -#define AW88399_DSP_CFG_ADDR (0x9B00) -#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) -#define AW88399_DSP_FW_ADDR (0x8980) -#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) -#define AW88399_DSP_ROM_CHECK_DATA (0x4638) - -#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) -#define AW88399_CALI_RE_HBITS_SHIFT (16) - -#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) -#define AW88399_CALI_RE_LBITS_SHIFT (0) - -#define AW88399_I2STXEN_START_BIT (9) -#define AW88399_I2STXEN_BITS_LEN (1) -#define AW88399_I2STXEN_MASK \ - (~(((1<> (shift)) -#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) -#define AW88399_CRC_CHECK_PASS_VAL (0x4) - -#define AW88399_CRC_CFG_BASE_ADDR (0xD80) -#define AW88399_CRC_FW_BASE_ADDR (0x4C0) -#define AW88399_ACF_FILE "aw88399_acf.bin" -#define AW88399_DEV_SYSST_CHECK_MAX (10) -#define AW88399_CHIP_ID 0x2183 +#include #define AW88399_I2C_NAME "aw88399" -#define AW88399_START_RETRIES (5) -#define AW88399_START_WORK_DELAY_MS (0) - #define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000) #define AW88399_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -550,80 +48,4 @@ .put = profile_set, \ } -enum { - AW_EF_AND_CHECK = 0, - AW_EF_OR_CHECK, -}; - -enum { - AW88399_DEV_VDSEL_DAC = 0, - AW88399_DEV_VDSEL_VSENSE = 32, -}; - -enum { - AW88399_DSP_CRC_NA = 0, - AW88399_DSP_CRC_OK = 1, -}; - -enum { - AW88399_DSP_FW_UPDATE_OFF = 0, - AW88399_DSP_FW_UPDATE_ON = 1, -}; - -enum { - AW88399_FORCE_UPDATE_OFF = 0, - AW88399_FORCE_UPDATE_ON = 1, -}; - -enum { - AW88399_1000_US = 1000, - AW88399_2000_US = 2000, - AW88399_3000_US = 3000, - AW88399_4000_US = 4000, -}; - -enum AW88399_DEV_STATUS { - AW88399_DEV_PW_OFF = 0, - AW88399_DEV_PW_ON, -}; - -enum AW88399_DEV_FW_STATUS { - AW88399_DEV_FW_FAILED = 0, - AW88399_DEV_FW_OK, -}; - -enum AW88399_DEV_MEMCLK { - AW88399_DEV_MEMCLK_OSC = 0, - AW88399_DEV_MEMCLK_PLL = 1, -}; - -enum AW88399_DEV_DSP_CFG { - AW88399_DEV_DSP_WORK = 0, - AW88399_DEV_DSP_BYPASS = 1, -}; - -enum { - AW88399_NOT_RCV_MODE = 0, - AW88399_RCV_MODE = 1, -}; - -enum { - AW88399_SYNC_START = 0, - AW88399_ASYNC_START, -}; - -struct aw88399 { - struct aw_device *aw_pa; - struct mutex lock; - struct gpio_desc *reset_gpio; - struct delayed_work start_work; - struct regmap *regmap; - struct aw_container *aw_cfg; - - unsigned int check_val; - unsigned int crc_init_val; - unsigned int vcalb_init_val; - unsigned int dither_st; -}; - -#endif +#endif /* __AW88399_H__ */ From df5654d7306197087aef090377d196573bb4ee46 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:04 +0200 Subject: [PATCH 261/791] ASoC: aw88399: derive channel from I2C address on ACPI systems Extend aw88399_parse_channel_dt to derive the audio channel from the I2C address when the Device Tree property "awinic,audio-channel" is absent. The original code calls of_property_read_u32 without checking the return value. On ACPI systems, the DT property is never present, and channel_value is used uninitialized in the assignment to aw_dev->channel. Add a fallback that computes the channel as (i2c_addr - 0x34), where 0x34 is the AW88399's base I2C address per the datasheet (valid range 0x34-0x37). This channel assignment may be subsequently overridden by the HDA side codec's property driver on systems that require it. No change on Device Tree systems where the property is present. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Co-developed-by: Yakov Till Signed-off-by: Yakov Till Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB772468BB9F4D6925DC4E8E3EFCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88399-lib.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 258d6efbf590..b525695c96d1 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1328,8 +1328,20 @@ static void aw88399_parse_channel_dt(struct aw_device *aw_dev) { struct device_node *np = aw_dev->dev->of_node; u32 channel_value; + int ret; - of_property_read_u32(np, "awinic,audio-channel", &channel_value); + ret = of_property_read_u32(np, "awinic,audio-channel", &channel_value); + if (ret) { + /* + * On ACPI systems, DT properties don't exist. Derive channel + * from I2C address: 0x34 -> channel 0 (left), 0x35 -> channel 1 (right) + */ + aw_dev->channel = aw_dev->i2c->addr - 0x34; + dev_dbg(aw_dev->dev, + "DT channel property not found, using I2C address-based channel %d (addr 0x%02x)\n", + aw_dev->channel, aw_dev->i2c->addr); + return; + } aw_dev->channel = channel_value; } From b4530a3e4895abfc308e2b45c0ea508f4dddd9f1 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:05 +0200 Subject: [PATCH 262/791] ASoC: aw88399: add per-instance BSTS status bypass flag Add a bsts_unreliable flag to struct aw88399 that, when set, causes the startup status check (aw_dev_check_sysst) to skip the BSTS (boost startup finished) requirement. On some hardware, the BSTS bit in the SYSST register (0x01, bit 9) does not reliably assert even during normal audio playback. Register inspection on affected Lenovo Legion hardware shows both amplifiers reporting BSTS=0 on both channels despite clean audio output. Per the AW88399 datasheet, BSTS indicates boost startup completion. If BSTS never reliably sets to 1, the chip is never allowed to start by aw_dev_check_sysst, regardless of whether the boot failure is genuine. The new flag defaults to false via kzalloc, preserving the original check behavior for all existing users. No existing code path sets this flag; it will be set by the forthcoming HDA side codec property driver for affected hardware. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB77242B8E5BB8BFB5E69816E9FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index 3a2153f0ee92..dee91b540b81 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -598,6 +598,7 @@ struct aw88399 { unsigned int crc_init_val; unsigned int vcalb_init_val; unsigned int dither_st; + bool bsts_unreliable; }; int aw_dev_check_syspll(struct aw_device *aw_dev); diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index b525695c96d1..2045c4171be0 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -167,8 +167,9 @@ int aw_dev_check_syspll(struct aw_device *aw_dev) } EXPORT_SYMBOL_GPL(aw_dev_check_syspll); -static int aw_dev_check_sysst(struct aw_device *aw_dev) +static int aw_dev_check_sysst(struct aw88399 *aw88399) { + struct aw_device *aw_dev = aw88399->aw_pa; unsigned int check_val; unsigned int reg_val; int ret, i; @@ -182,6 +183,14 @@ static int aw_dev_check_sysst(struct aw_device *aw_dev) else check_val = AW88399_BIT_SYSST_SWS_CHECK; + /* + * On some hardware the BSTS (boost-finished) status bit does not + * reliably assert even when audio output is working normally. + * Allow per-instance bypass when flagged by the side-codec driver. + */ + if (aw88399->bsts_unreliable) + check_val &= ~AW88399_BSTS_FINISHED_VALUE; + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); if (ret) @@ -710,7 +719,7 @@ static int aw88399_dev_start(struct aw88399 *aw88399) usleep_range(AW88399_1000_US, AW88399_1000_US + 50); /* check i2s status */ - ret = aw_dev_check_sysst(aw_dev); + ret = aw_dev_check_sysst(aw88399); if (ret) { dev_err(aw_dev->dev, "sysst check failed"); goto sysst_check_fail; From b5e1d685fb9c9acef5942794a8dc07580c607e66 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:06 +0200 Subject: [PATCH 263/791] ASoC: aw88399: add firmware reload flag for resume Add a fw_needs_reload flag to struct aw88399 that, when set, causes aw88399_start to perform a full DSP firmware upload instead of assuming the firmware binary is already present in memory. After system sleep, the AW88399 loses its memory contents. The existing start sequence assumes the firmware binary persists from initialization and only uploads register configuration and DSP config (AW88399_DSP_FW_UPDATE_OFF). When memory is empty, this causes the subsequent CRC check to fail, triggering the retry mechanism in aw88399_start_pa which re-uploads the firmware on the second attempt. While the retry mechanism recovers correctly, it produces misleading error-level log messages on every resume cycle. The fw_needs_reload flag allows the HDA side codec driver to signal that a full firmware reload is needed after resume, eliminating the spurious CRC failures. The flag defaults to false via kzalloc, preserving the original behavior for existing ASoC users. No existing code path sets this flag; it will be set by the HDA side codec driver's system suspend handler. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB77240CB79188C0B7AE243829FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index dee91b540b81..3dbc37dc13d1 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -599,6 +599,7 @@ struct aw88399 { unsigned int vcalb_init_val; unsigned int dither_st; bool bsts_unreliable; + bool fw_needs_reload; }; int aw_dev_check_syspll(struct aw_device *aw_dev); diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 2045c4171be0..094a37b66fb5 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1171,12 +1171,15 @@ void aw88399_start(struct aw88399 *aw88399, bool sync_start) if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) return; - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); + ret = aw88399_dev_fw_update(aw88399, aw88399->fw_needs_reload ? + AW88399_DSP_FW_UPDATE_ON : AW88399_DSP_FW_UPDATE_OFF, true); if (ret) { dev_err(aw88399->aw_pa->dev, "fw update failed."); return; } + aw88399->fw_needs_reload = false; + if (sync_start == AW88399_SYNC_START) aw88399_start_pa(aw88399); else From 15c9fdb3c39d5073463ffa746cc64ad9682c875f Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:07 +0200 Subject: [PATCH 264/791] ASoC: aw88399: add channel setter for HDA side codec Add aw88399_dev_set_channel() to the shared library so that the HDA side codec driver can set the amplifier's channel assignment without including the aw88395 device header directly. The AW88399's struct aw_device is defined in aw88395_device.h, which lives under sound/soc/codecs/aw88395/. Without this accessor, the HDA driver would need a cross-subsystem relative include path to access the channel field. Providing a setter in the library keeps the interface clean and avoids coupling the HDA driver to ASoC-internal headers. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB7724E8A1AD36D1E623FA2A0AFCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index 3dbc37dc13d1..a1e0de6be8ea 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -609,6 +609,7 @@ int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value); int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc); int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute); +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel); void aw88399_hw_reset(struct aw88399 *aw88399); int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap); extern const struct regmap_config aw88399_remap_config; diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 094a37b66fb5..5c7982891def 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1401,5 +1401,11 @@ int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap } EXPORT_SYMBOL_GPL(aw88399_init); +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel) +{ + aw88399->aw_pa->channel = channel; +} +EXPORT_SYMBOL_GPL(aw88399_dev_set_channel); + MODULE_DESCRIPTION("AW88399 common device library"); MODULE_LICENSE("GPL"); From 315a717c716eab4e8db1ec4ddd13d4b963bf73c6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:33:38 +0000 Subject: [PATCH 265/791] ASoC: mediatek: mt8192-mt6359-rt1015-rt5682: use *dev in mt8192_mt6359_card_set_be_link() use *dev, instead of card->dev. No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/871pcxjfd9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 91c57765ab57..864b4d7228f5 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), }; -static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, +static int mt8192_mt6359_card_set_be_link(struct device *dev, struct snd_soc_dai_link *link, struct device_node *node, char *link_name) @@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, int ret; if (node && strcmp(link->name, link_name) == 0) { - ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); + ret = snd_soc_of_get_dai_link_codecs(dev, node, link); if (ret < 0) { - dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); + dev_err_probe(dev, ret, "get dai link codecs fail\n"); return ret; } } @@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) } for_each_card_prelinks(card, i, dai_link) { - ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); if (ret) { dev_err_probe(dev, ret, "%s set speaker_codec fail\n", dai_link->name); break; } - ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); if (ret) { dev_err_probe(dev, ret, "%s set headset_codec fail\n", dai_link->name); break; } - ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); + ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); if (ret) { dev_err_probe(dev, ret, "%s set headset_codec fail\n", dai_link->name); From 8fd27d490a87c56be8f73c0f5ed26a88a20d1b98 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:33:42 +0000 Subject: [PATCH 266/791] ASoC: mediatek: common/mtk-dsp-sof-common: use for_each_card_prelinks() We already have for_each_card_prelinks(). Let's use it. Signed-off-by: Kuninori Morimoto Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/87zezli0sp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/common/mtk-dsp-sof-common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c index 17b9ea6be604..adefbed5bbe7 100644 --- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c +++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c @@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, const char *propname) { struct device_node *np = dev->of_node; + struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link *parsed_dai_link; const char *dai_name = NULL; int i, j, ret, num_links, parsed_num_links = 0; @@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, return ret; } dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); - for (j = 0; j < card->num_links; j++) { - if (!strcmp(dai_name, card->dai_link[j].name)) { - memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], + for_each_card_prelinks(card, j, dai_link) { + if (!strcmp(dai_name, dai_link->name)) { + memcpy(&parsed_dai_link[parsed_num_links++], dai_link, sizeof(struct snd_soc_dai_link)); break; } From a5db2423440548f86b0a5608ffc9ffb6c01ac67d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:33:46 +0000 Subject: [PATCH 267/791] ASoC: mediatek: mt8196-nau8825: remove unnecessary declaration No need to declarate mt8196_nau8825_soc_card. Remove it. Signed-off-by: Kuninori Morimoto Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/87y0f5i0sl.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8196/mt8196-nau8825.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c index c9424786c53d..8303ab960f80 100644 --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c @@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" -static struct snd_soc_card mt8196_nau8825_soc_card; - static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { /* SOF Uplink */ SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), From 7a8073fe03bbc77aa6adf71ec8c76cf6ad03c498 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:33:51 +0000 Subject: [PATCH 268/791] ASoC: mediatek: mt8365-mt6357: remove useless assignment static int mt8365_mt6357_dev_probe(...) { ... struct device *dev = card->dev; ... => card->dev = dev; ... } This is useless. Remove it. Signed-off-by: Kuninori Morimoto Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/87wlupi0sg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8365/mt8365-mt6357.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c index 4339f45ee115..dceee837c67d 100644 --- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c +++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c @@ -297,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool struct mt8365_mt6357_priv *mach_priv; int ret; - card->dev = dev; ret = parse_dai_link_info(card); if (ret) goto err; From 1d79804a358ce35a5c1182124a0f43aa4e54003c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 21 Jul 2026 15:54:11 -0700 Subject: [PATCH 269/791] ALSA: atmel: ac97c: use platform helpers and devm cleanup Convert atmel_ac97c_probe() to the managed APIs. Replace the open-coded platform_get_resource() + ioremap() with devm_platform_ioremap_resource(), which requests and maps the AC97C register window in one call. Switch the clock to devm_clk_get_enabled(), the card to snd_devm_card_new(), and the interrupt to devm_request_irq(). The now-unnecessary error-path cleanup and the manual teardown in atmel_ac97c_remove() are dropped, since devm handles them. platform_get_irq() was already used; tighten its error check to irq < 0. Both resource and IRQ lookups are equivalent for a platform-backed device. The AC97C register window is owned solely by this driver, so the new region request cannot conflict with another claimant, and it is mapped exactly once (no double mapping). No functional change; built for ARM (allmodconfig + SND_ATMEL_AC97C) with LLVM=1 and sound/atmel/ac97c.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260721225411.815553-1-rosenp@gmail.com Signed-off-by: Takashi Iwai --- sound/atmel/ac97c.c | 67 ++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index e394205f469b..234549e17351 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -693,7 +693,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct snd_card *card; struct atmel_ac97c *chip; - struct resource *regs; + void __iomem *regs; struct clk *pclk; static const struct snd_ac97_bus_ops ops = { .write = atmel_ac97c_write, @@ -702,42 +702,34 @@ static int atmel_ac97c_probe(struct platform_device *pdev) int retval; int irq; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "no memory resource\n"); - return -ENXIO; - } + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_dbg(&pdev->dev, "could not get irq: %d\n", irq); + if (irq < 0) return irq; - } - pclk = clk_get(&pdev->dev, "ac97_clk"); + pclk = devm_clk_get_enabled(&pdev->dev, "ac97_clk"); if (IS_ERR(pclk)) { dev_dbg(&pdev->dev, "no peripheral clock\n"); return PTR_ERR(pclk); } - retval = clk_prepare_enable(pclk); - if (retval) - goto err_prepare_enable; - retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, + retval = snd_devm_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(struct atmel_ac97c), &card); if (retval) { dev_dbg(&pdev->dev, "could not create sound card device\n"); - goto err_snd_card_new; + return retval; } chip = get_chip(card); - retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); - if (retval) { - dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); - goto err_request_irq; - } + retval = devm_request_irq(&pdev->dev, irq, atmel_ac97c_interrupt, 0, "AC97C", chip); + if (retval) + return retval; + chip->irq = irq; spin_lock_init(&chip->lock); @@ -749,13 +741,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) chip->card = card; chip->pclk = pclk; chip->pdev = pdev; - chip->regs = ioremap(regs->start, resource_size(regs)); - - if (!chip->regs) { - dev_dbg(&pdev->dev, "could not remap register memory\n"); - retval = -ENOMEM; - goto err_ioremap; - } + chip->regs = regs; chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH); if (IS_ERR(chip->reset_pin)) @@ -770,25 +756,25 @@ static int atmel_ac97c_probe(struct platform_device *pdev) retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); if (retval) { dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); - goto err_ac97_bus; + return retval; } retval = atmel_ac97c_mixer_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); - goto err_ac97_bus; + return retval; } retval = atmel_ac97c_pcm_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); - goto err_ac97_bus; + return retval; } retval = snd_card_register(card); if (retval) { dev_dbg(&pdev->dev, "could not register sound card\n"); - goto err_ac97_bus; + return retval; } platform_set_drvdata(pdev, card); @@ -797,18 +783,6 @@ static int atmel_ac97c_probe(struct platform_device *pdev) chip->regs, irq); return 0; - -err_ac97_bus: - iounmap(chip->regs); -err_ioremap: - free_irq(irq, chip); -err_request_irq: - snd_card_free(card); -err_snd_card_new: - clk_disable_unprepare(pclk); -err_prepare_enable: - clk_put(pclk); - return retval; } static int atmel_ac97c_suspend(struct device *pdev) @@ -839,13 +813,6 @@ static void atmel_ac97c_remove(struct platform_device *pdev) ac97c_writel(chip, CAMR, 0); ac97c_writel(chip, COMR, 0); ac97c_writel(chip, MR, 0); - - clk_disable_unprepare(chip->pclk); - clk_put(chip->pclk); - iounmap(chip->regs); - free_irq(chip->irq, chip); - - snd_card_free(card); } static struct platform_driver atmel_ac97c_driver = { From 784e04110272590a34d7faad656232b7f9aeb1df Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 22 Jul 2026 16:46:55 +0530 Subject: [PATCH 270/791] ASoC: qcom: qdsp6: Remove unused Q6AFE_MAX_CLK_ID define Q6AFE_MAX_CLK_ID is not used anywhere. Remove the unused define. Signed-off-by: Prasad Kumpatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260722111655.3558096-1-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 45850f2d4342..7b553a73bc92 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -237,8 +237,6 @@ #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 -#define Q6AFE_MAX_CLK_ID 104 - #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 #define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 From da3048b0c6153cb03b68cbcc5db62e298806d4b8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:49:17 +0000 Subject: [PATCH 271/791] ASoC: intel: sof_sdw: use &pdev->dev instead of card->dev sof_sdw.c will be updated when Card capsuling. To makes its review easy, use &pdev->dev instead of card->dev in mc_probe(). There is no diff, because static int mc_probe(...) { ... card->dev = &pdev->dev; ... } No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/877bmpi02q.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index acfa34aea242..3386beebf39d 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1481,12 +1481,12 @@ static int mc_probe(struct platform_device *pdev) dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(card->dev); + log_quirks(&pdev->dev); ctx->mc_quirk = sof_sdw_quirk; /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ @@ -1505,13 +1505,13 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) amp_num += codec_info_list[i].amp_num; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, " cfg-amp:%d", amp_num); if (!card->components) return -ENOMEM; if (mach->mach_params.dmic_num) { - card->components = devm_kasprintf(card->dev, GFP_KERNEL, + card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s mic:dmic cfg-mics:%d", card->components, mach->mach_params.dmic_num); @@ -1522,7 +1522,7 @@ static int mc_probe(struct platform_device *pdev) /* Register the card */ ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); asoc_sdw_mc_dailink_exit_loop(card); return ret; } From 2700946d7bdf7a112d79db018b5f8503bd2ffa0d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:30:08 +0000 Subject: [PATCH 272/791] ASoC: fsl: p1022_ds: add card_to_mdata() macro p1022_ds.c will be updated when Card capsuling. To makes its review easy, adds card_to_mdata() and useit to reduce un-related diff. No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/875x29jfj4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/fsl/p1022_ds.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index db07817a8024..98389f53246c 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -73,6 +73,8 @@ struct machine_data { char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; +#define card_to_mdata(_card) container_of(_card, struct machine_data, card) + /** * p1022_ds_machine_probe: initialize the board * @@ -82,8 +84,7 @@ struct machine_data { */ static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) static int p1022_ds_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct machine_data *mdata = - container_of(rtd->card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(rtd->card); struct device *dev = rtd->card->dev; int ret = 0; @@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) */ static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -399,8 +398,7 @@ static int p1022_ds_probe(struct platform_device *pdev) static void p1022_ds_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); snd_soc_unregister_card(card); kfree(mdata); From c02fe2006059c3bd2c5b7c25213a1797a0ba091e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:30:12 +0000 Subject: [PATCH 273/791] ASoC: fsl: p1022_rdk: add card_to_mdata() macro p1022_rdk.c will be updated when Card capsuling. To makes its review easy, adds card_to_mdata() and useit to reduce un-related diff. No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874ihtjfj0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/fsl/p1022_rdk.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index f80d6d04e791..d82fe22ca5c5 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -79,6 +79,8 @@ struct machine_data { char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; +#define card_to_mdata(_card) container_of(_card, struct machine_data, card) + /** * p1022_rdk_machine_probe - initialize the board * @card: ASoC card instance @@ -91,8 +93,7 @@ struct machine_data { */ static int p1022_rdk_machine_probe(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) static int p1022_rdk_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct machine_data *mdata = - container_of(rtd->card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(rtd->card); struct device *dev = rtd->card->dev; int ret = 0; @@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) */ static int p1022_rdk_machine_remove(struct snd_soc_card *card) { - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); struct ccsr_guts __iomem *guts; guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); @@ -361,8 +360,7 @@ static int p1022_rdk_probe(struct platform_device *pdev) static void p1022_rdk_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - struct machine_data *mdata = - container_of(card, struct machine_data, card); + struct machine_data *mdata = card_to_mdata(card); snd_soc_unregister_card(card); kfree(mdata); From 998b8a6c8aff2f1364197abd0dc05ccb37e62801 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 22 Jul 2026 15:36:07 +0100 Subject: [PATCH 274/791] ASoC: cs35l56: Sort table of sdw_device_id Swap the entries for 3562 and 3563 to keep the table in order of increasing part number. There's nothing broken here, it's just cosmetic. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260722143607.1001473-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 1e442f43b306..303d37e7d0bf 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -510,8 +510,8 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { static const struct sdw_device_id cs35l56_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), - SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), + SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); From 61471f29f3157f33a61194bf82b4a289cc03e1f1 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 23 Jul 2026 14:59:47 +0800 Subject: [PATCH 275/791] ALSA: hda/realtek - Add quirk to another Razer Blade 16 Add quirk to another machine. Fixes: 961d9f98da0d ("ALSA: hda/realtek: Enable internal speakers on Razer Blade 16 (2025)") Signed-off-by: Kailang Yang Link: https://lore.kernel.org/0cd8c77a82a4481ca9409ac68f1041e8@realtek.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 09e7247491f3..b22564f9291f 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7924,6 +7924,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1a58, 0x2023, "Razer Blade 16 (2025)", + ALC298_FIXUP_RAZER_BLADE16_2025_PINS), SND_PCI_QUIRK(0x1a58, 0x300e, "Razer Blade 16 (2025)", ALC298_FIXUP_RAZER_BLADE16_2025_PINS), SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), From 847b7114df54b6a5448b784e74d0868bf65a69b7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:32 +0700 Subject: [PATCH 276/791] ASoC: codecs: da7213: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 4bf91ab2553a..923b997efbc4 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_get_volsw(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_get_volsw(kcontrol, ucontrol); } static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, @@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_put_volsw(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_put_volsw(kcontrol, ucontrol); } static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, @@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_get_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_get_enum_double(kcontrol, ucontrol); } static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, @@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7213->ctrl_lock); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); - return ret; + return snd_soc_put_enum_double(kcontrol, ucontrol); } /* ALC */ @@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, __le16 val; int ret; - mutex_lock(&da7213->ctrl_lock); - ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7213->ctrl_lock); + scoped_guard(mutex, &da7213->ctrl_lock) + ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); if (ret) return ret; @@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, */ val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - mutex_lock(&da7213->ctrl_lock); + guard(mutex)(&da7213->ctrl_lock); ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); if (ret == 0 && (val_old != val_new)) ret = regmap_raw_write(da7213->regmap, reg, &val_new, sizeof(val_new)); - mutex_unlock(&da7213->ctrl_lock); if (ret < 0) return ret; From 47f85aa98bb2a23e42b24bde8fd3ac244f3f7c6c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:33 +0700 Subject: [PATCH 277/791] ASoC: codecs: da7219: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 62 ++++++++++++--------------------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index f0874d891e12..f6d371e93549 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_get_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_get_volsw(kcontrol, ucontrol); } static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, @@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_put_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_put_volsw(kcontrol, ucontrol); } static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, @@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_get_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_get_enum_double(kcontrol, ucontrol); } static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, @@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->ctrl_lock); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); - return ret; + return snd_soc_put_enum_double(kcontrol, ucontrol); } /* ALC */ @@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, __le16 val; int ret; - mutex_lock(&da7219->ctrl_lock); - ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7219->ctrl_lock); + scoped_guard(mutex, &da7219->ctrl_lock) + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); if (ret) return ret; @@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, */ val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - mutex_lock(&da7219->ctrl_lock); + guard(mutex)(&da7219->ctrl_lock); ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); if (ret == 0 && (val_old != val_new)) ret = regmap_raw_write(da7219->regmap, reg, - &val_new, sizeof(val_new)); - mutex_unlock(&da7219->ctrl_lock); + &val_new, sizeof(val_new)); if (ret < 0) return ret; @@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&da7219->pll_lock); + guard(mutex)(&da7219->pll_lock); - if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { - mutex_unlock(&da7219->pll_lock); + if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) return 0; - } - if ((freq < 2000000) || (freq > 54000000)) { - mutex_unlock(&da7219->pll_lock); + if (freq < 2000000 || freq > 54000000) { dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", freq); return -EINVAL; @@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); - mutex_unlock(&da7219->pll_lock); return -EINVAL; } @@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, freq = clk_round_rate(da7219->mclk, freq); ret = clk_set_rate(da7219->mclk, freq); if (ret) { - dev_err(codec_dai->dev, "Failed to set clock rate %d\n", - freq); - mutex_unlock(&da7219->pll_lock); + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); return ret; } } da7219->mclk_rate = freq; - mutex_unlock(&da7219->pll_lock); - return 0; } @@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, { struct snd_soc_component *component = codec_dai->component; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&da7219->pll_lock); - ret = da7219_set_pll(component, source, fout); - mutex_unlock(&da7219->pll_lock); + guard(mutex)(&da7219->pll_lock); - return ret; + return da7219_set_pll(component, source, fout); } static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) From 357d20c73de668504ddf336f07d30ba7b41dbcd8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:34 +0700 Subject: [PATCH 278/791] ASoC: codecs: es8316: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8316.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 3abe77423f29..87f331868dc7 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -620,15 +621,15 @@ static irqreturn_t es8316_irq(int irq, void *data) struct snd_soc_component *comp = es8316->component; unsigned int flags; - mutex_lock(&es8316->lock); + guard(mutex)(&es8316->lock); regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); if (flags == 0x00) - goto out; /* Powered-down / reset */ + return IRQ_HANDLED; /* Powered-down / reset */ /* Catch spurious IRQ before set_jack is called */ if (!es8316->jack) - goto out; + return IRQ_HANDLED; if (es8316->jd_inverted) flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; @@ -681,8 +682,6 @@ static irqreturn_t es8316_irq(int irq, void *data) } } -out: - mutex_unlock(&es8316->lock); return IRQ_HANDLED; } @@ -699,18 +698,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, es8316->jd_inverted = device_property_read_bool(component->dev, "everest,jack-detect-inverted"); - mutex_lock(&es8316->lock); + scoped_guard(mutex, &es8316->lock) { + es8316->jack = jack; - es8316->jack = jack; + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_enable_micbias_for_mic_gnd_short_detect(component); - if (es8316->jack->status & SND_JACK_MICROPHONE) - es8316_enable_micbias_for_mic_gnd_short_detect(component); - - snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, - ES8316_GPIO_ENABLE_INTERRUPT, - ES8316_GPIO_ENABLE_INTERRUPT); - - mutex_unlock(&es8316->lock); + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, + ES8316_GPIO_ENABLE_INTERRUPT); + } /* Enable irq and sync initial jack state */ enable_irq(es8316->irq); @@ -726,7 +723,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) disable_irq(es8316->irq); - mutex_lock(&es8316->lock); + guard(mutex)(&es8316->lock); snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, ES8316_GPIO_ENABLE_INTERRUPT, 0); @@ -737,8 +734,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) } es8316->jack = NULL; - - mutex_unlock(&es8316->lock); } static int es8316_set_jack(struct snd_soc_component *component, From 61bc0010fafab96f885bb69da214674ad15d8f3d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:35 +0700 Subject: [PATCH 279/791] ASoC: codecs: es8326: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a79b2da35099..c5460589a88b 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -6,6 +6,7 @@ // Authors: David Yang // +#include #include #include #include @@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ return; - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); iface = snd_soc_component_read(comp, ES8326_HPDET_STA); switch (iface) { case 0x93: @@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) } es8326_disable_micbias(es8326->component); } - mutex_unlock(&es8326->lock); } static void es8326_jack_detect_handler(struct work_struct *work) @@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) struct snd_soc_component *comp = es8326->component; unsigned int iface; - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); iface = snd_soc_component_read(comp, ES8326_HPDET_STA); dev_dbg(comp->dev, "gpio flag %#04x", iface); @@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? ~es8326->jack_pol : es8326->jack_pol)); - goto exit; + return; } if ((iface & ES8326_HPINSERT_FLAG) == 0) { @@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, msecs_to_jiffies(400)); es8326->hp = 1; - goto exit; + return; } if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ @@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); es8326_enable_micbias(es8326->component); queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); - goto exit; + return; } if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { dev_dbg(comp->dev, "Headphone detected\n"); @@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } } -exit: - mutex_unlock(&es8326->lock); } static irqreturn_t es8326_irq(int irq, void *dev_id) @@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - mutex_lock(&es8326->lock); - if (es8326->jd_inverted) - snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, - ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); - es8326->jack = jack; - - mutex_unlock(&es8326->lock); + scoped_guard(mutex, &es8326->lock) { + if (es8326->jd_inverted) + snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); + es8326->jack = jack; + } es8326_irq(es8326->irq, es8326); } @@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) return; /* Already disabled (or never enabled) */ cancel_delayed_work_sync(&es8326->jack_detect_work); - mutex_lock(&es8326->lock); + guard(mutex)(&es8326->lock); if (es8326->jack->status & SND_JACK_MICROPHONE) { es8326_disable_micbias(component); snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); } es8326->jack = NULL; - mutex_unlock(&es8326->lock); } static int es8326_set_jack(struct snd_soc_component *component, From 17b1563aad1fb77ed9e91309a2e06307cf8f95e0 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:36 +0700 Subject: [PATCH 280/791] ASoC: codecs: es9356: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index f574b3d6cb3c..a9863900bf70 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -500,16 +501,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne } /* power state changes are not independent across functions */ - mutex_lock(&es9356->pde_lock); - ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); - if (ret) { - regmap_write(es9356->regmap, - SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); - es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); - } else - dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); - - mutex_unlock(&es9356->pde_lock); + scoped_guard(mutex, &es9356->pde_lock) { + ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); + if (ret) { + regmap_write(es9356->regmap, + SDW_SDCA_CTL(func, pde_entity, + ES9356_SDCA_CTL_REQ_POWER_STATE, 0), + ps ? ps3 : ps0); + es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); + } else { + dev_dbg(component->dev, "%s PDE is already %d\n", __func__, + ps ? ps0 : ps3); + } + } if (rate) regmap_write(es9356->regmap, @@ -1091,9 +1095,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) { struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); - mutex_lock(&es9356->disable_irq_lock); - es9356->disable_irq = true; - mutex_unlock(&es9356->disable_irq_lock); + scoped_guard(mutex, &es9356->disable_irq_lock) + es9356->disable_irq = true; return es9356_sdca_dev_suspend(dev); } From db044108a54a12de82b344c44ae8e7fcb26caeb8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:37 +0700 Subject: [PATCH 281/791] ASoC: codecs: fs210x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/fs210x.c | 89 ++++++++++++++------------------------- 1 file changed, 31 insertions(+), 58 deletions(-) diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index 5f381fe063e8..dce525ae50dd 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -4,6 +4,7 @@ // // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. +#include #include #include #include @@ -770,9 +771,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) return -EOPNOTSUPP; - mutex_lock(&fs210x->lock); - ret = fs210x_set_hw_params(fs210x); - mutex_unlock(&fs210x->lock); + scoped_guard(mutex, &fs210x->lock) + ret = fs210x_set_hw_params(fs210x); if (ret) dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); @@ -789,15 +789,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) fs210x = snd_soc_component_get_drvdata(dai->component); - mutex_lock(&fs210x->lock); - - if (!fs210x->is_inited || fs210x->is_suspended) { - mutex_unlock(&fs210x->lock); - return 0; + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended) + return 0; } - mutex_unlock(&fs210x->lock); - if (mute) { cancel_delayed_work_sync(&fs210x->fault_check_work); cancel_delayed_work_sync(&fs210x->start_work); @@ -816,15 +812,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, fs210x = snd_soc_component_get_drvdata(dai->component); - mutex_lock(&fs210x->lock); - - if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { - mutex_unlock(&fs210x->lock); - return 0; + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) + return 0; } - mutex_unlock(&fs210x->lock); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -852,13 +844,11 @@ static void fs210x_start_work(struct work_struct *work) fs210x = container_of(work, struct fs210x_priv, start_work.work); - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); ret = fs210x_dev_play(fs210x); if (ret) dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); - - mutex_unlock(&fs210x->lock); } static void fs210x_fault_check_work(struct work_struct *work) @@ -869,15 +859,13 @@ static void fs210x_fault_check_work(struct work_struct *work) fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); - mutex_lock(&fs210x->lock); + scoped_guard(mutex, &fs210x->lock) { + if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) + return; - if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { - mutex_unlock(&fs210x->lock); - return; + ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); } - ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); - mutex_unlock(&fs210x->lock); if (ret) return; @@ -990,7 +978,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, if (fs210x->scene_id < 1) return -EINVAL; - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); /* * FS210x has scene(s) as below: * init scene: id = 0 @@ -999,7 +987,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, */ index = fs210x->scene_id - 1; ucontrol->value.integer.value[0] = index; - mutex_unlock(&fs210x->lock); return 0; } @@ -1018,7 +1005,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); /* * FS210x has scene(s) as below: @@ -1028,17 +1015,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, */ scene_id = ucontrol->value.integer.value[0] + 1; scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ - if (scene_id < 1 || scene_id > scene_count) { - mutex_unlock(&fs210x->lock); + if (scene_id < 1 || scene_id > scene_count) return -ERANGE; - } if (scene_id != fs210x->scene_id) is_changed = true; if (fs210x->is_suspended) { fs210x->scene_id = scene_id; - mutex_unlock(&fs210x->lock); return is_changed; } @@ -1046,8 +1030,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, if (ret) dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); - mutex_unlock(&fs210x->lock); - if (!ret && is_changed) return 1; @@ -1061,12 +1043,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); int ret = 0; - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); - if (fs210x->is_suspended) { - mutex_unlock(&fs210x->lock); + if (fs210x->is_suspended) return 0; - } switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1087,8 +1067,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, break; } - mutex_unlock(&fs210x->lock); - return ret; } @@ -1219,11 +1197,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) if (ret) return ret; - mutex_lock(&fs210x->lock); - ret = fs210x_init_chip(fs210x); - mutex_unlock(&fs210x->lock); + guard(mutex)(&fs210x->lock); - return ret; + return fs210x_init_chip(fs210x); } static void fs210x_remove(struct snd_soc_component *cmpnt) @@ -1250,15 +1226,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) regcache_cache_only(fs210x->regmap, true); - mutex_lock(&fs210x->lock); - fs210x->cur_scene = NULL; - fs210x->is_inited = false; - fs210x->is_playing = false; - fs210x->is_suspended = true; + scoped_guard(mutex, &fs210x->lock) { + fs210x->cur_scene = NULL; + fs210x->is_inited = false; + fs210x->is_playing = false; + fs210x->is_suspended = true; - gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ - fsleep(30000); /* >= 30ms */ - mutex_unlock(&fs210x->lock); + gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ + fsleep(30000); /* >= 30ms */ + } cancel_delayed_work_sync(&fs210x->start_work); cancel_delayed_work_sync(&fs210x->fault_check_work); @@ -1287,14 +1263,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) return ret; } - mutex_lock(&fs210x->lock); + guard(mutex)(&fs210x->lock); fs210x->is_suspended = false; - ret = fs210x_init_chip(fs210x); - mutex_unlock(&fs210x->lock); - - return ret; + return fs210x_init_chip(fs210x); } #else #define fs210x_suspend NULL From 51aac9ed7c0e5be51ca274a0f062e687ddb7f6ca Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:38 +0700 Subject: [PATCH 282/791] ASoC: codecs: hdac_hdmi: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 127 +++++++++++++++++------------------ 1 file changed, 62 insertions(+), 65 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 38073a70fa61..d9e6fa6d3e99 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -10,6 +10,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( continue; list_for_each_entry(port, &pcm->port_list, head) { - mutex_lock(&pcm->lock); - ret = hdac_hdmi_query_port_connlist(hdev, - port->pin, port); - mutex_unlock(&pcm->lock); + scoped_guard(mutex, &pcm->lock) { + ret = hdac_hdmi_query_port_connlist(hdev, + port->pin, + port); + } if (ret < 0) continue; @@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (pcm) { - mutex_lock(&pcm->lock); - pcm->chmap_set = false; - memset(pcm->chmap, 0, sizeof(pcm->chmap)); - pcm->channels = 0; - mutex_unlock(&pcm->lock); + scoped_guard(mutex, &pcm->lock) { + pcm->chmap_set = false; + memset(pcm->chmap, 0, sizeof(pcm->chmap)); + pcm->channels = 0; + } } if (dai_map->port) @@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, if (port == NULL) return -EINVAL; - mutex_lock(&hdmi->pin_mutex); + guard(mutex)(&hdmi->pin_mutex); list_for_each_entry(pcm, &hdmi->pcm_list, head) { if (list_empty(&pcm->port_list)) continue; @@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, list_add_tail(&port->head, &pcm->port_list); if (port->eld.monitor_present && port->eld.eld_valid) { hdac_hdmi_jack_report_sync(pcm, port, true); - mutex_unlock(&hdmi->pin_mutex); return ret; } } } - mutex_unlock(&hdmi->pin_mutex); return ret; } @@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, * In case of non MST pin, get_eld info API expectes port * to be -1. */ - mutex_lock(&hdmi->pin_mutex); - port->eld.monitor_present = false; + scoped_guard(mutex, &hdmi->pin_mutex) { + port->eld.monitor_present = false; - if (pin->mst_capable) - port_id = port->id; + if (pin->mst_capable) + port_id = port->id; - size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, - &port->eld.monitor_present, - port->eld.eld_buffer, - ELD_MAX_SIZE); + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, + &port->eld.monitor_present, + port->eld.eld_buffer, + ELD_MAX_SIZE); - if (size > 0) { - size = min(size, ELD_MAX_SIZE); - if (hdac_hdmi_parse_eld(hdev, port) < 0) - size = -EINVAL; - } - - eld_valid = port->eld.eld_valid; - - if (size > 0) { - port->eld.eld_valid = true; - port->eld.eld_size = size; - } else { - port->eld.eld_valid = false; - port->eld.eld_size = 0; - } - - eld_changed = (eld_valid != port->eld.eld_valid); - - pcm = hdac_hdmi_get_pcm(hdev, port); - - if (!port->eld.monitor_present || !port->eld.eld_valid) { - - dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", - __func__, pin->nid, port->id); - - /* - * PCMs are not registered during device probe, so don't - * report jack here. It will be done in usermode mux - * control select. - */ - if (pcm) { - hdac_hdmi_jack_report(pcm, port, false); - schedule_work(&port->dapm_work); + if (size > 0) { + size = min(size, ELD_MAX_SIZE); + if (hdac_hdmi_parse_eld(hdev, port) < 0) + size = -EINVAL; } - mutex_unlock(&hdmi->pin_mutex); - return; - } + eld_valid = port->eld.eld_valid; - if (port->eld.monitor_present && port->eld.eld_valid) { - if (pcm) { - hdac_hdmi_jack_report(pcm, port, true); - schedule_work(&port->dapm_work); + if (size > 0) { + port->eld.eld_valid = true; + port->eld.eld_size = size; + } else { + port->eld.eld_valid = false; + port->eld.eld_size = 0; } - print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, - port->eld.eld_buffer, port->eld.eld_size, false); + eld_changed = (eld_valid != port->eld.eld_valid); + pcm = hdac_hdmi_get_pcm(hdev, port); + + if (!port->eld.monitor_present || !port->eld.eld_valid) { + + dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); + + /* + * PCMs are not registered during device probe, so don't + * report jack here. It will be done in usermode mux + * control select. + */ + if (pcm) { + hdac_hdmi_jack_report(pcm, port, false); + schedule_work(&port->dapm_work); + } + + return; + } + + if (port->eld.monitor_present && port->eld.eld_valid) { + if (pcm) { + hdac_hdmi_jack_report(pcm, port, true); + schedule_work(&port->dapm_work); + } + + print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, + port->eld.eld_buffer, port->eld.eld_size, false); + } } - mutex_unlock(&hdmi->pin_mutex); if (eld_changed && pcm) snd_ctl_notify(hdmi->card, @@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, if (list_empty(&pcm->port_list)) return; - mutex_lock(&pcm->lock); + guard(mutex)(&pcm->lock); pcm->chmap_set = true; memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); list_for_each_entry(port, &pcm->port_list, head) if (prepared) hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); - mutex_unlock(&pcm->lock); } static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) From 9c61998c9c858839664bdf0b1457639b0b6c5e4c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:39 +0700 Subject: [PATCH 283/791] ASoC: codecs: hdmi-codec: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdmi-codec.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 13ae9e83bc21..bc2c22436ba6 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -4,6 +4,7 @@ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha */ +#include #include #include #include @@ -452,31 +453,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, if (!((has_playback && tx) || (has_capture && !tx))) return 0; - mutex_lock(&hcp->lock); + guard(mutex)(&hcp->lock); if (hcp->busy) { dev_err(dai->dev, "Only one simultaneous stream supported!\n"); - mutex_unlock(&hcp->lock); return -EINVAL; } if (hcp->hcd.ops->audio_startup) { ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); if (ret) - goto err; + return ret; } if (tx && hcp->hcd.ops->get_eld) { ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); if (ret) - goto err; + return ret; snd_parse_eld(dai->dev, &hcp->eld_parsed, hcp->eld, sizeof(hcp->eld)); ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); if (ret) - goto err; + return ret; /* Select chmap supported */ hdmi_codec_eld_chmap(hcp); @@ -484,8 +484,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, hcp->busy = true; -err: - mutex_unlock(&hcp->lock); return ret; } @@ -503,9 +501,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - mutex_lock(&hcp->lock); + guard(mutex)(&hcp->lock); hcp->busy = false; - mutex_unlock(&hcp->lock); } static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, From e199ec211b5342723c68b8152b96b12846f7bbe7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:40 +0700 Subject: [PATCH 284/791] ASoC: codecs: idt821034: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/idt821034.c | 144 ++++++++++++++--------------------- 1 file changed, 57 insertions(+), 87 deletions(-) diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 084090ccef77..387f17b5f7b3 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -7,6 +7,7 @@ // Author: Herve Codina #include +#include #include #include #include @@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(mc->reg); - mutex_lock(&idt821034->mutex); - if (IDT821034_ID_IS_OUT(mc->reg)) - val = idt821034->amps.ch[ch].amp_out.gain; - else - val = idt821034->amps.ch[ch].amp_in.gain; - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) { + if (IDT821034_ID_IS_OUT(mc->reg)) + val = idt821034->amps.ch[ch].amp_out.gain; + else + val = idt821034->amps.ch[ch].amp_in.gain; + } ucontrol->value.integer.value[0] = val & mask; if (invert) @@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(mc->reg); - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); if (IDT821034_ID_IS_OUT(mc->reg)) { amp = &idt821034->amps.ch[ch].amp_out; @@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, gain_type = IDT821034_GAIN_TX; } - if (amp->gain == val) { - ret = 0; - goto end; - } + if (amp->gain == val) + return 0; if (!amp->is_muted) { ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); if (ret) - goto end; + return ret; } amp->gain = val; - ret = 1; /* The value changed */ -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 1; } static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, @@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(id); - mutex_lock(&idt821034->mutex); - is_muted = IDT821034_ID_IS_OUT(id) ? - idt821034->amps.ch[ch].amp_out.is_muted : - idt821034->amps.ch[ch].amp_in.is_muted; - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) { + is_muted = IDT821034_ID_IS_OUT(id) ? + idt821034->amps.ch[ch].amp_out.is_muted : + idt821034->amps.ch[ch].amp_in.is_muted; + } ucontrol->value.integer.value[0] = !is_muted; @@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, ch = IDT821034_ID_GET_CHAN(id); is_mute = !ucontrol->value.integer.value[0]; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); if (IDT821034_ID_IS_OUT(id)) { amp = &idt821034->amps.ch[ch].amp_out; @@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, gain_type = IDT821034_GAIN_TX; } - if (amp->is_muted == is_mute) { - ret = 0; - goto end; - } + if (amp->is_muted == is_mute) + return 0; ret = idt821034_set_gain_channel(idt821034, ch, gain_type, is_mute ? 0 : amp->gain); if (ret) - goto end; + return ret; amp->is_muted = is_mute; - ret = 1; /* The value changed */ -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 1; } static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); @@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); unsigned int id = w->shift; u8 power, mask; - int ret; u8 ch; ch = IDT821034_ID_GET_CHAN(id); mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); power = idt821034_get_channel_power(idt821034, ch); if (SND_SOC_DAPM_EVENT_ON(event)) power |= mask; else power &= ~mask; - ret = idt821034_set_channel_power(idt821034, ch, power); - mutex_unlock(&idt821034->mutex); - - return ret; + return idt821034_set_channel_power(idt821034, ch, power); } static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { @@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, ch = 0; while (mask && ch < IDT821034_NB_CHANNEL) { if (mask & 0x1) { - mutex_lock(&idt821034->mutex); - ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_set_channel_ts(idt821034, ch, + IDT821034_CH_RX, slot); if (ret) { dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", ch, ret); @@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, ch = 0; while (mask && ch < IDT821034_NB_CHANNEL) { if (mask & 0x1) { - mutex_lock(&idt821034->mutex); - ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_set_channel_ts(idt821034, ch, + IDT821034_CH_TX, slot); if (ret) { dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", ch, ret); @@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); u8 conf; - int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); conf = idt821034_get_codec_conf(idt821034); @@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) default: dev_err(dai->dev, "Unsupported DAI format 0x%x\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); - ret = -EINVAL; - goto end; + return -EINVAL; } - ret = idt821034_set_codec_conf(idt821034, conf); -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return idt821034_set_codec_conf(idt821034, conf); } static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, @@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, { struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); u8 conf; - int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); conf = idt821034_get_codec_conf(idt821034); @@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, default: dev_err(dai->dev, "Unsupported PCM format 0x%x\n", params_format(params)); - ret = -EINVAL; - goto end; + return -EINVAL; } - ret = idt821034_set_codec_conf(idt821034, conf); -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return idt821034_set_codec_conf(idt821034, conf); } static const unsigned int idt821034_sample_bits[] = {8}; @@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) int ret; u8 i; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); ret = idt821034_set_codec_conf(idt821034, 0); if (ret) - goto end; + return ret; for (i = 0; i < IDT821034_NB_CHANNEL; i++) { idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; @@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, idt821034->amps.ch[i].amp_out.gain); if (ret) - goto end; + return ret; idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; idt821034->amps.ch[i].amp_in.is_muted = false; ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, idt821034->amps.ch[i].amp_in.gain); if (ret) - goto end; + return ret; ret = idt821034_set_channel_power(idt821034, i, 0); if (ret) - goto end; + return ret; } - ret = 0; -end: - mutex_unlock(&idt821034->mutex); - return ret; + return 0; } static int idt821034_component_probe(struct snd_soc_component *component) @@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, u8 slic_raw; int ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_raw = idt821034_get_written_slic_raw(idt821034, ch); if (val) @@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, slic_raw &= ~mask; ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); - mutex_unlock(&idt821034->mutex); - if (ret) dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); @@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) u8 slic_raw; int ret; - mutex_lock(&idt821034->mutex); - ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); - mutex_unlock(&idt821034->mutex); + scoped_guard(mutex, &idt821034->mutex) + ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); if (ret) { dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); @@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset struct idt821034 *idt821034 = gpiochip_get_data(c); u8 slic_dir; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_dir = idt821034_get_slic_conf(idt821034, ch); - mutex_unlock(&idt821034->mutex); return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } @@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) return -EPERM; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; @@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs offset, ch, mask, ret); } - mutex_unlock(&idt821034->mutex); return ret; } @@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off if (ret) return ret; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; @@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off offset, ch, mask, ret); } - mutex_unlock(&idt821034->mutex); return ret; } @@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) int ret; u8 i; - mutex_lock(&idt821034->mutex); + guard(mutex)(&idt821034->mutex); /* IO0 and IO1 as input for all channels and output IO set to 0 */ for (i = 0; i < IDT821034_NB_CHANNEL; i++) { ret = idt821034_set_slic_conf(idt821034, i, IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); if (ret) - goto end; + return ret; ret = idt821034_write_slic_raw(idt821034, i, 0); if (ret) - goto end; + return ret; } - ret = 0; -end: - mutex_unlock(&idt821034->mutex); - return ret; + + return 0; } static int idt821034_gpio_init(struct idt821034 *idt821034) From 9296b430502cc1af3d48f5c90c84e33140e6c5b6 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:41 +0700 Subject: [PATCH 285/791] ASoC: codecs: lpass-macro: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index 6e3b8d0897dd..7e59616ed7bc 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2022, The Linux Foundation. All rights reserved. +#include #include #include #include @@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); void lpass_macro_set_codec_version(enum lpass_codec_version version) { - mutex_lock(&lpass_codec_mutex); + guard(mutex)(&lpass_codec_mutex); lpass_codec_version = version; - mutex_unlock(&lpass_codec_mutex); } EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); enum lpass_codec_version lpass_macro_get_codec_version(void) { - enum lpass_codec_version ver; + guard(mutex)(&lpass_codec_mutex); - mutex_lock(&lpass_codec_mutex); - ver = lpass_codec_version; - mutex_unlock(&lpass_codec_mutex); - - return ver; + return lpass_codec_version; } EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); From 5dabb2549c89234050d001ff19e9732d745855dc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:42 +0700 Subject: [PATCH 286/791] ASoC: codecs: madera: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Charles Keepax Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-12-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/madera.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index 98d72db599d8..55ea6950ca90 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. // +#include #include #include #include @@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, * We can't rely on the DAPM mutex for locking because we need a lock * that can safely be called in hw_params */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, madera_debug_dump_domain_groups(priv); - mutex_unlock(&priv->rate_lock); - return 0; } EXPORT_SYMBOL_GPL(madera_domain_clk_ev); @@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, const int adsp_num = e->shift_l; int item; - mutex_lock(&priv->rate_lock); - cached_rate = priv->adsp_rate_cache[adsp_num]; - mutex_unlock(&priv->rate_lock); + scoped_guard(mutex, &priv->rate_lock) + cached_rate = priv->adsp_rate_cache[adsp_num]; item = snd_soc_enum_val_to_item(e, cached_rate); ucontrol->value.enumerated.item[0] = item; @@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; const int adsp_num = e->shift_l; const unsigned int item = ucontrol->value.enumerated.item[0]; - int ret = 0; if (item >= e->items) return -EINVAL; @@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, * maintain consistent behaviour that rate domains cannot be changed * while in use since this is a hardware requirement */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { dev_warn(priv->madera->dev, "Cannot change '%s' while in use by active audio paths\n", kcontrol->id.name); - ret = -EBUSY; + return -EBUSY; } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { /* Volatile register so defer until the codec is powered up */ priv->adsp_rate_cache[adsp_num] = e->values[item]; - ret = 1; + return 1; } - mutex_unlock(&priv->rate_lock); - - return ret; + return 0; } static const struct soc_enum madera_adsp_rate_enum[] = { @@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, * Prevent the domain powering up while we're checking whether it's * safe to change rate domain */ - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); val = snd_soc_component_read(component, e->reg); val >>= e->shift_l; val &= e->mask; - if (snd_soc_enum_item_to_val(e, item) == val) { - ret = 0; - goto out; - } + if (snd_soc_enum_item_to_val(e, item) == val) + return 0; if (!madera_can_change_grp_rate(priv, e->reg)) { dev_warn(priv->madera->dev, @@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, ret = snd_soc_put_enum_double(kcontrol, ucontrol); madera_spin_sysclk(priv); } -out: - mutex_unlock(&priv->rate_lock); return ret; } @@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) return 0; - mutex_lock(&priv->rate_lock); + guard(mutex)(&priv->rate_lock); if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { madera_aif_warn(dai, "Cannot change rate while active\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } /* Guard the rate change with SYSCLK cycles */ @@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, MADERA_AIF1_RATE_MASK, tar); madera_spin_sysclk(priv); -out: - mutex_unlock(&priv->rate_lock); - return ret; } From 750c61b2920f993eae36da32f6ea3071e7470e26 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:43 +0700 Subject: [PATCH 287/791] ASoC: codecs: max98095: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-13-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98095.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index ced9bd4d94da..c47bfa2378b8 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -5,6 +5,7 @@ * Copyright 2011 Maxim Integrated Products */ +#include #include #include #include @@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&max98095->lock); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); - m98095_eq_band(component, channel, 0, coef_set->band1); - m98095_eq_band(component, channel, 1, coef_set->band2); - m98095_eq_band(component, channel, 2, coef_set->band3); - m98095_eq_band(component, channel, 3, coef_set->band4); - m98095_eq_band(component, channel, 4, coef_set->band5); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&max98095->lock); + scoped_guard(mutex, &max98095->lock) { + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, M98095_SEG); + m98095_eq_band(component, channel, 0, coef_set->band1); + m98095_eq_band(component, channel, 1, coef_set->band2); + m98095_eq_band(component, channel, 2, coef_set->band3); + m98095_eq_band(component, channel, 3, coef_set->band4); + m98095_eq_band(component, channel, 4, coef_set->band5); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, 0); + } /* Restore the original on/off state */ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); @@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&max98095->lock); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); - m98095_biquad_band(component, channel, 0, coef_set->band1); - m98095_biquad_band(component, channel, 1, coef_set->band2); - snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&max98095->lock); + scoped_guard(mutex, &max98095->lock) { + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, M98095_SEG); + m98095_biquad_band(component, channel, 0, coef_set->band1); + m98095_biquad_band(component, channel, 1, coef_set->band2); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, + M98095_SEG, 0); + } /* Restore the original on/off state */ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); From 3ec678539a9fd98bc6100013d7900d22abd330f8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:44 +0700 Subject: [PATCH 288/791] ASoC: codecs: mt6359-accdet: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-14-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/mt6359-accdet.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c index ed34cc15b80e..e1190c644021 100644 --- a/sound/soc/codecs/mt6359-accdet.c +++ b/sound/soc/codecs/mt6359-accdet.c @@ -6,6 +6,7 @@ // Author: Argus Lin // +#include #include #include #include @@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) struct mt6359_accdet *priv = container_of(work, struct mt6359_accdet, accdet_work); - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); priv->pre_accdet_status = priv->accdet_status; check_jack_btn_type(priv); if (priv->jack_plugged && priv->pre_accdet_status != priv->accdet_status) mt6359_accdet_jack_report(priv); - mutex_unlock(&priv->res_lock); } static void mt6359_accdet_jd_work(struct work_struct *work) @@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) struct mt6359_accdet *priv = container_of(work, struct mt6359_accdet, jd_work); - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); if (priv->jd_sts == M_PLUG_IN) { priv->jack_plugged = true; @@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) if (priv->caps & ACCDET_PMIC_EINT_IRQ) recover_eint_setting(priv); - mutex_unlock(&priv->res_lock); } static irqreturn_t mt6359_accdet_irq(int irq, void *data) @@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) unsigned int irq_val = 0, val = 0, value = 0; int ret; - mutex_lock(&priv->res_lock); + guard(mutex)(&priv->res_lock); regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); if (irq_val & ACCDET_IRQ_MASK_SFT) { @@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) 1000); if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) if (ret) { dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); - mutex_unlock(&priv->res_lock); return IRQ_NONE; } regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, @@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) queue_work(priv->jd_workqueue, &priv->jd_work); } - mutex_unlock(&priv->res_lock); return IRQ_HANDLED; } From b812b16661e860dcc5be4d191c3b27db4c3bc481 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:45 +0700 Subject: [PATCH 289/791] ASoC: codecs: pcm512x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-15-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm512x.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 10868df3c75d..bf143e3fec54 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -6,7 +6,7 @@ * Copyright 2014 Linaro Ltd */ - +#include #include #include #include @@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); - mutex_unlock(&pcm512x->mutex); return 0; } @@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); int ret, changed = 0; - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { pcm512x->mute ^= 0x4; @@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, if (ret != 0) { dev_err(component->dev, "Failed to update digital mute: %d\n", ret); - mutex_unlock(&pcm512x->mutex); return ret; } } - mutex_unlock(&pcm512x->mutex); - return changed; } @@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) int ret; unsigned int mute_det; - mutex_lock(&pcm512x->mutex); + guard(mutex)(&pcm512x->mutex); if (mute) { pcm512x->mute |= 0x1; @@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) if (ret != 0) { dev_err(component->dev, "Failed to set digital mute: %d\n", ret); - goto unlock; + return ret; } regmap_read_poll_timeout(pcm512x->regmap, @@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) if (ret != 0) { dev_err(component->dev, "Failed to update digital mute: %d\n", ret); - goto unlock; + return ret; } regmap_read_poll_timeout(pcm512x->regmap, @@ -1499,9 +1495,6 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) 200, 10000); } -unlock: - mutex_unlock(&pcm512x->mutex); - return ret; } From f3da3b7f6ed247f4475855d38a55b1394e5eeb08 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:46 +0700 Subject: [PATCH 290/791] ASoC: codecs: pcm6240: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-16-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm6240.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 4ac4448ac3c8..a2b66eae6ac4 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -12,6 +12,7 @@ // Author: Shenghao Ding // +#include #include #include #include @@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, unsigned int reg = mc->reg; unsigned int val; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); if (pcm_dev->chip_id == PCM1690) { ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, @@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, if (ret) { dev_err(pcm_dev->dev, "%s: read mode err=%d\n", __func__, ret); - goto out; + return ret; } val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; /* Set to wide-range mode, before using vol ctrl. */ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { ucontrol->value.integer.value[0] = -25500; - goto out; + return ret; } /* Set to fine mode, before using fine vol ctrl. */ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { ucontrol->value.integer.value[0] = -12750; - goto out; + return ret; } } @@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, if (ret) { dev_err(pcm_dev->dev, "%s: read err=%d\n", __func__, ret); - goto out; + return ret; } val = (val >> shift) & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; ucontrol->value.integer.value[0] = val; -out: - mutex_unlock(&pcm_dev->codec_lock); + return ret; } @@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val, val_mask; unsigned int reg = mc->reg; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); val = ucontrol->value.integer.value[0] & mask; val = (val > max) ? max : val; val = mc->invert ? max - val : val; @@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, __func__, rc); else rc = 1; - mutex_unlock(&pcm_dev->codec_lock); + return rc; } @@ -1645,9 +1645,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) if (!pcm_dev) return; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcmdevice_config_info_remove(pcm_dev); - mutex_unlock(&pcm_dev->codec_lock); } static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { @@ -1890,9 +1889,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) else block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); - mutex_unlock(&pcm_dev->codec_lock); + return 0; } From f7a116261d6ba3b4d683539a4e9b5780f9a4ded8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:47 +0700 Subject: [PATCH 291/791] ASoC: codecs: peb2466: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Herve Codina Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-17-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/peb2466.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 2d71d204d8fa..5a1ed02abb84 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -6,6 +6,7 @@ // // Author: Herve Codina +#include #include #include #include @@ -1704,13 +1705,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x * So, a specific cache value is used. */ - mutex_lock(&peb2466->gpio.lock); + guard(mutex)(&peb2466->gpio.lock); cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); - if (!cache) { - ret = -EINVAL; - goto end; - } + if (!cache) + return -EINVAL; tmp = *cache; tmp &= ~mask; @@ -1718,14 +1717,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x ret = regmap_write(peb2466->regmap, xr_reg, tmp); if (ret) - goto end; + return ret; *cache = tmp; - ret = 0; -end: - mutex_unlock(&peb2466->gpio.lock); - return ret; + return 0; } static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, From ed2e18138f1849bcc573daf4f9b80ee6f0dc394c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:48 +0700 Subject: [PATCH 292/791] ASoC: codecs: rt5514-spi: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-18-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5514-spi.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 199507d12841..91290bfe8daa 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -6,6 +6,7 @@ * Author: Oder Chiou */ +#include #include #include #include @@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) unsigned int cur_wp, remain_data; u8 buf[8]; - mutex_lock(&rt5514_dsp->dma_lock); + guard(mutex)(&rt5514_dsp->dma_lock); if (!rt5514_dsp->substream) { dev_err(rt5514_dsp->dev, "No pcm substream\n"); - goto done; + return; } runtime = rt5514_dsp->substream->runtime; period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); if (!period_bytes) { schedule_delayed_work(&rt5514_dsp->copy_work, 5); - goto done; + return; } if (rt5514_dsp->buf_size % period_bytes) @@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) if (remain_data < period_bytes) { schedule_delayed_work(&rt5514_dsp->copy_work, 5); - goto done; + return; } } @@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) snd_pcm_period_elapsed(rt5514_dsp->substream); schedule_delayed_work(&rt5514_dsp->copy_work, 5); - -done: - mutex_unlock(&rt5514_dsp->dma_lock); } static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) @@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, snd_soc_component_get_drvdata(component); u8 buf[8]; - mutex_lock(&rt5514_dsp->dma_lock); + guard(mutex)(&rt5514_dsp->dma_lock); rt5514_dsp->substream = substream; rt5514_dsp->dma_offset = 0; @@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, if (buf[0] & RT5514_IRQ_STATUS_BIT) rt5514_schedule_copy(rt5514_dsp); - mutex_unlock(&rt5514_dsp->dma_lock); - return 0; } @@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5514_dsp->dma_lock); - rt5514_dsp->substream = NULL; - mutex_unlock(&rt5514_dsp->dma_lock); + scoped_guard(mutex, &rt5514_dsp->dma_lock) + rt5514_dsp->substream = NULL; cancel_delayed_work_sync(&rt5514_dsp->copy_work); From 1937a1e0969e575148412f3f0af8a5a963f2944d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:49 +0700 Subject: [PATCH 293/791] ASoC: codecs: rt5645: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-19-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 164 +++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 83 deletions(-) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 93a4148ccccd..0d546fb34e52 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -6,6 +6,7 @@ * Author: Bard Liao */ +#include #include #include #include @@ -3323,92 +3324,89 @@ static void rt5645_jack_detect_work(struct work_struct *work) if (!rt5645->component) return; - mutex_lock(&rt5645->jd_mutex); - - switch (rt5645->pdata.jd_mode) { - case 0: /* Not using rt5645 JD */ - if (rt5645->gpiod_hp_det) { - gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); - if (rt5645->pdata.inv_hp_pol) - gpio_state ^= 1; - dev_dbg(rt5645->component->dev, "gpio_state = %d\n", - gpio_state); - report = rt5645_jack_detect(rt5645->component, gpio_state); - } - snd_soc_jack_report(rt5645->hp_jack, - report, SND_JACK_HEADPHONE); - snd_soc_jack_report(rt5645->mic_jack, - report, SND_JACK_MICROPHONE); - mutex_unlock(&rt5645->jd_mutex); - return; - case 4: - val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; - break; - default: /* read rt5645 jd1_1 status */ - val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; - break; - - } - - if (!val && (rt5645->jack_type == 0)) { /* jack in */ - report = rt5645_jack_detect(rt5645->component, 1); - } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { - /* for push button and jack out */ - btn_type = 0; - if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { - /* button pressed */ - report = SND_JACK_HEADSET; - btn_type = rt5645_button_detect(rt5645->component); - /* rt5650 can report three kinds of button behavior, - one click, double click and hold. However, - currently we will report button pressed/released - event. So all the three button behaviors are - treated as button pressed. */ - switch (btn_type) { - case 0x8000: - case 0x4000: - case 0x2000: - report |= SND_JACK_BTN_0; - break; - case 0x1000: - case 0x0800: - case 0x0400: - report |= SND_JACK_BTN_1; - break; - case 0x0200: - case 0x0100: - case 0x0080: - report |= SND_JACK_BTN_2; - break; - case 0x0040: - case 0x0020: - case 0x0010: - report |= SND_JACK_BTN_3; - break; - case 0x0000: /* unpressed */ - break; - default: - dev_err(rt5645->component->dev, - "Unexpected button code 0x%04x\n", - btn_type); - break; + scoped_guard(mutex, &rt5645->jd_mutex) { + switch (rt5645->pdata.jd_mode) { + case 0: /* Not using rt5645 JD */ + if (rt5645->gpiod_hp_det) { + gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); + if (rt5645->pdata.inv_hp_pol) + gpio_state ^= 1; + dev_dbg(rt5645->component->dev, "gpio_state = %d\n", + gpio_state); + report = rt5645_jack_detect(rt5645->component, gpio_state); } + snd_soc_jack_report(rt5645->hp_jack, + report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, + report, SND_JACK_MICROPHONE); + return; + case 4: + val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; + break; + default: /* read rt5645 jd1_1 status */ + val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; + break; } - if (btn_type == 0)/* button release */ - report = rt5645->jack_type; - else { - mod_timer(&rt5645->btn_check_timer, - msecs_to_jiffies(100)); - } - } else { - /* jack out */ - report = 0; - snd_soc_component_update_bits(rt5645->component, - RT5645_INT_IRQ_ST, 0x1, 0x0); - rt5645_jack_detect(rt5645->component, 0); - } - mutex_unlock(&rt5645->jd_mutex); + if (!val && rt5645->jack_type == 0) { /* jack in */ + report = rt5645_jack_detect(rt5645->component, 1); + } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { + /* for push button and jack out */ + btn_type = 0; + if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5645_button_detect(rt5645->component); + /* + * rt5650 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5645->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5645->jack_type; + else + mod_timer(&rt5645->btn_check_timer, + msecs_to_jiffies(100)); + } else { + /* jack out */ + report = 0; + snd_soc_component_update_bits(rt5645->component, + RT5645_INT_IRQ_ST, 0x1, 0x0); + rt5645_jack_detect(rt5645->component, 0); + } + } snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); From f1c58069475cb2f7b14efa5a19ce0f041b60a43f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:50 +0700 Subject: [PATCH 294/791] ASoC: codecs: rt5665: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-20-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5665.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 48f57cd0920d..1d987472ba42 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -6,6 +6,7 @@ * Author: Bard Liao */ +#include #include #include #include @@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } - mutex_lock(&rt5665->calibrate_mutex); + guard(mutex)(&rt5665->calibrate_mutex); val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; if (!val) { @@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) schedule_delayed_work(&rt5665->jd_check_work, 0); else cancel_delayed_work_sync(&rt5665->jd_check_work); - - mutex_unlock(&rt5665->calibrate_mutex); } static const char * const rt5665_clk_sync[] = { @@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) { int value, count; - mutex_lock(&rt5665->calibrate_mutex); + guard(mutex)(&rt5665->calibrate_mutex); regcache_cache_bypass(rt5665->regmap, true); @@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) pr_err("HP Calibration Failure\n"); regmap_write(rt5665->regmap, RT5665_RESET, 0); regcache_cache_bypass(rt5665->regmap, false); - goto out_unlock; + rt5665->calibration_done = true; + return; } count++; @@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) pr_err("MONO Calibration Failure\n"); regmap_write(rt5665->regmap, RT5665_RESET, 0); regcache_cache_bypass(rt5665->regmap, false); - goto out_unlock; + rt5665->calibration_done = true; + return; } count++; @@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); -out_unlock: rt5665->calibration_done = true; - mutex_unlock(&rt5665->calibrate_mutex); } static void rt5665_calibrate_handler(struct work_struct *work) From c745a4e595c5fd0a5c793774359e1f1831a911ab Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:51 +0700 Subject: [PATCH 295/791] ASoC: codecs: rt5668: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-21-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5668.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index fed6de40b8c8..4154bad27610 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -6,6 +6,7 @@ * Author: Bard Liao */ +#include #include #include #include @@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) return; } - mutex_lock(&rt5668->calibrate_mutex); + guard(mutex)(&rt5668->calibrate_mutex); val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) & RT5668_JDH_RS_MASK; @@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) schedule_delayed_work(&rt5668->jd_check_work, 0); else cancel_delayed_work_sync(&rt5668->jd_check_work); - - mutex_unlock(&rt5668->calibrate_mutex); } static const struct snd_kcontrol_new rt5668_snd_controls[] = { @@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) { int value, count; - mutex_lock(&rt5668->calibrate_mutex); + guard(mutex)(&rt5668->calibrate_mutex); rt5668_reset(rt5668->regmap); regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); @@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) /* restore settings */ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); - - mutex_unlock(&rt5668->calibrate_mutex); - } static int rt5668_i2c_probe(struct i2c_client *i2c) From be03bd5c4c3404c50a585e8bc435cddbb1f21bef Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:52 +0700 Subject: [PATCH 296/791] ASoC: codecs: rt5677: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-22-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677-spi.c | 36 +++++++---------- sound/soc/codecs/rt5677.c | 75 ++++++++++++++++------------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 1bcafd5f4468..ebc527115ea5 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -6,6 +6,7 @@ * Author: Oder Chiou */ +#include #include #include #include @@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( struct rt5677_dsp *rt5677_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); rt5677_dsp->substream = substream; - mutex_unlock(&rt5677_dsp->dma_lock); return 0; } @@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( struct rt5677_dsp *rt5677_dsp = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); rt5677_dsp->substream = NULL; - mutex_unlock(&rt5677_dsp->dma_lock); return 0; } @@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) int ret = 0; /* Ensure runtime->dma_area buffer does not go away while copying. */ - mutex_lock(&rt5677_dsp->dma_lock); + guard(mutex)(&rt5677_dsp->dma_lock); if (!rt5677_dsp->substream) { dev_err(rt5677_dsp->dev, "No pcm substream\n"); - goto done; + return; } runtime = rt5677_dsp->substream->runtime; if (rt5677_spi_mic_write_offset(&mic_write_offset)) { dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); - goto done; + return; } /* If this is the first time that we've asked for streaming data after @@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); if (ret) { dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); - goto done; + return; } rt5677_dsp->avail_bytes += copy_bytes; if (rt5677_dsp->avail_bytes >= period_bytes) { @@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); -done: - mutex_unlock(&rt5677_dsp->dma_lock); } static int rt5677_spi_pcm_new(struct snd_soc_component *component, @@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) header[3] = ((addr + offset) & 0x0000ff00) >> 8; header[4] = ((addr + offset) & 0x000000ff) >> 0; - mutex_lock(&spi_mutex); - status |= spi_sync(g_spi, &m); - mutex_unlock(&spi_mutex); - + scoped_guard(mutex, &spi_mutex) + status |= spi_sync(g_spi, &m); /* Copy data back to caller buffer */ rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); @@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) offset += t.len; t.len += RT5677_SPI_HEADER + 1; - mutex_lock(&spi_mutex); - status |= spi_sync(g_spi, &m); - mutex_unlock(&spi_mutex); + scoped_guard(mutex, &spi_mutex) + status |= spi_sync(g_spi, &m); } return status; } @@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) return; } - mutex_lock(&rt5677_dsp->dma_lock); - dev_info(rt5677_dsp->dev, "Hotword detected\n"); - rt5677_dsp->new_hotword = true; - mutex_unlock(&rt5677_dsp->dma_lock); + scoped_guard(mutex, &rt5677_dsp->dma_lock) { + dev_info(rt5677_dsp->dev, "Hotword detected\n"); + rt5677_dsp->new_hotword = true; + } schedule_delayed_work(&rt5677_dsp->copy_work, 0); } diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 73fc008d558a..3e4d1dbce740 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -6,6 +6,7 @@ * Author: Oder Chiou */ +#include #include #include #include @@ -564,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, struct snd_soc_component *component = rt5677->component; int ret; - mutex_lock(&rt5677->dsp_cmd_lock); + guard(mutex)(&rt5677->dsp_cmd_lock); ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, value >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set data msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, value & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, opcode); if (ret < 0) { dev_err(component->dev, "Failed to set op code value: %d\n", ret); - goto err; + return ret; } -err: - mutex_unlock(&rt5677->dsp_cmd_lock); - return ret; } @@ -623,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( int ret; unsigned int msb, lsb; - mutex_lock(&rt5677->dsp_cmd_lock); + guard(mutex)(&rt5677->dsp_cmd_lock); ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); if (ret < 0) { dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); - goto err; + return ret; } ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, 0x0002); if (ret < 0) { dev_err(component->dev, "Failed to set op code value: %d\n", ret); - goto err; + return ret; } regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); *value = (msb << 16) | lsb; -err: - mutex_unlock(&rt5677->dsp_cmd_lock); - return ret; } @@ -941,21 +936,20 @@ static void rt5677_dsp_work(struct work_struct *work) activity = false; /* Don't turn off the DSP while handling irqs */ - mutex_lock(&rt5677->irq_lock); - /* Set DSP CPU to Stop */ - regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, - RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); + scoped_guard(mutex, &rt5677->irq_lock) { + /* Set DSP CPU to Stop */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); - rt5677_set_dsp_mode(rt5677, false); + rt5677_set_dsp_mode(rt5677, false); - /* Disable and clear VAD interrupt */ - regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); + /* Disable and clear VAD interrupt */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - /* Set GPIO1 pin back to be IRQ output for jack detect */ - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, - RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); - - mutex_unlock(&rt5677->irq_lock); + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + } } } @@ -4997,11 +4991,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) if (rt5677->is_dsp_mode) { if (reg > 0xff) { - mutex_lock(&rt5677->dsp_pri_lock); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, - reg & 0xff); - rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); - mutex_unlock(&rt5677->dsp_pri_lock); + scoped_guard(mutex, &rt5677->dsp_pri_lock) { + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); + } } else { rt5677_dsp_mode_i2c_read(rt5677, reg, val); } @@ -5019,12 +5013,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) if (rt5677->is_dsp_mode) { if (reg > 0xff) { - mutex_lock(&rt5677->dsp_pri_lock); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, - reg & 0xff); - rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, - val); - mutex_unlock(&rt5677->dsp_pri_lock); + scoped_guard(mutex, &rt5677->dsp_pri_lock) { + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, + val); + } } else { rt5677_dsp_mode_i2c_write(rt5677, reg, val); } @@ -5416,7 +5410,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) * Without this explicit check, unplug the headset right after suspend * starts, then after resume the headset is still shown as plugged in. */ - mutex_lock(&rt5677->irq_lock); + guard(mutex)(&rt5677->irq_lock); for (i = 0; i < RT5677_IRQ_NUM; i++) { if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { virq = irq_find_mapping(rt5677->domain, i); @@ -5424,7 +5418,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) handle_nested_irq(virq); } } - mutex_unlock(&rt5677->irq_lock); } static void rt5677_irq_bus_lock(struct irq_data *data) From 97ed7ddb98d67167e422954a4378a6200d73769f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:53 +0700 Subject: [PATCH 297/791] ASoC: codecs: rt5682: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-23-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682-sdw.c | 24 ++++++++++++------------ sound/soc/codecs/rt5682.c | 5 ++--- sound/soc/codecs/rt5682s.c | 17 +++++------------ 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index e49ec28b6103..23c7d2a9dbee 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -6,6 +6,7 @@ // Author: Oder Chiou // +#include #include #include #include @@ -670,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt5682->disable_irq_lock); + guard(mutex)(&rt5682->disable_irq_lock); if (status->control_port & 0x4 && !rt5682->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); } - mutex_unlock(&rt5682->disable_irq_lock); return 0; } @@ -746,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt5682->disable_irq_lock); - rt5682->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt5682->disable_irq_lock); + scoped_guard(mutex, &rt5682->disable_irq_lock) { + rt5682->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ @@ -770,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt5682->disable_irq_lock); - if (rt5682->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); - rt5682->disable_irq = false; + scoped_guard(mutex, &rt5682->disable_irq_lock) { + if (rt5682->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt5682->disable_irq = false; + } } - mutex_unlock(&rt5682->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 4b82e07d3b2c..6f7e68c20d13 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -6,6 +6,7 @@ // Author: Bard Liao // +#include #include #include #include @@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) { int value, count; - mutex_lock(&rt5682->calibrate_mutex); + guard(mutex)(&rt5682->calibrate_mutex); rt5682_reset(rt5682); regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); @@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); - - mutex_unlock(&rt5682->calibrate_mutex); } EXPORT_SYMBOL_GPL(rt5682_calibrate); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 3624067950c0..34997cfb465b 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -6,6 +6,7 @@ // Author: Derek Fang // +#include #include #include #include @@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode { struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); - mutex_lock(&rt5682s->sar_mutex); + guard(mutex)(&rt5682s->sar_mutex); switch (mode) { case SAR_PWR_SAVING: @@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); break; } - - mutex_unlock(&rt5682s->sar_mutex); } static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) @@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) if (!rt5682s_clk_check(rt5682s)) return -EINVAL; - mutex_lock(&rt5682s->wclk_mutex); + guard(mutex)(&rt5682s->wclk_mutex); snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, @@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) rt5682s->wclk_enabled = 1; - mutex_unlock(&rt5682s->wclk_mutex); - return 0; } @@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) if (!rt5682s_clk_check(rt5682s)) return; - mutex_lock(&rt5682s->wclk_mutex); + guard(mutex)(&rt5682s->wclk_mutex); if (!rt5682s->jack_type) snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, @@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) rt5682s_set_pllb_power(rt5682s, 0); rt5682s->wclk_enabled = 0; - - mutex_unlock(&rt5682s->wclk_mutex); } static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, @@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) { unsigned int count, value; - mutex_lock(&rt5682s->calibrate_mutex); + guard(mutex)(&rt5682s->calibrate_mutex); regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); usleep_range(15000, 20000); @@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); - - mutex_unlock(&rt5682s->calibrate_mutex); } static const struct regmap_config rt5682s_regmap = { From 5d649ea1d05389e9aee3e38cb0349127bae7ecec Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:54 +0700 Subject: [PATCH 298/791] ASoC: codecs: rt700: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-24-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index bb449f08e30c..3ba0cc37acd9 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt700->disable_irq_lock); + guard(mutex)(&rt700->disable_irq_lock); if (status->control_port & 0x4 && !rt700->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt700->jack_detect_work, msecs_to_jiffies(250)); } - mutex_unlock(&rt700->disable_irq_lock); return 0; } @@ -499,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt700->disable_irq_lock); - rt700->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt700->disable_irq_lock); + scoped_guard(mutex, &rt700->disable_irq_lock) { + rt700->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ From f8e861f73b904c063f1a189c33a270a564fbe49a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:55 +0700 Subject: [PATCH 299/791] ASoC: codecs: rt711: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-25-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdca-sdw.c | 29 ++++++++++-------- sound/soc/codecs/rt711-sdca.c | 5 ++-- sound/soc/codecs/rt711-sdw.c | 24 +++++++-------- sound/soc/codecs/rt711.c | 50 +++++++++++++++---------------- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index e028a1c3a9ac..e292b28b029e 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt711_sdca->disable_irq_lock); - rt711_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt711_sdca->disable_irq_lock); + scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { + rt711_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt711->disable_irq_lock); - if (rt711->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt711->disable_irq = false; + scoped_guard(mutex, &rt711->disable_irq_lock) { + if (rt711->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt711->disable_irq = false; + } } - mutex_unlock(&rt711->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 3a26c782d800..9781f289f8ad 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -450,7 +451,7 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) { - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); if (rt711->hs_jack) { /* Enable HID1 event & set button RTC mode */ @@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); } - - mutex_unlock(&rt711->calibrate_mutex); } static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index a0c6a9efa840..4ad2b1da1954 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); - mutex_lock(&rt711->disable_irq_lock); + guard(mutex)(&rt711->disable_irq_lock); if (status->control_port & 0x4 && !rt711->disable_irq) { mod_delayed_work(system_power_efficient_wq, &rt711->jack_detect_work, msecs_to_jiffies(250)); } - mutex_unlock(&rt711->disable_irq_lock); return 0; } @@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt711->disable_irq_lock); - rt711->disable_irq = true; - ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, - SDW_SCP_INT1_IMPL_DEF, 0); - mutex_unlock(&rt711->disable_irq_lock); + scoped_guard(mutex, &rt711->disable_irq_lock) { + rt711->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + } if (ret < 0) { /* log but don't prevent suspend from happening */ @@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt711->disable_irq_lock); - if (rt711->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); - rt711->disable_irq = false; + scoped_guard(mutex, &rt711->disable_irq_lock) { + if (rt711->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt711->disable_irq = false; + } } - mutex_unlock(&rt711->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 5dbe9b67703e..3e49c3d0f25b 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) struct regmap *regmap = rt711->regmap; int ret = 0; - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); dev = regmap_get_device(regmap); /* Calibration manual mode */ rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, - 0xf, 0x0); + 0xf, 0x0); /* trigger */ rt711_index_update_bits(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, - RT711_DAC_DC_CALI_TRIGGER); + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); /* wait for calibration process */ rt711_index_read(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, &val); + RT711_DAC_DC_CALI_CTL1, &val); while (val & RT711_DAC_DC_CALI_TRIGGER) { if (loop >= 500) { @@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) usleep_range(10000, 11000); rt711_index_read(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, &val); + RT711_DAC_DC_CALI_CTL1, &val); } /* depop mode */ rt711_index_update_bits(regmap, RT711_VENDOR_REG, - RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); return ret; @@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); /* power on */ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); if (rt711->hs_jack) { /* unsolicited response & IRQ control */ regmap_write(rt711->regmap, - RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); regmap_write(rt711->regmap, - RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); regmap_write(rt711->regmap, - RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); rt711_index_write(rt711->regmap, RT711_VENDOR_REG, - 0x10, 0x2420); + 0x10, 0x2420); rt711_index_write(rt711->regmap, RT711_VENDOR_REG, - 0x19, 0x2e11); + 0x19, 0x2e11); switch (rt711->jd_src) { case RT711_JD1: @@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) /* power off */ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); } static int rt711_set_jack_detect(struct snd_soc_component *component, @@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, unsigned int read_ll, read_rl; int i; - mutex_lock(&rt711->calibrate_mutex); + guard(mutex)(&rt711->calibrate_mutex); /* Can't use update bit function, so read the original value first */ addr_h = mc->reg; @@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); return 0; } @@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: - mutex_lock(&rt711->calibrate_mutex); - regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, - AC_PWRST_D3); - mutex_unlock(&rt711->calibrate_mutex); + scoped_guard(mutex, &rt711->calibrate_mutex) { + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + } break; default: From cd1c99ff8cf7682bb2ac92973d1da2db01975cf7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:56 +0700 Subject: [PATCH 300/791] ASoC: codecs: rt712-sdca-sdw: Simplify regcache error handling in resume Calling regcache_mark_dirty() on error is redundant as regcache_sync() retains dirty state on failure, and any write in cache_only mode marks the cache dirty anyway. Restore cache_only directly on error for active regmaps and drop the redundant regcache_mark_dirty() calls. This also removes goto labels to prepare for guard cleanup. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-26-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index ebdf7b308331..e23501ed87a7 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -473,22 +473,20 @@ static int rt712_sdca_dev_resume(struct device *dev) regcache_cache_only(rt712->regmap, false); ret = regcache_sync(rt712->regmap); - if (ret) - goto err_sync; + if (ret) { + regcache_cache_only(rt712->regmap, true); + return ret; + } regcache_cache_only(rt712->mbq_regmap, false); ret = regcache_sync(rt712->mbq_regmap); - if (ret) - goto err_sync; + if (ret) { + regcache_cache_only(rt712->mbq_regmap, true); + regcache_cache_only(rt712->regmap, true); + return ret; + } return 0; - -err_sync: - regcache_cache_only(rt712->regmap, true); - regcache_cache_only(rt712->mbq_regmap, true); - regcache_mark_dirty(rt712->regmap); - regcache_mark_dirty(rt712->mbq_regmap); - return ret; } static const struct dev_pm_ops rt712_sdca_pm = { From 32ac6377cec13e0b1727507cf6cd9ff808a81e42 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:57 +0700 Subject: [PATCH 301/791] ASoC: codecs: rt712: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-27-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 30 ++++++++++++++++-------------- sound/soc/codecs/rt712-sdca.c | 5 ++--- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index e23501ed87a7..c50e74e20a88 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt712_sdca->disable_irq_lock); - rt712_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt712_sdca->disable_irq_lock); + scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { + rt712_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt712->disable_irq_lock); - if (rt712->disable_irq == true) { - - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt712->disable_irq = false; + scoped_guard(mutex, &rt712->disable_irq_lock) { + if (rt712->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt712->disable_irq = false; + } } - mutex_unlock(&rt712->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index d6353af07380..2218f9918ae3 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -7,6 +7,7 @@ // #include +#include #include #include #include @@ -403,7 +404,7 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) { - mutex_lock(&rt712->calibrate_mutex); + guard(mutex)(&rt712->calibrate_mutex); if (rt712->hs_jack) { /* Enable HID1 event & set button RTC mode */ @@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); } - - mutex_unlock(&rt712->calibrate_mutex); } static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, From 2df329044c614fce2d0f4f46ddba276ec3aa4ae5 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:58 +0700 Subject: [PATCH 302/791] ASoC: codecs: rt721-sdca-sdw: Simplify regcache error handling in resume Calling regcache_mark_dirty() on error is redundant as regcache_sync() retains dirty state on failure, and any write in cache_only mode marks the cache dirty anyway. Restore cache_only directly on error for active regmaps and drop the redundant regcache_mark_dirty() calls. This also removes goto labels to prepare for guard cleanup. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-28-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt721-sdca-sdw.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 6e27e761afb6..6411df4eede7 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -511,22 +511,20 @@ static int rt721_sdca_dev_resume(struct device *dev) regcache_cache_only(rt721->regmap, false); ret = regcache_sync(rt721->regmap); - if (ret) - goto err_sync; + if (ret) { + regcache_cache_only(rt721->regmap, true); + return ret; + } regcache_cache_only(rt721->mbq_regmap, false); ret = regcache_sync(rt721->mbq_regmap); - if (ret) - goto err_sync; + if (ret) { + regcache_cache_only(rt721->mbq_regmap, true); + regcache_cache_only(rt721->regmap, true); + return ret; + } return 0; - -err_sync: - regcache_cache_only(rt721->regmap, true); - regcache_cache_only(rt721->mbq_regmap, true); - regcache_mark_dirty(rt721->regmap); - regcache_mark_dirty(rt721->mbq_regmap); - return ret; } static const struct dev_pm_ops rt721_sdca_pm = { From 219fbaa1b6d1b9ac2e8bc29fc2e904d51c6f8b17 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:25:59 +0700 Subject: [PATCH 303/791] ASoC: codecs: rt721: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-29-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt721-sdca-sdw.c | 29 ++++++++++++++++------------- sound/soc/codecs/rt721-sdca.c | 5 ++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 6411df4eede7..eae7d662efae 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt721_sdca->disable_irq_lock); - rt721_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt721_sdca->disable_irq_lock); + scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { + rt721_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt721->disable_irq_lock); - if (rt721->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt721->disable_irq = false; + scoped_guard(mutex, &rt721->disable_irq_lock) { + if (rt721->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt721->disable_irq = false; + } } - mutex_unlock(&rt721->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index 35960c225224..159c35d19dba 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -5,7 +5,7 @@ // Copyright(c) 2024 Realtek Semiconductor Corp. // // - +#include #include #include #include @@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) { - mutex_lock(&rt721->calibrate_mutex); + guard(mutex)(&rt721->calibrate_mutex); if (rt721->hs_jack) { sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); @@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, RT721_GE_REL_CTRL1, 0x4000, 0x4000); } - mutex_unlock(&rt721->calibrate_mutex); } static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, From cce043d552cde5ee71c9261e0ad9789209275e31 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 21 Jul 2026 17:26:00 +0700 Subject: [PATCH 304/791] ASoC: codecs: rt722: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260721102600.523199-30-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca-sdw.c | 29 ++++++++++++++++------------- sound/soc/codecs/rt722-sdca.c | 4 ++-- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index d2db33b4f684..05e8e23f1f43 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -513,13 +514,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) * deferred work completes and before the parent disables * interrupts on the link */ - mutex_lock(&rt722_sdca->disable_irq_lock); - rt722_sdca->disable_irq = true; - ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0, 0); - ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_8, 0); - mutex_unlock(&rt722_sdca->disable_irq_lock); + scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + } if (ret1 < 0 || ret2 < 0) { /* log but don't prevent suspend from happening */ @@ -541,13 +542,15 @@ static int rt722_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { - mutex_lock(&rt722->disable_irq_lock); - if (rt722->disable_irq == true) { - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); - sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); - rt722->disable_irq = false; + scoped_guard(mutex, &rt722->disable_irq_lock) { + if (rt722->disable_irq) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + rt722->disable_irq = false; + } } - mutex_unlock(&rt722->disable_irq_lock); } ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 1b6729f363fc..decf9407ab6d 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -6,6 +6,7 @@ // // +#include #include #include #include @@ -294,7 +295,7 @@ static void rt722_sdca_btn_check_handler(struct work_struct *work) static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) { - mutex_lock(&rt722->calibrate_mutex); + guard(mutex)(&rt722->calibrate_mutex); if (rt722->hs_jack) { /* set SCP_SDCA_IntMask1[0]=1 */ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, @@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, 0x4000, 0x4000); } - mutex_unlock(&rt722->calibrate_mutex); } static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, From 13651ae6548d8957b75bff0c8d1239beff8a9394 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 04:11:51 +0000 Subject: [PATCH 305/791] ASoC: meson: axg-card: tidyup not to use card->dev struct snd_soc_card will be capsuled soon, its member will not be able to access from non soc-card.c. To reduce the difference during conversion, replace dev. - card->dev, ... + dev, ... No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/87a4ritke0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-card.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index b4dca80e15e4..c36f727af4ba 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, struct snd_soc_dai_link *pad; struct snd_soc_dai_link *lb; struct snd_soc_dai_link_component *dlc; + struct device *dev = card->dev; int ret; /* extend links */ @@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, pad = &card->dai_link[*index]; lb = &card->dai_link[*index + 1]; - lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); + lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); if (!lb->name) return -ENOMEM; - dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); + dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; @@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, struct device_node *node, struct axg_dai_link_tdm_data *be) { + struct device *dev = card->dev; char propname[32]; u32 tx, rx; int i; - be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, sizeof(*be->tx_mask), GFP_KERNEL); - be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, sizeof(*be->rx_mask), GFP_KERNEL); if (!be->tx_mask || !be->rx_mask) return -ENOMEM; @@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, /* ... but the interface should at least have one direction */ if (!tx && !rx) { - dev_err(card->dev, "tdm link has no cpu slots\n"); + dev_err(dev, "tdm link has no cpu slots\n"); return -EINVAL; } @@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, * Error if the slots can't accommodate the largest mask or * if it is just too big */ - dev_err(card->dev, "bad slot number\n"); + dev_err(dev, "bad slot number\n"); return -EINVAL; } @@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, struct axg_dai_link_tdm_data *be) { struct axg_dai_link_tdm_mask *codec_mask; + struct device *dev = card->dev; - codec_mask = devm_kcalloc(card->dev, link->num_codecs, + codec_mask = devm_kcalloc(dev, link->num_codecs, sizeof(*codec_mask), GFP_KERNEL); if (!codec_mask) return -ENOMEM; @@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *link = &card->dai_link[*index]; struct axg_dai_link_tdm_data *be; + struct device *dev = card->dev; int ret; /* Allocate tdm link parameters */ - be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); if (!be) return -ENOMEM; priv->link_data[*index] = be; @@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); if (ret) { - dev_err(card->dev, "error parsing tdm link slots\n"); + dev_err(dev, "error parsing tdm link slots\n"); return ret; } @@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, { struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; struct snd_soc_dai_link_component *cpu; + struct device *dev = card->dev; int ret; - cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); + cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); if (!cpu) return -ENOMEM; From 138a0faeaca4384e0324865c1e41b199e5f8d406 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 04:11:55 +0000 Subject: [PATCH 306/791] ASoC: meson: gx-card: tidyup not to use card->dev struct snd_soc_card will be capsuled soon, its member will not be able to access from non soc-card.c. To reduce the difference during conversion, replace dev. - card->dev, ... + dev, ... No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/878q72tkdw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/gx-card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c index b408cc2bbc91..932be0a09306 100644 --- a/sound/soc/meson/gx-card.c +++ b/sound/soc/meson/gx-card.c @@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, struct meson_card *priv = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *link = &card->dai_link[*index]; struct gx_dai_link_i2s_data *be; + struct device *dev = card->dev; /* Allocate i2s link parameters */ - be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); if (!be) return -ENOMEM; priv->link_data[*index] = be; @@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, { struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; struct snd_soc_dai_link_component *cpu; + struct device *dev = card->dev; int ret; - cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); + cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); if (!cpu) return -ENOMEM; From 6c3042f65f20b3d7b8972d4882a9b5339bc4fd3e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 04:11:59 +0000 Subject: [PATCH 307/791] ASoC: meson: meson-card-utils: tidyup not to use card->dev struct snd_soc_card will be capsuled soon, its member will not be able to access from non soc-card.c. To reduce the difference during conversion, replace dev. - card->dev, ... + dev, ... No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/877bmmtkds.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/meson-card-utils.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index cdb759b466ad..be05fba2df70 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -76,6 +76,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, struct device_node *node, struct snd_soc_dai_link_component *dlc) { + struct device *dev = card->dev; int ret; if (!dlc || !node) @@ -83,7 +84,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); if (ret) - return dev_err_probe(card->dev, ret, "can't parse dai\n"); + return dev_err_probe(dev, ret, "can't parse dai\n"); return ret; } @@ -94,7 +95,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, struct device_node *node, const char *prefix) { - char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + struct device *dev = card->dev; + char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", prefix, node->full_name); if (!name) return -ENOMEM; @@ -137,16 +139,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, struct device_node *node) { struct snd_soc_dai_link_component *codec; + struct device *dev = card->dev; int ret, num_codecs; num_codecs = of_get_child_count(node); if (!num_codecs) { - dev_err(card->dev, "be link %s has no codec\n", + dev_err(dev, "be link %s has no codec\n", node->full_name); return -EINVAL; } - codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); if (!codec) return -ENOMEM; @@ -163,7 +166,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, ret = meson_card_set_link_name(card, link, node, "be"); if (ret) - dev_err(card->dev, "error setting %pOFn link name\n", node); + dev_err(dev, "error setting %pOFn link name\n", node); return ret; } @@ -194,12 +197,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); static int meson_card_add_links(struct snd_soc_card *card) { struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct device_node *node = card->dev->of_node; + struct device *dev = card->dev; + struct device_node *node = dev->of_node; int num, i, ret; num = of_get_child_count(node); if (!num) { - dev_err(card->dev, "card has no links\n"); + dev_err(dev, "card has no links\n"); return -EINVAL; } @@ -224,8 +228,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, int (*func)(struct snd_soc_card *c, const char *p)) { + struct device *dev = card->dev; + /* If property is not provided, don't fail ... */ - if (!of_property_present(card->dev->of_node, propname)) + if (!of_property_present(dev->of_node, propname)) return 0; /* ... but do fail if it is provided and the parsing fails */ From 3a065257412f612d4f3fe538003c4520d6e4e4c0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 21 Jul 2026 15:59:36 -0700 Subject: [PATCH 308/791] ASoC: fsl: mpc5200_dma: use platform helpers and devm cleanup Convert mpc5200_audio_dma_create() to the managed APIs. Replace the open-coded of_address_to_resource() + devm_ioremap() of the PSC registers with devm_platform_get_and_ioremap_resource(), and irq_of_parse_and_map() with platform_get_irq() (which returns a negative errno instead of 0). Switch the allocation to devm_kzalloc(), the three interrupt requests to devm_request_irq(), and drop the now-unneeded error-path cleanup and the manual teardown in mpc5200_audio_dma_destroy(). The PSC register window is owned solely by this driver, so the new region request from devm_platform_get_and_ioremap_resource() cannot conflict with another claimant, and it is mapped exactly once (no double mapping). The resource pointer is still used (res->start) to compute the FIFO physical address. No functional change; built for powerpc (allmodconfig + CONFIG_SND_SOC_MPC5200_DMA) with LLVM=1 and sound/soc/fsl/mpc5200_dma.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260721225936.838299-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_dma.c | 58 ++++++++++---------------------- sound/soc/fsl/mpc5200_psc_ac97.c | 4 +-- sound/soc/fsl/mpc5200_psc_i2s.c | 3 +- 3 files changed, 20 insertions(+), 45 deletions(-) diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index bfedb2dea0b3..8327fff3e1b5 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) { phys_addr_t fifo; struct psc_dma *psc_dma; - struct resource res; + struct resource *res; int size, irq, rc; const __be32 *prop; void __iomem *regs; - int ret; + + regs = devm_platform_get_and_ioremap_resource(op, 0, &res); + if (IS_ERR(regs)) + return PTR_ERR(regs); /* Fetch the registers and IRQ of the PSC */ - irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (of_address_to_resource(op->dev.of_node, 0, &res)) { - dev_err(&op->dev, "Missing reg property\n"); - return -ENODEV; - } - regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); - if (!regs) { - dev_err(&op->dev, "Could not map registers\n"); - return -ENODEV; - } + irq = platform_get_irq(op, 0); + if (irq < 0) + return irq; /* Allocate and initialize the driver private data */ - psc_dma = kzalloc_obj(*psc_dma); + psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); if (!psc_dma) return -ENOMEM; /* Get the PSC ID */ prop = of_get_property(op->dev.of_node, "cell-index", &size); - if (!prop || size < sizeof *prop) { - ret = -ENODEV; - goto out_free; - } + if (!prop || size < sizeof *prop) + return -ENODEV; spin_lock_init(&psc_dma->lock); mutex_init(&psc_dma->mutex); @@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) /* Find the address of the fifo data registers and setup the * DMA tasks */ - fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); + fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); psc_dma->capture.bcom_task = bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); psc_dma->playback.bcom_task = @@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) if (!psc_dma->capture.bcom_task || !psc_dma->playback.bcom_task) { dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); - ret = -ENODEV; - goto out_free; + return -ENODEV; } /* Disable all interrupts and reset the PSC */ @@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) psc_dma->capture.irq = bcom_get_task_irq(psc_dma->capture.bcom_task); - rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, + rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, "psc-dma-status", psc_dma); - rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, + rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-capture", &psc_dma->capture); - rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, + rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-playback", &psc_dma->playback); - if (rc) { - ret = -ENODEV; - goto out_irq; - } + if (rc) + return -ENODEV; /* Save what we've done so it can be found again later */ dev_set_drvdata(&op->dev, psc_dma); @@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) /* Tell the ASoC OF helpers about it */ return devm_snd_soc_register_component(&op->dev, &mpc5200_audio_dma_component, NULL, 0); -out_irq: - free_irq(psc_dma->irq, psc_dma); - free_irq(psc_dma->capture.irq, &psc_dma->capture); - free_irq(psc_dma->playback.irq, &psc_dma->playback); -out_free: - kfree(psc_dma); - return ret; } EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); @@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); - /* Release irqs */ - free_irq(psc_dma->irq, psc_dma); - free_irq(psc_dma->capture.irq, &psc_dma->capture); - free_irq(psc_dma->playback.irq, &psc_dma->playback); - - kfree(psc_dma); dev_set_drvdata(&op->dev, NULL); return 0; diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 2aefd6414ace..ccd8bda05860 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -276,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) return rc; } - rc = snd_soc_register_component(&op->dev, &psc_ac97_component, + rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); if (rc != 0) { dev_err(&op->dev, "Failed to register DAI\n"); @@ -302,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) static void psc_ac97_of_remove(struct platform_device *op) { mpc5200_audio_dma_destroy(op); - snd_soc_unregister_component(&op->dev); - snd_soc_set_ac97_ops(NULL); } /* Match table for of_platform binding */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 7831136f4f12..55a12be6ad18 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -166,7 +166,7 @@ static int psc_i2s_of_probe(struct platform_device *op) if (rc != 0) return rc; - rc = snd_soc_register_component(&op->dev, &psc_i2s_component, + rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); @@ -213,7 +213,6 @@ static int psc_i2s_of_probe(struct platform_device *op) static void psc_i2s_of_remove(struct platform_device *op) { mpc5200_audio_dma_destroy(op); - snd_soc_unregister_component(&op->dev); } /* Match table for of_platform binding */ From 4c69d04958ec87163ba826e2506babab37192e4b Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Tue, 21 Jul 2026 19:35:55 -0700 Subject: [PATCH 309/791] ASoC: spacemit: rename clock inputs to match binding The driver requests the per-controller SSPA bus and functional clocks as "sspa_bus" and "sspa", but the device tree binding (spacemit,k1-i2s) specifies them as "bus" and "func". As a result, any DT written against the published binding fails to probe. There are currently no in-tree DT users referencing these names, so rename the clock inputs in the driver to match the binding rather than changing the binding. While at it, rename the matching struct member sspa_clk to func_clk for consistency with the new clock-names. Fixes: fce217449075 ("ASoC: spacemit: add i2s support for K1 SoC") Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260721-kx-i2s-dts-v1-1-d22cb6cfaab5@linux.spacemit.com Signed-off-by: Mark Brown --- sound/soc/spacemit/k1_i2s.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index cd461f2aa756..2751485ac0b0 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -52,7 +52,7 @@ struct spacemit_i2s_dev { struct clk *sysclk; struct clk *bclk; - struct clk *sspa_clk; + struct clk *func_clk; struct clk *sysclk_div; struct clk *c_sysclk; struct clk *c_bclk; @@ -221,7 +221,7 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - return clk_set_rate(i2s->sspa_clk, bclk_rate); + return clk_set_rate(i2s->func_clk, bclk_rate); } static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, @@ -452,14 +452,14 @@ static int spacemit_i2s_probe(struct platform_device *pdev) if (IS_ERR(i2s->bclk)) return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n"); - clk = devm_clk_get_enabled(i2s->dev, "sspa_bus"); + clk = devm_clk_get_enabled(i2s->dev, "bus"); if (IS_ERR(clk)) - return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n"); + return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable bus clock\n"); - i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa"); - if (IS_ERR(i2s->sspa_clk)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), - "failed to enable sspa clock\n"); + i2s->func_clk = devm_clk_get_enabled(i2s->dev, "func"); + if (IS_ERR(i2s->func_clk)) + return dev_err_probe(i2s->dev, PTR_ERR(i2s->func_clk), + "failed to enable func clock\n"); i2s->sysclk_div = devm_clk_get_optional_enabled(i2s->dev, "sysclk_div"); if (IS_ERR(i2s->sysclk_div)) From ec926b3bcb49000bafb402a6864d19ae40a3d288 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Tue, 21 Jul 2026 19:35:56 -0700 Subject: [PATCH 310/791] ASoC: dt-bindings: sound: spacemit,k1-i2s: allow 6 clocks for K3 i2s1 The K3 I2S controllers normally use the published 7-clock layout: sysclk, bclk, bus, func, sysclk_div, c_sysclk, c_bclk However, K3 i2s1 has no dedicated sysclk divider and therefore uses a 6-clock layout: sysclk, bclk, bus, func, c_sysclk, c_bclk Describe the 4-clock K1 and both K3 layouts as separate tuples. Lower the K3 minimum from 7 to 6 clocks while preserving the existing 7-clock ordering. Fixes: 6bc6b28c0314 ("ASoC: dt-bindings: add SpacemiT K3 SoC compatible") Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260721-kx-i2s-dts-v1-2-d22cb6cfaab5@linux.spacemit.com Signed-off-by: Mark Brown --- .../bindings/sound/spacemit,k1-i2s.yaml | 64 +++++++++++++------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml index 240d90402e4f..72723f067b2a 100644 --- a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml @@ -23,9 +23,9 @@ allOf: then: properties: clocks: - minItems: 7 + minItems: 6 clock-names: - minItems: 7 + minItems: 6 else: properties: clocks: @@ -43,26 +43,50 @@ properties: maxItems: 1 clocks: - minItems: 4 - items: - - description: clock for I2S sysclk - - description: clock for I2S bclk - - description: clock for I2S bus - - description: clock for I2S controller - - description: clock for I2S sysclk divider - - description: clock for I2S common sysclk - - description: clock for I2S common bclk + oneOf: + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - description: clock for I2S common sysclk + - description: clock for I2S common bclk + - items: + - description: clock for I2S sysclk + - description: clock for I2S bclk + - description: clock for I2S bus + - description: clock for I2S controller + - description: clock for I2S sysclk divider + - description: clock for I2S common sysclk + - description: clock for I2S common bclk clock-names: - minItems: 4 - items: - - const: sysclk - - const: bclk - - const: bus - - const: func - - const: sysclk_div - - const: c_sysclk - - const: c_bclk + oneOf: + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - const: c_sysclk + - const: c_bclk + - items: + - const: sysclk + - const: bclk + - const: bus + - const: func + - const: sysclk_div + - const: c_sysclk + - const: c_bclk dmas: minItems: 1 From 7a047311de7fc738984ed10c5fc4f6c9cc418326 Mon Sep 17 00:00:00 2001 From: "shaikh.kamal" Date: Sun, 26 Jul 2026 11:22:27 +0530 Subject: [PATCH 311/791] ASoC: dt-bindings: fix spelling errors Fix spelling errors reported by codespell: susbsytem -> subsystem (amlogic,axg-sound-card, amlogic,gx-sound-card) vlaue -> value (realtek,rt1015) spped -> speed (st,sta32x) No functional change. Signed-off-by: shaikh.kamal Link: https://patch.msgid.link/20260726055227.18123-1-shaikhkamal2012@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/amlogic,axg-sound-card.yaml | 2 +- .../devicetree/bindings/sound/amlogic,gx-sound-card.yaml | 2 +- Documentation/devicetree/bindings/sound/realtek,rt1015.yaml | 2 +- Documentation/devicetree/bindings/sound/st,sta32x.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml index 4f13e8ab50b2..177db900d4a0 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml @@ -30,7 +30,7 @@ properties: minItems: 1 maxItems: 3 description: - Base PLL clocks of audio susbsytem, used to configure base clock + Base PLL clocks of audio subsystem, used to configure base clock frequencies for different audio use-cases. patternProperties: diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index 413b47778181..6fdf605ad59c 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -31,7 +31,7 @@ properties: minItems: 1 maxItems: 3 description: - Base PLL clocks of audio susbsytem, used to configure base clock + Base PLL clocks of audio subsystem, used to configure base clock frequencies for different audio use-cases. patternProperties: diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml index 880196081a60..3c44ebf93a44 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml @@ -19,7 +19,7 @@ properties: realtek,power-up-delay-ms: description: Set a delay time for flush work to be completed, - this vlaue is adjustable depending on platform. + this value is adjustable depending on platform. maxItems: 1 required: diff --git a/Documentation/devicetree/bindings/sound/st,sta32x.txt b/Documentation/devicetree/bindings/sound/st,sta32x.txt index 52265fb757c5..266b6fc9f009 100644 --- a/Documentation/devicetree/bindings/sound/st,sta32x.txt +++ b/Documentation/devicetree/bindings/sound/st,sta32x.txt @@ -73,7 +73,7 @@ Optional properties: - st,odd-pwm-speed-mode: If present, PWM speed mode run on odd speed mode (341.3 kHz) on all - channels. If not present, normal PWM spped mode (384 kHz) will be used. + channels. If not present, normal PWM speed mode (384 kHz) will be used. - st,invalid-input-detect-mute: If present, automatic invalid input detect mute is enabled. From c626e2e0b2798e7d06a5310316253c61988c256b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:16 +0800 Subject: [PATCH 312/791] ASoC: Intel: add snd_soc_acpi_intel_rt712_vb_no_function_topology machine check function This check function return snd_soc_acpi_intel_sdca_is_device_rt712_vb() && snd_soc_acpi_intel_no_function_topology() for the cases that need check is the device is rt712 vb and no function topology. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c | 8 ++++++++ sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h | 1 + 2 files changed, 9 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c index 7caabc501b16..9fdeb6ad96ac 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c @@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) } EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); +bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) +{ + return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && + snd_soc_acpi_intel_no_function_topology(arg); +} +EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, + "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h index 2ea0a1881c4b..60b665ab5924 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h @@ -11,5 +11,6 @@ bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); bool snd_soc_acpi_intel_no_function_topology(void *arg); +bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); #endif From 168e80166596409cbe7373ca4603f73acdf409eb Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:17 +0800 Subject: [PATCH 313/791] ASoC: soc-acpi-intel-ptl-match: add machine check for machines can't use function topology There are still some Google machines that need to use the monolithic topology. Add the machine check for those machines. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index f7694b2a2b02..41c059d43998 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rt722_l0_rt1320_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, + .machine_check = snd_soc_acpi_intel_no_function_topology, }, { .link_mask = BIT(1) | BIT(2), @@ -527,9 +527,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .link_mask = BIT(3), .links = ptl_sdw_rt712_vb_l3_rt1320_l3, .drv_name = "sof_sdw", - .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; From 3b4cfca660b533f66e417cc3dc6b0a9a8e20ab52 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:18 +0800 Subject: [PATCH 314/791] ASoC: soc-acpi-intel-ptl-match: use function topology by default With commit c84179a1d36b ("ASoC: Intel: sof_sdw: append dai type to dai link name unconditionally"), function topology can apply to all SoundWire codec configurations. Set .get_function_tplg_files callback to use function topology by default. If any required function topology can not be found in the file system, it will fallback to use the monolithic topology. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index 41c059d43998..756bbf82a326 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -522,6 +522,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3), From c4611d4c476d69aecfe1de33a799a3a6d48d5503 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:19 +0800 Subject: [PATCH 315/791] ASoC: soc-acpi-intel-lnl-match: use function topology by default With commit c84179a1d36b ("ASoC: Intel: sof_sdw: append dai type to dai link name unconditionally"), function topology can apply to all SoundWire codec configurations. Set .get_function_tplg_files callback to use function topology by default. If any required function topology can not be found in the file system, it will fallback to use the monolithic topology. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-5-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-lnl-match.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 937a74a5d523..3b9758f73238 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_3_in_1_sdca, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = lnl_cs42l43_l0_cs35l56_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = lnl_cs42l43_l2_cs35l56x6_l13, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(3), .links = lnl_cs42l43_l0_cs35l56_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = lnl_712_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .link_mask = GENMASK(2, 0), .links = lnl_sdw_rt1318_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = lnl_sdw_rt1320_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1), .links = lnl_sdw_rt713_l0_rt1318_l1, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" + .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2), From 3f17f5a1e2b2bf7f8d02b53a8d218578b78e5f5e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:20 +0800 Subject: [PATCH 316/791] ASoC: soc-acpi-intel-arl-match: use function topology by default With commit c84179a1d36b ("ASoC: Intel: sof_sdw: append dai type to dai link name unconditionally"), function topology can apply to all SoundWire codec configurations. Set .get_function_tplg_files callback to use function topology by default. If any required function topology can not be found in the file system, it will fallback to use the monolithic topology. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-6-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-arl-match.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 59bfd5248819..1033f2d45c8a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rt711_l0_rt1316_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(3), @@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x1, /* link0 required */ .links = arl_sdca_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt711-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2), From da1e707bc2586c7c695b4a1f9d98516b360f0b57 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Jul 2026 15:05:21 +0800 Subject: [PATCH 317/791] ASoC: soc-acpi-intel-mtl-match: use function topology by default With commit c84179a1d36b ("ASoC: Intel: sof_sdw: append dai type to dai link name unconditionally"), function topology can apply to all SoundWire codec configurations. Set .get_function_tplg_files callback to use function topology by default. If any required function topology can not be found in the file system, it will fallback to use the monolithic topology. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260723070521.870256-7-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-mtl-match.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 2e4222456f27..596582053d93 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = tac5572_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5572.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tac5672_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5672.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tac5682_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tac5682.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tas2783_link0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tas2783.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = tas2883_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-tas2883.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = mtl_rt713_l0_rt1316_l12, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3) | BIT(0), .links = mtl_712_l0_1712_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .link_mask = GENMASK(2, 0), .links = mtl_sdw_rt1318_l12_rt714_l0, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" + .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), @@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_3_in_1_sdca, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x9, /* 2 active links required */ .links = mtl_rt711_l0_rt1316_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = rt5682_link2_max98373_link0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = cs42l42_link0_max98363_link2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; From 54324f3973c84f3cc81d2c614cb846be086f37f4 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 24 Jul 2026 23:54:44 +0530 Subject: [PATCH 318/791] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Extend the qcom,q6apm-lpass-dais device tree binding to explicitly describe Digital Audio Interface (DAI) child nodes. Add #address-cells and #size-cells to allow representation of multiple DAI instances as child nodes, and define a dai@ pattern to document per-DAI properties such as the interface ID and associated clocks. On platforms such as Monaco and Lemans, third-party codecs are hardware wired to the SoC and do not always have an in-tree codec driver to manage their clocks. For these designs, clock line enablement must be driven from the platform side, and this series provides the necessary support for that. On QAIF-based platforms such as Shikra and Hawi, responsibility for voting I2S MCLK and BCLK has moved from the DSP to the kernel. This series introduces the required device tree binding support to represent and vote for these clocks from the kernel. Co-developed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Signed-off-by: Mohammad Rafi Shaik Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260724182446.1484894-2-mohammad.rafi.shaik@oss.qualcomm.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,q6apm-lpass-dais.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml index 2fb95544db8b..4878c424ef03 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml @@ -21,6 +21,43 @@ properties: '#sound-dai-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +# Digital Audio Interfaces +patternProperties: + '^dai@[0-9a-f]+$': + type: object + description: + Q6DSP Digital Audio Interfaces. + + properties: + reg: + maxItems: 1 + description: + Digital Audio Interface ID + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - enum: [bclk, mclk] + - const: mclk + + dependencies: + clocks: [clock-names] + + required: + - reg + + additionalProperties: false + required: - compatible - '#sound-dai-cells' @@ -29,7 +66,20 @@ unevaluatedProperties: false examples: - | + #include + dais { compatible = "qcom,q6apm-lpass-dais"; #sound-dai-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dai@10 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_CLK_ID_MCLK_1 + LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk", "mclk"; + }; }; From cc8495c1d45ab113a638b5df6c2e0bfa150a7376 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 24 Jul 2026 23:54:45 +0530 Subject: [PATCH 319/791] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Add support for MI2S clock control within q6apm-lpass DAIs, including handling of MCLK, BCLK via the DAI .set_sysclk callback. Each MI2S port now retrieves its clock handles from the device tree, allowing per-port clock configuration and proper enable/disable during startup and shutdown. Co-developed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Tested-by: Neil Armstrong Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20260724182446.1484894-3-mohammad.rafi.shaik@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 168 +++++++++++++++++++++++- sound/soc/qcom/qdsp6/q6prm.h | 3 + 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9..e68e8b000e07 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -2,10 +2,12 @@ // Copyright (c) 2021, Linaro Limited #include +#include #include #include #include #include +#include #include #include #include @@ -15,15 +17,54 @@ #include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" +#include "q6prm.h" #define AUDIOREACH_BE_PCM_BASE 16 +struct q6apm_dai_priv_data { + struct clk *mclk; + struct clk *bclk; + bool mclk_enabled, bclk_enabled; +}; + struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; +static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) +{ + if (dai_data->priv[id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].mclk); + dai_data->priv[id].mclk_enabled = false; + } + + if (dai_data->priv[id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].bclk); + dai_data->priv[id].bclk_enabled = false; + } +} + +static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) +{ + int i; + + for (i = 0; i < APM_PORT_MAX; i++) { + q6apm_lpass_dai_disable_clocks(dai_data, i); + + if (dai_data->priv[i].mclk) { + clk_put(dai_data->priv[i].mclk); + dai_data->priv[i].mclk = NULL; + } + if (dai_data->priv[i].bclk) { + clk_put(dai_data->priv[i].bclk); + dai_data->priv[i].bclk = NULL; + } + } +} + static int q6dma_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_ch_mask, @@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return q6apm_lpass_dai_startup(substream, dai); +} + +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + q6apm_lpass_dai_shutdown(substream, dai); + q6apm_lpass_dai_disable_clocks(dai_data, dai->id); +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + default: + return -EINVAL; + } + + if (sysclk) { + ret = clk_set_rate(sysclk, freq); + if (ret) { + dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", + freq, clk_id); + return ret; + } + + if (*enabled) + return 0; + + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -272,11 +369,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6apm_lpass_dai_shutdown, + .startup = q6i2s_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -297,6 +395,64 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + int err = PTR_ERR(priv->mclk); + + priv->mclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s mclk\n"); + } + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + int err = PTR_ERR(priv->bclk); + + priv->bclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s bclk\n"); + } + } + break; + default: + break; + } + } + + return 0; +} + +static void q6apm_lpass_dai_clocks_action(void *data) +{ + q6apm_lpass_dai_put_clocks(data); +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -304,12 +460,20 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; + int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); + ret = of_q6apm_parse_dai_data(dev, dai_data); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); + if (ret) + return ret; memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a988a32086fe..bc5b9fa13283 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,6 +3,9 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ +#define LPAIF_MI2S_MCLK 1 +#define LPAIF_MI2S_BCLK 2 + /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */ From 766f3f79c312c9ecd3c439ef993d55c58b790c09 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 24 Jul 2026 23:54:46 +0530 Subject: [PATCH 320/791] ASoC: qcom: sc8280xp: enhance machine driver for board-specific config The sc8280xp machine driver is currently written with a largely SoC-centric view and assumes a uniform audio topology across all boards. In practice, multiple products based on the same SoC use different board designs and external audio components, which require board-specific configuration to function correctly. Several Qualcomm platforms integrate third-party audio codecs or use different external audio paths. These designs often require additional configuration such as explicit MI2S MCLK/BCLK settings for audio to work. This change enhances the sc8280xp machine driver to support board-specific configuration such as allowing each board variant to provide its own DAPM widgets and routes, reflecting the actual audio components and connectors present and enabling MI2S MCLK programming for boards that use external codecs requiring a stable master clock. Tested-by: Neil Armstrong Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20260724182446.1484894-4-mohammad.rafi.shaik@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 251 +++++++++++++++++++++++++++++++++++--- 1 file changed, 231 insertions(+), 20 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 98b15a527e37..2ecba74d736e 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -12,17 +12,78 @@ #include #include #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" +#include "qdsp6/q6prm.h" #include "common.h" #include "sdw.h" +#define I2S_MCLKFS 256 + +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), +}; + +struct snd_soc_common { + const char *driver_name; + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + const struct snd_kcontrol_new *controls; + int num_controls; + unsigned int codec_dai_fmt; + bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; +}; + struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; + const struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; +static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) +{ + int rate = params_rate(params); + + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} + +static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) +{ + return I2S_BIT_RATE(params_rate(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); +} + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -32,10 +93,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; switch (cpu_dai->id) { - case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX...QUINARY_MI2S_TX: - snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - break; case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* @@ -64,7 +121,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + if (data->snd_soc_common_priv->wcd_jack) + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + + return 0; } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -96,6 +156,63 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + int mclk_freq = sc8280xp_get_mclk_freq(params); + int bclk_freq = sc8280xp_get_bclk_freq(params); + int ret; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret && ret != -ENOTSUPP) + return ret; + + if (data->snd_soc_common_priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + data->snd_soc_common_priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } + + if (data->snd_soc_common_priv->mi2s_mclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->snd_soc_common_priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->snd_soc_common_priv->codec_sysclk_set) { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + break; + default: + break; + } + + return 0; +} + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -117,6 +234,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sc8280xp_be_ops = { .startup = qcom_snd_sdw_startup, .shutdown = qcom_snd_sdw_shutdown, + .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, }; @@ -145,38 +263,131 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->owner = THIS_MODULE; + /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + data->snd_soc_common_priv = of_device_get_match_data(dev); + if (!data->snd_soc_common_priv) + return -ENODEV; + + card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); + card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; + card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; + card->dapm_routes = data->snd_soc_common_priv->dapm_routes; + card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; + card->controls = data->snd_soc_common_priv->controls; + card->num_controls = data->snd_soc_common_priv->num_controls; + ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = of_device_get_match_data(dev); + card->driver_name = data->snd_soc_common_priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } +static const struct snd_soc_common eliza_priv_data = { + .driver_name = "eliza", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common kaanapali_priv_data = { + .driver_name = "kaanapali", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static const struct snd_soc_common qcs615_priv_data = { + .driver_name = "qcs615", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static const struct snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static const struct snd_soc_common sc8280xp_priv_data = { + .driver_name = "sc8280xp", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common sm8450_priv_data = { + .driver_name = "sm8450", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common sm8550_priv_data = { + .driver_name = "sm8550", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common sm8650_priv_data = { + .driver_name = "sm8650", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct snd_soc_common sm8750_priv_data = { + .driver_name = "sm8750", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,eliza-sndcard", "eliza"}, - {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, - {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, - {.compatible = "qcom,qcs615-sndcard", "qcs615"}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, - {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, - {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, - {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, - {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, - {.compatible = "qcom,sm8450-sndcard", "sm8450"}, - {.compatible = "qcom,sm8550-sndcard", "sm8550"}, - {.compatible = "qcom,sm8650-sndcard", "sm8650"}, - {.compatible = "qcom,sm8750-sndcard", "sm8750"}, + { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, + { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, + { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, + { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, + { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, + { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, + { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, + { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, + { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, + { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, {} }; From 7859b74c0bd600cddd86afd55a3dde285a8c2937 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 23 Jul 2026 17:36:39 +0800 Subject: [PATCH 321/791] ASoC: Intel: avs: da7219: Remove redundant DAI link name allocation avs_create_dai_link() assigns dl->name twice; the first devm_kasprintf() is immediately overwritten by the TDM-aware name. Drop the redundant first assignment. Signed-off-by: Linmao Li Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260723093639.2364360-1-lilinmao@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/intel/avs/boards/da7219.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 2b17abcbd2bc..6cc8a6618fa1 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, if (!dl || !platform) return -ENOMEM; - dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); From 78a9d1426416248eec2afff8a86f1a8456780b4d Mon Sep 17 00:00:00 2001 From: Frank Li Date: Fri, 15 May 2026 10:52:03 -0400 Subject: [PATCH 322/791] ASoC: dt-bindings: Convert eukrea-tlv320.txt to yaml Convert eukrea-tlv320.txt to yaml format. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260515145205.1696584-1-Frank.Li@oss.nxp.com Signed-off-by: Mark Brown --- .../bindings/sound/eukrea,asoc-tlv320.yaml | 61 +++++++++++++++++++ .../bindings/sound/eukrea-tlv320.txt | 26 -------- 2 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml delete mode 100644 Documentation/devicetree/bindings/sound/eukrea-tlv320.txt diff --git a/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml b/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml new file mode 100644 index 000000000000..24b793feb02d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/eukrea,asoc-tlv320.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/eukrea,asoc-tlv320.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Audio complex for Eukrea boards with tlv320aic23 codec. + +maintainers: + - Frank Li + +properties: + compatible: + const: eukrea,asoc-tlv320 + + eukrea,model: + $ref: /schemas/types.yaml#/definitions/string + description: + The user-visible name of this sound complex. + + ssi-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The phandle of the SSI controller. + + fsl,mux-int-port: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The internal port of the i.MX audio muxer (AUDMUX). + Note: The AUDMUX port numbering should start at 1, which is consistent with + hardware manual. + minimum: 1 + maximum: 8 + + fsl,mux-ext-port: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The external port of the i.MX audio muxer. + Note: The AUDMUX port numbering should start at 1, which is consistent with + hardware manual. + minimum: 1 + maximum: 8 + +required: + - compatible + - eukrea,model + - ssi-controller + - fsl,mux-int-port + - fsl,mux-ext-port + +additionalProperties: false + +examples: + - | + sound { + compatible = "eukrea,asoc-tlv320"; + eukrea,model = "imx51-eukrea-tlv320aic23"; + ssi-controller = <&ssi2>; + fsl,mux-int-port = <2>; + fsl,mux-ext-port = <3>; + }; diff --git a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt deleted file mode 100644 index 6dfa88c4dc1e..000000000000 --- a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt +++ /dev/null @@ -1,26 +0,0 @@ -Audio complex for Eukrea boards with tlv320aic23 codec. - -Required properties: - - - compatible : "eukrea,asoc-tlv320" - - - eukrea,model : The user-visible name of this sound complex. - - - ssi-controller : The phandle of the SSI controller. - - - fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX). - - - fsl,mux-ext-port : The external port of the i.MX audio muxer. - -Note: The AUDMUX port numbering should start at 1, which is consistent with -hardware manual. - -Example: - - sound { - compatible = "eukrea,asoc-tlv320"; - eukrea,model = "imx51-eukrea-tlv320aic23"; - ssi-controller = <&ssi2>; - fsl,mux-int-port = <2>; - fsl,mux-ext-port = <3>; - }; From ca9b51f41c9ddd9b5aab9171b78d647ab05ccb17 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:16 +0900 Subject: [PATCH 323/791] ASoC: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260723184538.3888637-15-ekffu200098@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 11 +++-------- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 7 +------ sound/soc/samsung/smdk_spdif.c | 8 ++------ sound/soc/sof/intel/hda-dsp.c | 6 +----- 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index ef784cca13f2..01a0aaa60246 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, MAX98388_CODEC_DAI); - int ret; - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF); } static const struct snd_soc_ops acp_max98388_ops = { diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 9ee4d9926e06..a4c8cbfba096 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -236,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, stream->stream_info.str_id = str_params.stream_id; - ret_val = stream->ops->open(sst->dev, &str_params); - if (ret_val <= 0) - return ret_val; - - - return ret_val; + return stream->ops->open(sst->dev, &str_params); } static void sst_period_elapsed(void *arg) diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index 2474eb619882..515e4dfc1432 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, return ret; /* Set S/PDIF uses internal source clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, - rclk_rate, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, + rclk_rate, SND_SOC_CLOCK_IN); } static const struct snd_soc_ops smdk_spdif_ops = { diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index e9f092f082a1..b9b2bdff4ccb 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) usleep_range(500, 1000); /* Restore state for shutdown, back to reset */ - ret = hda_dsp_ctrl_link_reset(sdev, true); - if (ret < 0) - return ret; - - return ret; + return hda_dsp_ctrl_link_reset(sdev, true); } int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) From 3799a56da8f3250e5765344aa5d280678dc89124 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 01:03:40 +0900 Subject: [PATCH 324/791] ASoC: amd: acp: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260725160344.916838-2-ekffu200098@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index ef784cca13f2..01a0aaa60246 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, MAX98388_CODEC_DAI); - int ret; - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF); } static const struct snd_soc_ops acp_max98388_ops = { From 52c09577296275d96233429d6375b50fae24cd9c Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 01:03:41 +0900 Subject: [PATCH 325/791] ASoC: Intel: atom: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260725160344.916838-3-ekffu200098@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 9ee4d9926e06..a4c8cbfba096 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -236,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, stream->stream_info.str_id = str_params.stream_id; - ret_val = stream->ops->open(sst->dev, &str_params); - if (ret_val <= 0) - return ret_val; - - - return ret_val; + return stream->ops->open(sst->dev, &str_params); } static void sst_period_elapsed(void *arg) From cdb8b41357bb8319c1f57c5694b3323846d11dd5 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 01:03:42 +0900 Subject: [PATCH 326/791] ASoC: samsung: smdk_spdif: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260725160344.916838-4-ekffu200098@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/smdk_spdif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index 2474eb619882..515e4dfc1432 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, return ret; /* Set S/PDIF uses internal source clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, - rclk_rate, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return ret; + return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, + rclk_rate, SND_SOC_CLOCK_IN); } static const struct snd_soc_ops smdk_spdif_ops = { From 619446b87c0eb6ca896170c30c0285e6adfa1594 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 01:03:43 +0900 Subject: [PATCH 327/791] ASoC: SOF: Intel: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260725160344.916838-5-ekffu200098@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index e9f092f082a1..b9b2bdff4ccb 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) usleep_range(500, 1000); /* Restore state for shutdown, back to reset */ - ret = hda_dsp_ctrl_link_reset(sdev, true); - if (ret < 0) - return ret; - - return ret; + return hda_dsp_ctrl_link_reset(sdev, true); } int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) From 6b44ad3ec83b4f06bb6959f4e75998c7d7f7f070 Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Fri, 10 Jul 2026 23:09:49 +0200 Subject: [PATCH 328/791] ASoC: meson: aiu-encoder-i2s: fix bs quirk incompatibility check The bs-quirk incompatibility check has two flaws: - It only rejects one direction of the mismatch. A stream that does not require the quirk is rejected while a quirked stream is active, but the opposite is not true: a stream requiring the quirk passes the check while a non-quirked stream is active, silently reprogramming the shared mclk/bclk divider with the 50% increase and corrupting the output of the running stream. - 'bs_quirk' is only cleared in hw_free() when the last substream closes, but userspace may legally stop/reconfigure/start the stream without an intervening hw_free. Reconfiguring a single stream from the quirked configuration (8ch/16-bit) to one that does not need the quirk therefore fails with -EINVAL due to the stale flag. Drop the interface-wide flag and instead compare the quirk requirement of the incoming parameters against the committed configuration of the opposite stream at hw_params() time. The committed channels/width are cleared in hw_free() so that a released stream no longer constrains the other one. Signed-off-by: Valerio Setti Link: https://patch.msgid.link/20260710-aiu-improve-quirk-check-v1-1-2fdd1b6f8896@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 66 ++++++++++++++++++++----------- sound/soc/meson/gx-interface.h | 3 -- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index 83b579e98f1c..c2a280bfdfe2 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -62,13 +62,36 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, return 0; } +/* + * Return true if the given combination of channels and sample width requires + * the bs quirk. Return false otherwise. + */ +static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) +{ + return (channels == 8) && (width == 16); +} + +static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); + + /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ + if (!other_stream || !other_stream->channels) + return 0; + + if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != + aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) + return -EINVAL; + + return 0; +} + static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) { - struct aiu *aiu = snd_soc_component_get_drvdata(component); - struct gx_iface *iface = &aiu->i2s.iface; - /* * NOTE: this HW is odd. * In most configuration, the i2s divider is 'mclk / blck'. @@ -76,25 +99,13 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, * increased by 50% to get the correct output rate. * No idea why ! */ - if (params_width(params) == 16 && params_channels(params) == 8) { + if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { if (bs % 2) { dev_err(component->dev, "Cannot increase i2s divider by 50%%\n"); return -EINVAL; } bs += bs / 2; - iface->bs_quirk = true; - } else { - /* - * If the bs quirk is currently applied for one stream and another - * ones tries to setup a configuration for which the quirk is - * not required, then fail. - */ - if (iface->bs_quirk) { - dev_err(component->dev, - "bclk requirements are incompatible with active stream\n"); - return -EINVAL; - } } /* Use CLK_MORE for mclk to bclk divider */ @@ -110,9 +121,11 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, return 0; } -static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, - struct snd_pcm_hw_params *params) +static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { + struct snd_soc_component *component = dai->component; struct aiu *aiu = snd_soc_component_get_drvdata(component); struct gx_iface *iface = &aiu->i2s.iface; unsigned int srate = params_rate(params); @@ -133,10 +146,15 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, bs = fs / 64; - if (aiu->platform->has_clk_ctrl_more_i2s_div) + if (aiu->platform->has_clk_ctrl_more_i2s_div) { + if (aiu_encoder_check_bs_quirk(substream, params, dai)) { + dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); + return -EINVAL; + } ret = aiu_encoder_i2s_set_more_div(component, params, bs); - else + } else { ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); + } if (ret) return ret; @@ -155,7 +173,6 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, { struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); struct gx_iface *iface = ts->iface; - struct snd_soc_component *component = dai->component; int ret; /* @@ -170,7 +187,7 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, } } - ret = aiu_encoder_i2s_set_clocks(component, params); + ret = aiu_encoder_i2s_set_clocks(substream, params, dai); if (ret) { dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); return ret; @@ -219,7 +236,6 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, if (snd_soc_dai_active(dai) <= 1) { aiu_encoder_i2s_divider_enable(component, 0); iface->rate = 0; - iface->bs_quirk = false; } if (ts->clk_enabled) { @@ -227,6 +243,10 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, ts->clk_enabled = false; } + ts->channels = 0; + ts->width = 0; + ts->physical_width = 0; + return 0; } diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h index 65c46dcce32a..d9ab894589fa 100644 --- a/sound/soc/meson/gx-interface.h +++ b/sound/soc/meson/gx-interface.h @@ -22,9 +22,6 @@ struct gx_iface { /* For component wide symmetry */ int rate; - - /* Only for GX platform */ - int bs_quirk; }; struct gx_stream { From df3c987ab3d70146e2c657bc50efe8737aa4da28 Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Fri, 10 Jul 2026 23:09:50 +0200 Subject: [PATCH 329/791] ASoC: meson: aiu-encoder-i2s: reflect bs quirk in hw constraints Currently the only check for bs-quirk is implemented in hw_params(), but this is too late: nothing in the refined hw parameters hints at the restriction, so userspace has no way to know the configuration is invalid until the setup fails, as Jerome pointed out during review [1]. Add hw rules on CHANNELS and SAMPLE_BITS at startup() so that the restriction shows up during parameter refinement instead. The rules are refined against the committed configuration of the opposite stream: - if it uses the quirk, the current stream is narrowed to the same 8ch/16-bit configuration; - otherwise, selecting a 16-bit physical width limits the stream to 2 channels, and selecting 8 channels requires a physical width larger than 16 bits. The rules key on the physical width while the quirk is defined on the significant bits. This is safe because S16_LE is the only format supported by the encoder where both are 16 bits. The check in aiu_encoder_i2s_set_clocks() is kept as the last backstop: both streams may be refined concurrently before either commits its configuration. The rules are only registered on GX platforms where the bs-quirk exists and only when the DAI has a stream in the opposite direction. [1] https://lore.kernel.org/r/1jik7pebk7.fsf@starbuckisacylon.baylibre.com/ Suggested-by: Jerome Brunet Signed-off-by: Valerio Setti Link: https://lore.kernel.org/r/1jik7pebk7.fsf@starbuckisacylon.baylibre.com/ Link: https://patch.msgid.link/20260710-aiu-improve-quirk-check-v1-2-2fdd1b6f8896@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index c2a280bfdfe2..4c62ea41d7e8 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -147,6 +147,13 @@ static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, bs = fs / 64; if (aiu->platform->has_clk_ctrl_more_i2s_div) { + /* + * The hw rules added in startup() make this unreachable in the + * sequential case, but both streams may be refined concurrently + * before either commits its config, since only ops->hw_params + * runs under the card's pcm_mutex. Re-check against the committed + * state of the other stream, which is stable under that mutex. + */ if (aiu_encoder_check_bs_quirk(substream, params, dai)) { dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); return -EINVAL; @@ -333,10 +340,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { .mask = 0, }; +static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct gx_stream *other = rule->private; + struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + /* + * The quirk is technically based on the significant bits whereas here + * we're using the physical width for simplicity. This works because + * S16_LE is the only format supported by this encoder that has: + * significant bits = physical width = 16-bits + */ + struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + struct snd_interval new_i; + + if (other->channels == 0) + return 0; + + snd_interval_any(&new_i); + + if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { + if (aiu_encoder_is_bs_quirk(other->channels, other->width)) + new_i.min = new_i.max = 8; + else if (snd_interval_single(phys_width) && phys_width->min == 16) + new_i.max = 2; /* Force 2ch */ + } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ + if (aiu_encoder_is_bs_quirk(other->channels, other->width)) + new_i.min = new_i.max = 16; + else if (snd_interval_single(ch) && ch->min == 8) + new_i.min = 17; /* Request physical width > 16 bits */ + } + + return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); +} + static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); int ret; /* Make sure the encoder gets either 2 or 8 channels */ @@ -348,6 +390,31 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, return ret; } + /* + * If DAI supports both playback and capture streams ensure the bs-quirk is + * handled correctly. + * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). + */ + if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + aiu_encoder_i2s_pcm_hw_rule, + other_stream, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (ret) + return ret; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + aiu_encoder_i2s_pcm_hw_rule, + other_stream, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (ret) + return ret; + } + /* * Enable only clocks which are required for the interface internal * logic. MCLK is enabled/disabled from the formatter and the I2S From e82159384a5052212e7d2f2aa1de39827f1bed3c Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Fri, 10 Jul 2026 23:09:51 +0200 Subject: [PATCH 330/791] ASoC: meson: aiu-encoder-i2s: use the core symmetric_rate handling The driver manually implement the interface-wide rate symmetry enforcement in hw_params(), which suffers from the same problem addressed in the previous patch: the restriction is not visible in the hw parameter constraints, so a stream with a mismatching rate only finds out via -EINVAL late in the stream setup. The ASoC core already provides this feature through the DAI's 'symmetric_rate' flag: when another stream of the DAI is active, soc_pcm_apply_symmetry() constrains the rate at open time so the restriction shows up during parameter refinement, and soc_pcm_params_symmetry() still rejects a mismatch at hw_params() time as a backstop. Set 'symmetric_rate' on the I2S encoder DAI and drop the open-coded check along with the now unused 'rate' member of struct gx_iface. Signed-off-by: Valerio Setti Link: https://patch.msgid.link/20260710-aiu-improve-quirk-check-v1-3-2fdd1b6f8896@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 21 ++------------------- sound/soc/meson/aiu.c | 1 + sound/soc/meson/gx-interface.h | 3 --- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index 4c62ea41d7e8..58dce9f08c9d 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -179,28 +179,14 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct gx_iface *iface = ts->iface; int ret; - /* - * Enforce interface wide rate symmetry only if there is more than - * 1 stream active. - */ - if (snd_soc_dai_active(dai) > 1) { - if (iface->rate && iface->rate != params_rate(params)) { - dev_err(dai->dev, "can't set iface rate (%d != %d)\n", - iface->rate, params_rate(params)); - return -EINVAL; - } - } - ret = aiu_encoder_i2s_set_clocks(substream, params, dai); if (ret) { dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); return ret; } - iface->rate = params_rate(params); ts->physical_width = params_physical_width(params); ts->width = params_width(params); ts->channels = params_channels(params); @@ -233,17 +219,14 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct gx_iface *iface = ts->iface; struct snd_soc_component *component = dai->component; /* * If this is the last substream being closed then disable the i2s - * clock divider and clear 'iface->rate'. + * clock divider. */ - if (snd_soc_dai_active(dai) <= 1) { + if (snd_soc_dai_active(dai) <= 1) aiu_encoder_i2s_divider_enable(component, 0); - iface->rate = 0; - } if (ts->clk_enabled) { clk_disable_unprepare(ts->iface->mclk); diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index 64ace4d25d92..2668646e3597 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -154,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_encoder_i2s_dai_ops, + .symmetric_rate = 1, }, [CPU_SPDIF_ENCODER] = { .name = "SPDIF Encoder", diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h index d9ab894589fa..2a6207e393e8 100644 --- a/sound/soc/meson/gx-interface.h +++ b/sound/soc/meson/gx-interface.h @@ -19,9 +19,6 @@ struct gx_iface { /* format is common to all the DAIs of the iface */ unsigned int fmt; - - /* For component wide symmetry */ - int rate; }; struct gx_stream { From c7a6909de856f89c9d913550d9d70e4b3ebec4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:45 +0200 Subject: [PATCH 331/791] ASoC: codecs: mt6357: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignment. While touching this array unify spacing, use a named initializer for .name and drop a trailing comma after the list terminator. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/724af6f60ed9420d5513724d126d70f22e319c91.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/codecs/mt6357.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c index 674cf7df9df4..3b07087e1cab 100644 --- a/sound/soc/codecs/mt6357.c +++ b/sound/soc/codecs/mt6357.c @@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) } static const struct platform_device_id mt6357_platform_ids[] = { - {"mt6357-sound", 0}, - { /* sentinel */ }, + { .name = "mt6357-sound" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); From 0ca8f65516a9ebc155ecca98e386ec592ed71eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:46 +0200 Subject: [PATCH 332/791] ASoC: renesas: fsi: Drop platform probing metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 38d3273075d6 ("ASoC: renesas: fsi: remove platform data style support") probing using the traditional platform bus matching isn't supported any more. Drop the platform_device_id entries that should have been removed in the above commit. Note that keeping the empty array results in the driver not matching on "fsi-pcm-audio" (i.e. the driver name). While touching that array, use a single space and no trailing comma for the list terminator, which is the most used style for this. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/c79fa0d31abc0c80fbd7b4ec94d95b197ddb9f94.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/renesas/fsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index ae86014c3819..1e1a04cc7929 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { MODULE_DEVICE_TABLE(of, fsi_of_match); static const struct platform_device_id fsi_id_table[] = { - { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - {}, + /* an array with no valid entry prevents matching on driver name */ + { } }; MODULE_DEVICE_TABLE(platform, fsi_id_table); From 2b26d06fd3d81659b482b9de17e135edba1fb27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:47 +0200 Subject: [PATCH 333/791] ASoC: amd: acp: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/a23fa9f649eaab706c704b671527c238f211ee26.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 4 ++-- sound/soc/amd/acp/acp-sdw-sof-mach.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..8947d3ad8cdc 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -580,8 +580,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "amd_sdw", }, - {} + { .name = "amd_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..d200388f1393 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -438,8 +438,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "amd_sof_sdw", }, - {} + { .name = "amd_sof_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); From 4bed1074db1e65d5250ecbb528c05f16f137ad8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:48 +0200 Subject: [PATCH 334/791] ASoC: amd: acp: Unify code style for platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a trailing comma for initializers unless the closing brace is on the same line and for the list terminator; - Use a single space in the list terminator; Acked-by: Cezary Rojewski Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/08137cf5b3cdd0cc2e00da32256664f3cb9b1e4e.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sof-mach.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 36ecef7013b9..8874151e159a 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) static const struct platform_device_id board_ids[] = { { .name = "rt5682-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, }, { .name = "rt5682-max", - .driver_data = (kernel_ulong_t)&sof_rt5682_max_data + .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, }, { .name = "rt5682s-max", - .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, }, { .name = "rt5682s-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, }, { .name = "nau8825-max", - .driver_data = (kernel_ulong_t)&sof_nau8825_data + .driver_data = (kernel_ulong_t)&sof_nau8825_data, }, { .name = "rt5682s-hs-rt1019", - .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data + .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, }, { .name = "nau8821-max", - .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data + .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, }, { } }; From 232725a0e3867fc9845e2bccfa94e01840db30be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:49 +0200 Subject: [PATCH 335/791] ASoC: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Charles Keepax Link: https://patch.msgid.link/a76e26aa4edb901b4bea684919463b2243dd8d9d.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs40l50-codec.c | 4 ++-- sound/soc/codecs/cs42l43.c | 4 ++-- sound/soc/intel/boards/sof_sdw.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c index aa629ef53db4..676367163067 100644 --- a/sound/soc/codecs/cs40l50-codec.c +++ b/sound/soc/codecs/cs40l50-codec.c @@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) } static const struct platform_device_id cs40l50_id[] = { - { "cs40l50-codec", }, - {} + { .name = "cs40l50-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, cs40l50_id); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 1d133577702e..67283efdd268 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2941,8 +2941,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { }; static const struct platform_device_id cs42l43_codec_id_table[] = { - { "cs42l43-codec", }, - {} + { .name = "cs42l43-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index c527d575d1ed..b306cc19fcc7 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) } static const struct platform_device_id mc_id_table[] = { - { "sof_sdw", }, - {} + { .name = "sof_sdw" }, + { } }; MODULE_DEVICE_TABLE(platform, mc_id_table); From e4dc03df1f716981712f41a5dabe88acdb843aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 20 Jul 2026 08:23:50 +0200 Subject: [PATCH 336/791] ASOC: Unify code style for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a trailing comma for initializers unless the closing brace is on the same line and for the list terminator; - Use a single space in the list terminator; - Use compact one-line style for small entries; - s/\t=/ =/ were the tab is only one char wide anyhow; Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/093867d47c079d2aaab06bccaae734c54c342a7d.1784528081.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/au1x/db1200.c | 2 +- sound/soc/codecs/adau7118-hw.c | 2 +- sound/soc/codecs/bt-sco.c | 10 +++------- sound/soc/codecs/wcd934x.c | 6 ++---- sound/soc/fsl/imx-pcm-rpmsg.c | 6 +++--- sound/soc/intel/avs/boards/da7219.c | 6 ++---- sound/soc/intel/avs/boards/dmic.c | 6 ++---- sound/soc/intel/avs/boards/es8336.c | 6 ++---- sound/soc/intel/avs/boards/hdaudio.c | 6 ++---- sound/soc/intel/avs/boards/i2s_test.c | 6 ++---- sound/soc/intel/avs/boards/max98357a.c | 6 ++---- sound/soc/intel/avs/boards/max98373.c | 6 ++---- sound/soc/intel/avs/boards/max98927.c | 6 ++---- sound/soc/intel/avs/boards/nau8825.c | 6 ++---- sound/soc/intel/avs/boards/pcm3168a.c | 6 ++---- sound/soc/intel/avs/boards/probe.c | 2 +- sound/soc/intel/avs/boards/rt274.c | 2 +- sound/soc/intel/avs/boards/rt286.c | 2 +- sound/soc/intel/avs/boards/rt298.c | 2 +- sound/soc/intel/avs/boards/rt5514.c | 2 +- sound/soc/intel/avs/boards/rt5640.c | 2 +- sound/soc/intel/avs/boards/rt5663.c | 2 +- sound/soc/intel/avs/boards/rt5682.c | 2 +- sound/soc/intel/avs/boards/ssm4567.c | 2 +- sound/soc/samsung/i2s.c | 2 +- 25 files changed, 40 insertions(+), 66 deletions(-) diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 81abe2e18402..78346cd7959c 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { .name = "db1550-i2s", .driver_data = 5, }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, db1200_pids); diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c index 92b226b8b4bb..4342eb7dd9e5 100644 --- a/sound/soc/codecs/adau7118-hw.c +++ b/sound/soc/codecs/adau7118-hw.c @@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { MODULE_DEVICE_TABLE(of, adau7118_of_match); static const struct platform_device_id adau7118_id[] = { - { .name = "adau7118" }, + { .name = "adau7118" }, { } }; MODULE_DEVICE_TABLE(platform, adau7118_id); diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index c0bf45b76cb8..b085885e3f18 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) } static const struct platform_device_id bt_sco_driver_ids[] = { - { - .name = "dfbmcs320", - }, - { - .name = "bt-sco", - }, - {}, + { .name = "dfbmcs320" }, + { .name = "bt-sco" }, + { } }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index bc41a1466c70..a9e6f2923099 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -5899,10 +5899,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) } static const struct platform_device_id wcd934x_driver_id[] = { - { - .name = "wcd934x-codec", - }, - {}, + { .name = "wcd934x-codec" }, + { } }; MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2a4813c6cda9..792a416d552f 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -819,9 +819,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { }; static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { - { .name = "rpmsg-audio-channel" }, - { .name = "rpmsg-micfil-channel" }, - { }, + { .name = "rpmsg-audio-channel" }, + { .name = "rpmsg-micfil-channel" }, + { } }; MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 2b17abcbd2bc..163d9982d797 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -259,10 +259,8 @@ static int avs_da7219_probe(struct platform_device *pdev) } static const struct platform_device_id avs_da7219_driver_ids[] = { - { - .name = "avs_da7219", - }, - {}, + { .name = "avs_da7219" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index bf6f580a5164..8d36bc7ddf16 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) } static const struct platform_device_id avs_dmic_driver_ids[] = { - { - .name = "avs_dmic", - }, - {}, + { .name = "avs_dmic" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 301cfb3cf15b..36c13db3a272 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) } static const struct platform_device_id avs_es8336_driver_ids[] = { - { - .name = "avs_es8336", - }, - {}, + { .name = "avs_es8336" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index aec769e2396c..03cfd91202d3 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) } static const struct platform_device_id avs_hdaudio_driver_ids[] = { - { - .name = "avs_hdaudio", - }, - {}, + { .name = "avs_hdaudio" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 9a6b89ffdf14..787d781ba1d9 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) } static const struct platform_device_id avs_i2s_test_driver_ids[] = { - { - .name = "avs_i2s_test", - }, - {}, + { .name = "avs_i2s_test" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index e9a87804f918..389a50923d3b 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98357a_driver_ids[] = { - { - .name = "avs_max98357a", - }, - {}, + { .name = "avs_max98357a" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 8b45b643ca29..b8231f71d3d6 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98373_driver_ids[] = { - { - .name = "avs_max98373", - }, - {}, + { .name = "avs_max98373" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index db073125fa4d..d657e7da1cc0 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) } static const struct platform_device_id avs_max98927_driver_ids[] = { - { - .name = "avs_max98927", - }, - {}, + { .name = "avs_max98927" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index d44edacbfc9a..d7ea08bb27cd 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) } static const struct platform_device_id avs_nau8825_driver_ids[] = { - { - .name = "avs_nau8825", - }, - {}, + { .name = "avs_nau8825" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c index b5bebadbbcb2..9d415fd0499a 100644 --- a/sound/soc/intel/avs/boards/pcm3168a.c +++ b/sound/soc/intel/avs/boards/pcm3168a.c @@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) } static const struct platform_device_id avs_pcm3168a_driver_ids[] = { - { - .name = "avs_pcm3168a", - }, - {}, + { .name = "avs_pcm3168a" }, + { } }; MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c index 73884f8a535c..4053d14289b9 100644 --- a/sound/soc/intel/avs/boards/probe.c +++ b/sound/soc/intel/avs/boards/probe.c @@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { { .name = "avs_probe_mb", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index a689f4c80867..289f1230851f 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { { .name = "avs_rt274", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 4c9ac545555a..9364d60cfcc7 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { { .name = "avs_rt286", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 2d7a7748d577..5d3b3cdc9564 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { { .name = "avs_rt298", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index 22139eaad83a..e448bc9fa5ad 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { { .name = "avs_rt5514", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c index 2990d32f2301..c4e33d71e82f 100644 --- a/sound/soc/intel/avs/boards/rt5640.c +++ b/sound/soc/intel/avs/boards/rt5640.c @@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { { .name = "avs_rt5640", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index 68fea325376a..aadbd3f2a1b2 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { { .name = "avs_rt5663", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 81863728da1d..d33699cca595 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { { .name = "avs_rt5682", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index ae0e6e27a8b8..d7c9bff37556 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { { .name = "avs_ssm4567", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f80f697a5d55..1aa51062ed7f 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1682,7 +1682,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { .name = "samsung-i2s", .driver_data = (kernel_ulong_t)&i2sv3_dai_type, }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); From 52ead5e1f1110e570a4b2dd03ec78188b8c831cd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:27:14 +0000 Subject: [PATCH 337/791] ASoC: atmel: atmel_wm8904: use dev in atmel_asoc_wm8904_dt_init() atmel_asoc_wm8904_dt_init() will be updated when Card capsuling. To makes its review easy, use dev in this function and reduce un-related diff. No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878q75jfny.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/atmel/atmel_wm8904.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index 0f4021c6c588..df3e5ef8fa61 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct device_node *codec_np, *cpu_np; struct snd_soc_card *card = &atmel_asoc_wm8904_card; struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; int ret; if (!np) { - dev_err(&pdev->dev, "only device tree supported\n"); + dev_err(dev, "only device tree supported\n"); return -EINVAL; } ret = snd_soc_of_parse_card_name(card, "atmel,model"); if (ret) { - dev_err(&pdev->dev, "failed to parse card name\n"); + dev_err(dev, "failed to parse card name\n"); return ret; } ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); if (ret) { - dev_err(&pdev->dev, "failed to parse audio routing\n"); + dev_err(dev, "failed to parse audio routing\n"); return ret; } cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); if (!cpu_np) { - dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + dev_err(dev, "failed to get dai and pcm info\n"); ret = -EINVAL; return ret; } @@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); if (!codec_np) { - dev_err(&pdev->dev, "failed to get codec info\n"); + dev_err(dev, "failed to get codec info\n"); ret = -EINVAL; return ret; } From 327679a605129bda12df53345881a331c4449d46 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Mon, 13 Jul 2026 23:39:06 +0530 Subject: [PATCH 338/791] ASoC: dt-bindings: qcom,sm8250: Add Hawi sound card Add bindings for Hawi sound card, which is compatible with the existing SM8450. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260713180907.874954-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index dae440ecab59..bd5f5a6268a3 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -24,6 +24,7 @@ properties: - items: - enum: - qcom,eliza-sndcard + - qcom,hawi-sndcard - qcom,kaanapali-sndcard - qcom,sm8550-sndcard - qcom,sm8650-sndcard From 358782121d824e0650a72be664d94c207f677f79 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Mon, 13 Jul 2026 23:39:07 +0530 Subject: [PATCH 339/791] ASoC: qcom: sc8280xp: Add support for Hawi Add compatible for sound card on Qualcomm Hawi platform. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260713180907.874954-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 2ecba74d736e..a9304784d41e 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -300,6 +300,15 @@ static const struct snd_soc_common eliza_priv_data = { .wcd_jack = true, }; +static const struct snd_soc_common hawi_priv_data = { + .driver_name = "hawi", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .codec_sysclk_set = true, + .mi2s_bclk_enable = true, + .wcd_jack = true, +}; + static const struct snd_soc_common kaanapali_priv_data = { .driver_name = "kaanapali", .dapm_widgets = sc8280xp_dapm_widgets, @@ -376,6 +385,7 @@ static const struct snd_soc_common sm8750_priv_data = { static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, + { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, From 0f97c75d15c3869836cd58d8c3b89add2d9b68f5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 24 Jul 2026 16:34:29 -0700 Subject: [PATCH 340/791] ASoC: ti: omap-twl4030: drop support for platform data There are no users of omap_tw4030_pdata in the mainline kernel so remove support for it from the driver. Signed-off-by: Dmitry Torokhov Reviewed-by: Sebastian Reichel Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260724233432.31325-1-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- include/linux/platform_data/omap-twl4030.h | 42 ------- sound/soc/ti/omap-twl4030.c | 134 +++++++-------------- 2 files changed, 44 insertions(+), 132 deletions(-) delete mode 100644 include/linux/platform_data/omap-twl4030.h diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h deleted file mode 100644 index 7fcb55fe21c9..000000000000 --- a/include/linux/platform_data/omap-twl4030.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/** - * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 - * codec, header. - * - * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - */ - -#ifndef _OMAP_TWL4030_H_ -#define _OMAP_TWL4030_H_ - -/* To select if only one channel is connected in a stereo port */ -#define OMAP_TWL4030_LEFT (1 << 0) -#define OMAP_TWL4030_RIGHT (1 << 1) - -struct omap_tw4030_pdata { - const char *card_name; - /* Voice port is connected to McBSP3 */ - bool voice_connected; - - /* The driver will parse the connection flags if this flag is set */ - bool custom_routing; - /* Flags to indicate connected audio ports. */ - u8 has_hs; - u8 has_hf; - u8 has_predriv; - u8 has_carkit; - bool has_ear; - - bool has_mainmic; - bool has_submic; - bool has_hsmic; - bool has_carkitmic; - bool has_digimic0; - bool has_digimic1; - u8 has_linein; -}; - -#endif /* _OMAP_TWL4030_H_ */ diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 4d80f8a7a947..950879fc7275 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -17,7 +17,6 @@ */ #include -#include #include #include @@ -133,20 +132,12 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { }, }; -static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); - struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); - int ret = 0; + int ret; /* * This is a bit of a hack, but the GPIO is optional so we @@ -170,29 +161,7 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata || !pdata->custom_routing) - return ret; - - /* Disable not connected paths if not used */ - twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); - twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); - - twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); - twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); - twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); - - return ret; + return 0; } /* Digital audio interface glue - connects codec <--> CPU */ @@ -237,11 +206,15 @@ static struct snd_soc_card omap_twl4030_card = { static int omap_twl4030_probe(struct platform_device *pdev) { - struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); - struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_twl4030_card; + struct device_node *node, *dai_node; struct omap_twl4030 *priv; - int ret = 0; + struct property *prop; + int ret; + + node = pdev->dev.of_node; + if (!node) + return -ENODEV; card->dev = &pdev->dev; @@ -249,62 +222,43 @@ static int omap_twl4030_probe(struct platform_device *pdev) if (priv == NULL) return -ENOMEM; - if (node) { - struct device_node *dai_node; - struct property *prop; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - dai_node = of_parse_phandle(node, "ti,mcbsp", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McBSP node is not provided\n"); - return -EINVAL; - } - omap_twl4030_dai_links[0].cpus->dai_name = NULL; - omap_twl4030_dai_links[0].cpus->of_node = dai_node; - - omap_twl4030_dai_links[0].platforms->name = NULL; - omap_twl4030_dai_links[0].platforms->of_node = dai_node; - - dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); - if (!dai_node) { - card->num_links = 1; - } else { - omap_twl4030_dai_links[1].cpus->dai_name = NULL; - omap_twl4030_dai_links[1].cpus->of_node = dai_node; - - omap_twl4030_dai_links[1].platforms->name = NULL; - omap_twl4030_dai_links[1].platforms->of_node = dai_node; - } - - /* Optional: audio routing can be provided */ - prop = of_find_property(node, "ti,audio-routing", NULL); - if (prop) { - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) - return ret; - - card->fully_routed = 1; - } - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (!pdata->voice_connected) - card->num_links = 1; - } else { - dev_err(&pdev->dev, "Missing pdata\n"); + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } + dai_node = of_parse_phandle(node, "ti,mcbsp", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McBSP node is not provided\n"); + return -EINVAL; + } + omap_twl4030_dai_links[0].cpus->dai_name = NULL; + omap_twl4030_dai_links[0].cpus->of_node = dai_node; + + omap_twl4030_dai_links[0].platforms->name = NULL; + omap_twl4030_dai_links[0].platforms->of_node = dai_node; + + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpus->dai_name = NULL; + omap_twl4030_dai_links[1].cpus->of_node = dai_node; + + omap_twl4030_dai_links[1].platforms->name = NULL; + omap_twl4030_dai_links[1].platforms->of_node = dai_node; + } + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } + snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { From a9b7250a2f8b84372b1d2fc039fefe2a9f7459b5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 24 Jul 2026 16:34:30 -0700 Subject: [PATCH 341/791] ASoC: ti: omap-twl4030: use per-device instance of headset jack gpio hs_jack_gpios is being potentially shared among several instances of the same device, and is being modified. This is not the best approach to structuring the code (even if the device is in fact a singleton). Change it to allocate a per-device instance. Signed-off-by: Dmitry Torokhov Reviewed-by: Sebastian Reichel Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260724233432.31325-2-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-twl4030.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 950879fc7275..4abcfff04bc7 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -28,6 +28,7 @@ #include "omap-mcbsp.h" struct omap_twl4030 { + struct snd_soc_jack_gpio hs_jack_gpio; struct snd_soc_jack hs_jack; }; @@ -123,15 +124,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -/* Headset jack detection gpios */ -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - { - .name = "ti,jack-det", - .report = SND_JACK_HEADSET, - .debounce_time = 200, - }, -}; - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -144,9 +136,6 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) * only want to add the jack detection if the GPIO is there. */ if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { - hs_jack_gpios[0].gpiod_dev = card->dev; - hs_jack_gpios[0].idx = 0; - ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", SND_JACK_HEADSET, &priv->hs_jack, hs_jack_pins, @@ -154,9 +143,14 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - ret = snd_soc_jack_add_gpios(&priv->hs_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); + priv->hs_jack_gpio.name = "ti,jack-det"; + priv->hs_jack_gpio.report = SND_JACK_HEADSET; + priv->hs_jack_gpio.debounce_time = 200; + priv->hs_jack_gpio.gpiod_dev = card->dev; + priv->hs_jack_gpio.idx = 0; + + ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, + &priv->hs_jack_gpio); if (ret) return ret; } From 34dfc478c963e035d0c2ce61bbe610c9c745b634 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 10:55:09 -0700 Subject: [PATCH 342/791] ASoC: SOF: don't use "/**" for non-kernel-doc comments Modify these errant comments to use "/*" since they are not kernel-doc comments. Warning: ../include/sound/sof/header.h:182 This comment starts with '/**', but isn't a kernel-doc comment. * OOPS header architecture specific data. Warning: ../include/sound/sof/header.h:190 This comment starts with '/**', but isn't a kernel-doc comment. * OOPS header platform specific data. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260713175510.524728-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/sound/sof/header.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4e406dc22f31..6c2c3a5fc90d 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { uint32_t count; /**< count of 0 means end of compound sequence */ } __packed; -/** +/* * OOPS header architecture specific data. */ struct sof_ipc_dsp_oops_arch_hdr { @@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { uint32_t totalsize; /* Total size of oops message */ } __packed; -/** +/* * OOPS header platform specific data. */ struct sof_ipc_dsp_oops_plat_hdr { From 14cdac7638c3367c39af206456ed2f2d37e59401 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 10:55:10 -0700 Subject: [PATCH 343/791] ASoC: SOF: ipc4-topology: repair struct kernel-doc comments Use the correct struct names in the kernel-doc comments. Add missing struct member descriptions. This prevents all kernel-doc warnings: Warning: ../sound/soc/sof/ipc4-topology.h:176 expecting prototype for struct sof_ipc4_multi_pipeline_data. Prototype was for struct ipc4_pipeline_set_state_data instead Warning: ../sound/soc/sof/ipc4-topology.h:307 expecting prototype for struct sof_ipc4_dma_config. Prototype was for struct sof_ipc4_dma_config_tlv instead Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260713175510.524728-2-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index a289c1d8f3ff..54bf0236a127 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { }; /** - * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data + * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data * @count: Number of pipelines to be triggered * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered */ @@ -210,7 +210,7 @@ struct sof_ipc4_available_audio_format { * @node_id: ID of Gateway Node * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) * @config_length: Length of gateway node configuration blob specified in #config_data - * config_data: Gateway node configuration blob + * @config_data: Gateway node configuration blob */ struct sof_copier_gateway_cfg { uint32_t node_id; @@ -285,7 +285,9 @@ struct sof_ipc4_dma_stream_ch_map { struct sof_ipc4_dma_config { uint8_t dma_method; uint8_t pre_allocated_by_host; + /* private: */ uint16_t rsvd; + /* public: */ uint32_t dma_channel_id; uint32_t stream_id; struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; @@ -296,7 +298,7 @@ struct sof_ipc4_dma_config { #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 /** - * struct sof_ipc4_dma_config: DMA configuration + * struct sof_ipc4_dma_config_tlv - DMA configuration * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size * @dma_config: actual DMA configuration @@ -321,6 +323,7 @@ struct sof_ipc4_alh_configuration_blob { * @data: IPC copier data * @copier_config: Copier + blob * @ipc_config_size: Size of copier_config + * @ipc_config_data: Copier module config data * @available_fmt: Available audio format * @frame_fmt: frame format * @msg: message structure for copier From b7adaa94e336f3b062ab85131d299d09e6d7ff47 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 28 Jul 2026 19:13:09 +0800 Subject: [PATCH 344/791] ALSA: usb-audio: Fix boot-time audio stuttering for USB Audio device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This USB Audio device (0x1e0b:0xd01e) exhibits audio stuttering during boot when playing audio. Once the system is fully booted, playback is normal. The device reports its isochronous endpoints with the Asynchronous sync type (bmAttributes = 0x03), which causes the driver to calculate nurbs = min(max_urbs, ...) = 3, providing only ~16ms of buffering. During boot, the higher system scheduling jitter (e.g., from init scripts, device enumeration, and driver probing) can exceed this buffer depth, causing audible stuttering. This patch adds a device-specific quirk (QUIRK_FLAG_PLAYBACK_URB_FIXUP) that applies two changes for this device: 1. Forces nurbs to MAX_URBS (12), providing sufficient buffering 2. Sets URB_ISO_ASAP flag for more consistent xHCI scheduling Both changes are required together for stable boot-time playback: - The larger buffer absorbs scheduling jitter during boot - URB_ISO_ASAP ensures consistent URB submission timing, preventing the xHCI scheduler from introducing variable delays Test methodology: - Without patch: reboot and play audio → stuttering audible in all tests (reproduced consistently across multiple attempts) - With nurbs=8 only: occasional minor stuttering observed after multiple tests (insufficient buffer depth) - With full patch (nurbs=12 + URB_ISO_ASAP): reboot and play audio → no stuttering observed (tested in 10+ reboot cycles without reproducing the issue) Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260728111309.1271834-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 7 ++++++- sound/usb/quirks.c | 3 +++ sound/usb/usbaudio.h | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 24cd7692bd01..54aeac7d087b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1232,7 +1232,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) /* try to use enough URBs to contain an entire ALSA buffer */ max_urbs = min((unsigned) MAX_URBS, MAX_QUEUE * packs_per_ms / urb_packs); - ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); + if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP) + ep->nurbs = MAX_URBS; + else + ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } /* allocate and initialize data urbs */ @@ -1256,6 +1259,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) goto out_of_memory; u->urb->pipe = ep->pipe; u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP) + u->urb->transfer_flags |= URB_ISO_ASAP; u->urb->interval = 1 << ep->datainterval; u->urb->context = u; u->urb->complete = snd_complete_urb; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 41149561aa06..52dbbdb7f9a1 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2415,6 +2415,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x1e0b, 0xd01e, /* Generic USB Audio Device */ + QUIRK_FLAG_PLAYBACK_URB_FIXUP), DEVICE_FLG(0x1ff7, 0x0f81, /* SC13A Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */ @@ -2624,6 +2626,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), + QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), NULL }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index e26f9092417e..dc1d0c8c9c80 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -254,6 +254,12 @@ extern bool snd_usb_skip_validation; * check being non-fatal and only disabling GET_CUR instead of the whole mixer. * The current volume will then be provided by the internal cache that stores * the last set volume + * QUIRK_FLAG_PLAYBACK_URB_FIXUP + * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. + * This is needed for devices that exhibit boot-time audio stuttering due + * to insufficient buffer depth combined with xHCI scheduling variability. + * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling + * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. */ enum { @@ -288,6 +294,7 @@ enum { QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, + QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, /* Please also edit snd_usb_audio_quirk_flag_names */ }; @@ -324,5 +331,6 @@ enum { #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) #define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) +#define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) #endif /* __USBAUDIO_H */ From 9167f260477b18ee9ffffc35fcf721f7255c444f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 20 Jul 2026 13:41:31 +0700 Subject: [PATCH 345/791] ASoC: soc-generic-dmaengine: Handle DMA channel request failures correctly Currently any dma_request_chan() failure other than -EPROBE_DEFER is silently ignored, since a missing channel is expected for devices that only support one DMA direction. Improve the handling of these failures by: - reporting failures when a configured DMA channel cannot be requested; - failing probe if neither playback nor capture obtains a DMA channel, since the PCM device would be unusable. Devices that legitimately support only one DMA direction continue to work as before. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260720064131.75156-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 467426d2b5e4..98ba9a836936 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -3,6 +3,7 @@ // Copyright (C) 2013, Analog Devices Inc. // Author: Lars-Peter Clausen +#include #include #include #include @@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, */ if (PTR_ERR(chan) == -EPROBE_DEFER) return -EPROBE_DEFER; + + bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); + bool name_exists_in_fw = false; + + if (has_fw_node) + name_exists_in_fw = device_property_match_string(dev, + "dma-names", + name) >= 0; + + if (has_fw_node && name_exists_in_fw) + dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", + name, PTR_ERR(chan)); + + if (has_fw_node && !name_exists_in_fw) + dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", + name, PTR_ERR(chan)); + + if (!has_fw_node) + dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", + name, PTR_ERR(chan)); + pcm->chan[i] = NULL; } else { pcm->chan[i] = chan; @@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) pcm->chan[1] = pcm->chan[0]; + if (!pcm->chan[0] && + !pcm->chan[1]) { + dev_err(dev, "no DMA channel found for either playback or capture\n"); + return -ENODEV; + } + return 0; } From 5413fb3ba54c4cffaeb7c6dd8220071afcdc6ab4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:02 +0700 Subject: [PATCH 346/791] ASoC: ti: ams-delta: Use dev_err_probe() for error handling Use dev_err_probe() to replace dev_err() followed by returning the error code. This keeps the error handling concise and suppresses log messages for deferred probe errors. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260716103911.77652-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/ams-delta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index 61252359d5cb..2759b39c4ebe 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -574,9 +574,9 @@ static int ams_delta_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); card->dev = NULL; - return ret; + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); } return 0; } From 7ae5829ab0f2f69e8a62689a147e16cd41d1b155 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:03 +0700 Subject: [PATCH 347/791] ASoC: ti: davinci-evm: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when reporting devm_snd_soc_register_card() failures. This suppresses unnecessary log messages for deferred probe errors. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260716103911.77652-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-evm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index ad514c2e5a25..3156d87a3a12 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&evm_soc_card, drvdata); ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); goto err_put; } From 1a1145d1b249acf36ff576f303ca6cdea4add99c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:04 +0700 Subject: [PATCH 348/791] ASoC: ti: j721e-evm: Return the original error from card name parsing Return the error from snd_soc_of_parse_card_name() directly instead of converting it to -ENODEV. The helper already logs the error, so drop the redundant dev_err(). Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260716103911.77652-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index c214ae0d7b95..b95ade8198eb 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -868,10 +868,9 @@ static int j721e_soc_probe(struct platform_device *pdev) card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); card->fully_routed = 1; - if (snd_soc_of_parse_card_name(card, "model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) + return ret; link_cnt = 0; conf_cnt = 0; From e0b2ab952d198a514222df6109e46f5b0727203d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:05 +0700 Subject: [PATCH 349/791] ASoC: ti: omap-abe-twl6040: Preserve error code and drop redundant log Return the original errors from the OF parsing helpers and remove the redundant error messages, as the helpers already report failures. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-abe-twl6040.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index 56aa4b22083b..dfa931071d81 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) card->dapm_routes = audio_map; card->num_dapm_routes = ARRAY_SIZE(audio_map); - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + ret = snd_soc_of_parse_card_name(card, "ti,model"); + if (ret) + return ret; ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + if (ret) return ret; - } dai_node = of_parse_phandle(node, "ti,mcpdm", 0); if (!dai_node) { From 3e8b2361c651d95b08b8f406d3c9f24b1f2d6746 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:06 +0700 Subject: [PATCH 350/791] ASoC: ti: omap-abe-twl6040: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when reporting devm_snd_soc_register_card() failures. This suppresses unnecessary log messages for deferred probe errors. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-abe-twl6040.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index dfa931071d81..05239ccc1f41 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -296,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(&pdev->dev, ret, + "devm_snd_soc_register_card() failed\n"); return ret; } From fa1d248f163880a0b4486d9a587ab79450846a8e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:07 +0700 Subject: [PATCH 351/791] ASoC: ti: omap-dmic: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when handling clock lookup failures. This preserves the original error code and suppresses unnecessary deferred probe error messages. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-dmic.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index b795b9f66b0e..c8d791f05ae4 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) mutex_init(&dmic->mutex); dmic->fclk = devm_clk_get(dmic->dev, "fck"); - if (IS_ERR(dmic->fclk)) { - dev_err(dmic->dev, "can't get fck\n"); - return -ENODEV; - } + if (IS_ERR(dmic->fclk)) + return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), + "can't get fck\n"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res) { From 492a53506ab27d59e9c1a1ac32adac954607b512 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:08 +0700 Subject: [PATCH 352/791] ASoC: ti: omap-hdmi: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when reporting devm_snd_soc_register_card() failures. This suppresses unnecessary log messages for deferred probe errors. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-hdmi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index e60f5b483fc5..1e3a9711e70c 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -375,10 +375,8 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->dev = dev; ret = devm_snd_soc_register_card(dev, card); - if (ret) { - dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); ad->card = card; snd_soc_card_set_drvdata(card, ad); From 23c89b7d91ccd9de3e2e6ab90202d52bfdaaff54 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:09 +0700 Subject: [PATCH 353/791] ASoC: ti: omap-twl4030: Return the original error code Return the error from snd_soc_of_parse_card_name() directly and drop the redundant error message since the helper already logs the failure. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-twl4030.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 4d80f8a7a947..2a80e44035d7 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -253,10 +253,9 @@ static int omap_twl4030_probe(struct platform_device *pdev) struct device_node *dai_node; struct property *prop; - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + ret = snd_soc_of_parse_card_name(card, "ti,model"); + if (ret) + return ret; dai_node = of_parse_phandle(node, "ti,mcbsp", 0); if (!dai_node) { From 31d9b5bc1abb5b0dc58e47c75f947d9706577aa9 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:10 +0700 Subject: [PATCH 354/791] ASoC: ti: omap-twl4030: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when reporting devm_snd_soc_register_card() failures. This suppresses unnecessary log messages for deferred probe errors. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-twl4030.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 2a80e44035d7..576f4d3615e8 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -306,11 +306,9 @@ static int omap_twl4030_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "devm_snd_soc_register_card() failed\n"); return 0; } From 924448b41b65fedac11f35b2191d9eb58ef56e08 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 16 Jul 2026 17:39:11 +0700 Subject: [PATCH 355/791] ASoC: ti: rx51: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() when reporting probe failures. This preserves the original error code and suppresses unnecessary log messages for deferred probe errors. Signed-off-by: bui duc phuc Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260716103911.77652-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/rx51.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index cfc23e0838c2..b13faf162c75 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -418,31 +418,27 @@ static int rx51_soc_probe(struct platform_device *pdev) pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, "tvout-selection", GPIOD_OUT_LOW); - if (IS_ERR(pdata->tvout_selection_gpio)) { - dev_err(card->dev, "could not get tvout selection gpio\n"); - return PTR_ERR(pdata->tvout_selection_gpio); - } + if (IS_ERR(pdata->tvout_selection_gpio)) + return dev_err_probe(card->dev, PTR_ERR(pdata->tvout_selection_gpio), + "could not get tvout selection gpio\n"); pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", GPIOD_OUT_HIGH); - if (IS_ERR(pdata->eci_sw_gpio)) { - dev_err(card->dev, "could not get eci switch gpio\n"); - return PTR_ERR(pdata->eci_sw_gpio); - } + if (IS_ERR(pdata->eci_sw_gpio)) + return dev_err_probe(card->dev, PTR_ERR(pdata->eci_sw_gpio), + "could not get eci switch gpio\n"); pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, "speaker-amplifier", GPIOD_OUT_LOW); - if (IS_ERR(pdata->speaker_amp_gpio)) { - dev_err(card->dev, "could not get speaker enable gpio\n"); - return PTR_ERR(pdata->speaker_amp_gpio); - } + if (IS_ERR(pdata->speaker_amp_gpio)) + return dev_err_probe(card->dev, PTR_ERR(pdata->speaker_amp_gpio), + "could not get speaker enable gpio\n"); err = devm_snd_soc_register_card(card->dev, card); - if (err) { - dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); - return err; - } + if (err) + return dev_err_probe(card->dev, err, + "snd_soc_register_card() failed\n"); return 0; } From a08ec8252596bdf5e6aebc825825259d97064759 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 09:45:16 +0200 Subject: [PATCH 356/791] ALSA: docs: Add description of usb-audio playback_urb_fixup quirk We missed the description for the recently introduced quirk bit QUIRK_FLAG_PLAYBACK_URB_FIXUP. A brief explanation is added here. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729074523.92761-2-tiwai@suse.de --- Documentation/sound/alsa-configuration.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index c4aef14a3147..b2171472e6cb 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2401,6 +2401,11 @@ quirk_flags disabling GET_CUR instead of the whole mixer. The current volume will then be provided by the internal cache that stores the last set volume + * bit 31: ``playback_urb_fixup`` + Some devices show the stuttering at playback, and this quirk + works around it by enforcing the fixed max URBs (12) instead of + the dynamic calculation from the buffer size, and passing the + `URB_ISO_ASAP` URB flag. This module supports multiple devices, autoprobe and hotplugging. From e6fc0af9dd9db148abfec8da5f85e23b719fabbc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 09:45:17 +0200 Subject: [PATCH 357/791] ALSA: usb-audio: Extend quirk_flags to 64bit Now we reached the limit of 32bit bitmap for quirk flags. In order to be future-ready, simply extend the flag bitmap to 64bit. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729074523.92761-3-tiwai@suse.de --- sound/usb/card.c | 2 +- sound/usb/quirks.c | 10 +++++----- sound/usb/quirks.h | 2 +- sound/usb/usbaudio.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index b36f513dccb9..24112e491779 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -715,7 +715,7 @@ static void snd_usb_init_quirk_flags(int idx, struct snd_usb_audio *chip) /* old style option found: the position-based integer value */ if (quirk_flags[idx] && - !kstrtou32(quirk_flags[idx], 0, &chip->quirk_flags)) { + !kstrtou64(quirk_flags[idx], 0, &chip->quirk_flags)) { snd_usb_apply_flag_dbg("module param", chip, chip->quirk_flags); return; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 52dbbdb7f9a1..77adefe4f9cb 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2145,7 +2145,7 @@ struct usb_string_match { struct usb_audio_quirk_flags_table { u32 id; - u32 flags; + u64 flags; const struct usb_string_match *usb_string_match; }; @@ -2638,7 +2638,7 @@ const char *snd_usb_quirk_flag_find_name(unsigned long index) return snd_usb_audio_quirk_flag_names[index]; } -u32 snd_usb_quirk_flags_from_name(const char *name) +u64 snd_usb_quirk_flags_from_name(const char *name) { int i; @@ -2647,7 +2647,7 @@ u32 snd_usb_quirk_flags_from_name(const char *name) for (i = 0; snd_usb_audio_quirk_flag_names[i]; i++) { if (strcasecmp(name, snd_usb_audio_quirk_flag_names[i]) == 0) - return BIT_U32(i); + return BIT_U64(i); } return 0; @@ -2705,7 +2705,7 @@ void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, { u16 chip_vid = USB_ID_VENDOR(chip->usb_id); u16 chip_pid = USB_ID_PRODUCT(chip->usb_id); - u32 mask_flags, unmask_flags, bit; + u64 mask_flags, unmask_flags, bit; char *p, *field, *flag; bool is_unmask; u16 vid, pid; @@ -2759,7 +2759,7 @@ void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, is_unmask = false; } - if (!kstrtou32(flag, 16, &bit)) { + if (!kstrtou64(flag, 16, &bit)) { if (is_unmask) unmask_flags |= bit; else diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index f24d6a5a197a..54f4cca8d7af 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -57,6 +57,6 @@ void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, const char *str); const char *snd_usb_quirk_flag_find_name(unsigned long flag); -u32 snd_usb_quirk_flags_from_name(const char *name); +u64 snd_usb_quirk_flags_from_name(const char *name); #endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index dc1d0c8c9c80..31e612500050 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -44,7 +44,7 @@ struct snd_usb_audio { atomic_t active; atomic_t shutdown; struct snd_refcount usage_count; - unsigned int quirk_flags; + u64 quirk_flags; unsigned int need_delayed_register:1; /* warn for delayed registration */ int num_interfaces; int last_iface; @@ -298,7 +298,7 @@ enum { /* Please also edit snd_usb_audio_quirk_flag_names */ }; -#define QUIRK_FLAG(x) BIT_U32(QUIRK_TYPE_ ## x) +#define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) #define QUIRK_FLAG_GET_SAMPLE_RATE QUIRK_FLAG(GET_SAMPLE_RATE) #define QUIRK_FLAG_SHARE_MEDIA_DEVICE QUIRK_FLAG(SHARE_MEDIA_DEVICE) From 76b588c0613c05287b9a28bb73d73287aebb886a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 09:45:18 +0200 Subject: [PATCH 358/791] ALSA: usb-audio: Make some quirk-string helpers local As snd_usb_quirk_flags_from_name() is used only locally, make it local. Also, drop the unused snd_usb_quirk_flag_find_name(), too. Only a code cleanup, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729074523.92761-4-tiwai@suse.de --- sound/usb/quirks.c | 10 +--------- sound/usb/quirks.h | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 77adefe4f9cb..3c1343da45b4 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2630,15 +2630,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { NULL }; -const char *snd_usb_quirk_flag_find_name(unsigned long index) -{ - if (index >= ARRAY_SIZE(snd_usb_audio_quirk_flag_names)) - return NULL; - - return snd_usb_audio_quirk_flag_names[index]; -} - -u64 snd_usb_quirk_flags_from_name(const char *name) +static u64 snd_usb_quirk_flags_from_name(const char *name) { int i; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 54f4cca8d7af..0a723c266bb4 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -56,7 +56,4 @@ void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip); void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, const char *str); -const char *snd_usb_quirk_flag_find_name(unsigned long flag); -u64 snd_usb_quirk_flags_from_name(const char *name); - #endif /* __USBAUDIO_QUIRKS_H */ From 273806f38ce95c95c288a03d3de1e423cd3e5543 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Wed, 29 Jul 2026 15:09:35 +0800 Subject: [PATCH 359/791] ALSA: hda/conexant: Add NULL check for dc_mode_path snd_hda_add_new_path() returns NULL when no path exists between the given NIDs, but olpc_xo_update_mic_pins() passes dc_mode_path straight to snd_hda_activate_path() which dereferences it without checking. Add the missing NULL guards, same as the local path variable already has in the same function. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260729070935.548050-2-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/conexant.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 40da2832ba66..5ea4c74715c5 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -451,7 +451,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec) if (!spec->dc_enable) { /* disable DC bias path and pin for port F */ update_mic_pin(codec, 0x1e, 0); - snd_hda_activate_path(codec, spec->dc_mode_path, false, false); + if (spec->dc_mode_path) + snd_hda_activate_path(codec, spec->dc_mode_path, false, false); /* update port B (ext mic) and C (int mic) */ /* OLPC defers mic widget control until when capture is @@ -487,7 +488,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec) update_mic_pin(codec, 0x1b, 0); /* enable DC bias path and pin */ update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0); - snd_hda_activate_path(codec, spec->dc_mode_path, true, false); + if (spec->dc_mode_path) + snd_hda_activate_path(codec, spec->dc_mode_path, true, false); } } From a8023598f3ae3c129a53aa89c7c664265377bfcb Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:20 +0700 Subject: [PATCH 360/791] ASoC: sunxi: sun4i-codec: Use dev_err_probe() for probe error handling Use dev_err_probe() for probe error handling to simplify the error paths and handle -EPROBE_DEFER correctly. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260715095525.40668-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 70 ++++++++++++++--------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index f4e22af594fa..05308df3ae5b 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2316,67 +2316,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, quirks->regmap_config); - if (IS_ERR(scodec->regmap)) { - dev_err(&pdev->dev, "Failed to create our regmap\n"); - return PTR_ERR(scodec->regmap); - } + if (IS_ERR(scodec->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), + "Failed to create our regmap\n"); /* Get the clocks from the DT */ scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); - if (IS_ERR(scodec->clk_apb)) { - dev_err(&pdev->dev, "Failed to get the APB clock\n"); - return PTR_ERR(scodec->clk_apb); - } + if (IS_ERR(scodec->clk_apb)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), + "Failed to get the APB clock\n"); scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); - if (IS_ERR(scodec->clk_module)) { - dev_err(&pdev->dev, "Failed to get the module clock\n"); - return PTR_ERR(scodec->clk_module); - } + if (IS_ERR(scodec->clk_module)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), + "Failed to get the module clock\n"); if (quirks->has_reset) { scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); - if (IS_ERR(scodec->rst)) { - dev_err(&pdev->dev, "Failed to get reset control\n"); - return PTR_ERR(scodec->rst); - } + if (IS_ERR(scodec->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), + "Failed to get reset control\n"); } scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", GPIOD_OUT_LOW); - if (IS_ERR(scodec->gpio_pa)) { - ret = PTR_ERR(scodec->gpio_pa); - dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); - return ret; - } + if (IS_ERR(scodec->gpio_pa)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), + "Failed to get pa gpio\n"); + scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); - if (IS_ERR(scodec->gpio_hp)) { - ret = PTR_ERR(scodec->gpio_hp); - dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); - return ret; - } + if (IS_ERR(scodec->gpio_hp)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), + "Failed to get hp-det gpio\n"); /* reg_field setup */ scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, scodec->regmap, quirks->reg_adc_fifoc); - if (IS_ERR(scodec->reg_adc_fifoc)) { - ret = PTR_ERR(scodec->reg_adc_fifoc); - dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", - ret); - return ret; - } + if (IS_ERR(scodec->reg_adc_fifoc)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), + "Failed to create regmap fields\n"); scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, scodec->regmap, quirks->reg_dac_fifoc); - if (IS_ERR(scodec->reg_dac_fifoc)) { - ret = PTR_ERR(scodec->reg_dac_fifoc); - dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", - ret); - return ret; - } + if (IS_ERR(scodec->reg_dac_fifoc)) + return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), + "Failed to create regmap fields\n"); /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; @@ -2422,10 +2409,9 @@ static int sun4i_codec_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, scodec); ret = snd_soc_register_card(card); - if (ret) { - dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register our card\n"); return 0; } From de6bda2088e4b72e01538b5b116fbbb1a49dfc71 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:21 +0700 Subject: [PATCH 361/791] ASoC: sunxi: sun4i-codec: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260715095525.40668-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 05308df3ae5b..71f7a1fedd88 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2380,31 +2380,22 @@ static int sun4i_codec_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, &sun4i_codec_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register our codec\n"); + if (ret) return ret; - } ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_codec_component, &dummy_cpu_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register our DAI\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); + if (ret) return ret; - } card = quirks->create_card(&pdev->dev); - if (IS_ERR(card)) { - ret = PTR_ERR(card); - dev_err(&pdev->dev, "Failed to create our card\n"); - return ret; - } + if (IS_ERR(card)) + return PTR_ERR(card); snd_soc_card_set_drvdata(card, scodec); From 3e2262df8460742b8a0cb3796e3780691e37be1f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:22 +0700 Subject: [PATCH 362/791] ASoC: sunxi: sun4i-i2s: Use dev_err_probe() for probe error handling Use dev_err_probe() for probe error handling to simplify the error paths and handle -EPROBE_DEFER correctly. Signed-off-by: bui duc phuc Reviewed-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260715095525.40668-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-i2s.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 40de99a34bc3..716fa4d872ff 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) } i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); - if (IS_ERR(i2s->bus_clk)) { - dev_err(&pdev->dev, "Can't get our bus clock\n"); - return PTR_ERR(i2s->bus_clk); - } + if (IS_ERR(i2s->bus_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), + "Can't get our bus clock\n"); i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, i2s->variant->sun4i_i2s_regmap); - if (IS_ERR(i2s->regmap)) { - dev_err(&pdev->dev, "Regmap initialisation failed\n"); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), + "Regmap initialisation failed\n"); i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); - if (IS_ERR(i2s->mod_clk)) { - dev_err(&pdev->dev, "Can't get our mod clock\n"); - return PTR_ERR(i2s->mod_clk); - } + if (IS_ERR(i2s->mod_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), + "Can't get our mod clock\n"); if (i2s->variant->has_reset) { i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->rst)) { - dev_err(&pdev->dev, "Failed to get reset control\n"); - return PTR_ERR(i2s->rst); - } + if (IS_ERR(i2s->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), + "Failed to get reset control\n"); } if (!IS_ERR(i2s->rst)) { From ac95bdb9824699fd1196d91caa691afafcc0edf4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:23 +0700 Subject: [PATCH 363/791] ASoC: sunxi: sun4i-spdif: Use dev_err_probe() for probe error handling Use dev_err_probe() for probe error handling to simplify the error paths and handle -EPROBE_DEFER correctly. Signed-off-by: bui duc phuc Reviewed-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260715095525.40668-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-spdif.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index c2ec19437cd7..d979bb00312f 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) host->regmap = devm_regmap_init_mmio(&pdev->dev, base, &sun4i_spdif_regmap_config); - if (IS_ERR(host->regmap)) { - dev_err(&pdev->dev, "failed to initialise regmap.\n"); - return PTR_ERR(host->regmap); - } + if (IS_ERR(host->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), + "failed to initialise regmap.\n"); /* Clocks */ host->apb_clk = devm_clk_get(&pdev->dev, "apb"); - if (IS_ERR(host->apb_clk)) { - dev_err(&pdev->dev, "failed to get a apb clock.\n"); - return PTR_ERR(host->apb_clk); - } + if (IS_ERR(host->apb_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), + "failed to get a apb clock.\n"); if (quirks->tx_clk_name) tx_clk_name = quirks->tx_clk_name; host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); - if (IS_ERR(host->spdif_clk)) { - dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", - tx_clk_name); - return PTR_ERR(host->spdif_clk); - } + if (IS_ERR(host->spdif_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), + "failed to get the \"%s\" clock.\n", + tx_clk_name); host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; host->dma_params_tx.maxburst = 8; From 76a770c30bcdb0447247c578e0283336a20b6990 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:24 +0700 Subject: [PATCH 364/791] ASoC: sunxi: sun50i-codec-analog: Improve probe error handling Drop the redundant error message after devm_platform_ioremap_resource(), which already reports failures, and use dev_err_probe() for regmap initialization errors. Signed-off-by: bui duc phuc Reviewed-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260715095525.40668-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 9f5b067a8ccc..50c0dee8c433 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) bool enable; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - dev_err(&pdev->dev, "Failed to map the registers\n"); + if (IS_ERR(base)) return PTR_ERR(base); - } regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); - if (IS_ERR(regmap)) { - dev_err(&pdev->dev, "Failed to create regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "Failed to create regmap\n"); enable = device_property_read_bool(&pdev->dev, "allwinner,internal-bias-resistor"); From bb0d825c8ad9d35abcb4b4f88d8ca91b0755ee4b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 15 Jul 2026 16:55:25 +0700 Subject: [PATCH 365/791] ASoC: sunxi: sun8i-codec-analog: Improve probe error handling Drop the redundant error message after devm_platform_ioremap_resource(), which already reports failures, and use dev_err_probe() for regmap initialization errors. Signed-off-by: bui duc phuc Reviewed-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260715095525.40668-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec-analog.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index 2024e2b2553e..3335855c6311 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) void __iomem *base; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - dev_err(&pdev->dev, "Failed to map the registers\n"); + if (IS_ERR(base)) return PTR_ERR(base); - } regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); - if (IS_ERR(regmap)) { - dev_err(&pdev->dev, "Failed to create regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "Failed to create regmap\n"); return devm_snd_soc_register_component(&pdev->dev, &sun8i_codec_analog_cmpnt_drv, From 9af2c1d4f84748fbfc0d71de78e3d477d0cfa6e0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:42:58 +0000 Subject: [PATCH 366/791] ASoC: ti: omap-hdmi: remove unused *card No one is using ad->card. Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jyqpi0d9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-hdmi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index e60f5b483fc5..d6b7a97a1fd9 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -24,8 +24,6 @@ #define DRV_NAME "omap-hdmi-audio" struct hdmi_audio_data { - struct snd_soc_card *card; - const struct omap_hdmi_audio_ops *ops; struct device *dssdev; struct snd_dmaengine_dai_dma_data dma_data; @@ -380,7 +378,6 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) return ret; } - ad->card = card; snd_soc_card_set_drvdata(card, ad); dev_set_drvdata(dev, ad); From 4bd2afcdc837244ed362a3e96ae41806e8fee541 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:43:08 +0000 Subject: [PATCH 367/791] ASoC: ti: tidyup not to use *card on rx51_soc_probe() struct snd_soc_card will be capsuled soon, its member will not be able to access from non soc-card.c. To reduce the difference during conversion, replace dev. - card->dev, ... + dev, ... No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Link: https://patch.msgid.link/87ik69i0d0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ti/rx51.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index cfc23e0838c2..09de571c898c 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { static int rx51_soc_probe(struct platform_device *pdev) { struct rx51_audio_pdata *pdata; - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct snd_soc_card *card = &rx51_sound_card; int err; if (!of_machine_is_compatible("nokia,omap3-n900")) return -ENODEV; - card->dev = &pdev->dev; + card->dev = dev; if (np) { struct device_node *dai_node; dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); if (!dai_node) { - dev_err(card->dev, "McBSP node is not provided\n"); + dev_err(dev, "McBSP node is not provided\n"); return -EINVAL; } rx51_dai[0].cpus->dai_name = NULL; @@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); if (!dai_node) { - dev_err(card->dev, "Codec node is not provided\n"); + dev_err(dev, "Codec node is not provided\n"); return -EINVAL; } rx51_dai[0].codecs->name = NULL; @@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); if (!dai_node) { - dev_err(card->dev, "Auxiliary Codec node is not provided\n"); + dev_err(dev, "Auxiliary Codec node is not provided\n"); return -EINVAL; } rx51_aux_dev[0].dlc.name = NULL; @@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); if (!dai_node) { - dev_err(card->dev, "Headphone amplifier node is not provided\n"); + dev_err(dev, "Headphone amplifier node is not provided\n"); return -EINVAL; } rx51_aux_dev[1].dlc.name = NULL; @@ -409,38 +410,38 @@ static int rx51_soc_probe(struct platform_device *pdev) rx51_codec_conf[1].dlc.of_node = dai_node; } - pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (pdata == NULL) return -ENOMEM; snd_soc_card_set_drvdata(card, pdata); - pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, + pdata->tvout_selection_gpio = devm_gpiod_get(dev, "tvout-selection", GPIOD_OUT_LOW); if (IS_ERR(pdata->tvout_selection_gpio)) { - dev_err(card->dev, "could not get tvout selection gpio\n"); + dev_err(dev, "could not get tvout selection gpio\n"); return PTR_ERR(pdata->tvout_selection_gpio); } - pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", + pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", GPIOD_OUT_HIGH); if (IS_ERR(pdata->eci_sw_gpio)) { - dev_err(card->dev, "could not get eci switch gpio\n"); + dev_err(dev, "could not get eci switch gpio\n"); return PTR_ERR(pdata->eci_sw_gpio); } - pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, + pdata->speaker_amp_gpio = devm_gpiod_get(dev, "speaker-amplifier", GPIOD_OUT_LOW); if (IS_ERR(pdata->speaker_amp_gpio)) { - dev_err(card->dev, "could not get speaker enable gpio\n"); + dev_err(dev, "could not get speaker enable gpio\n"); return PTR_ERR(pdata->speaker_amp_gpio); } - err = devm_snd_soc_register_card(card->dev, card); + err = devm_snd_soc_register_card(dev, card); if (err) { - dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); + dev_err(dev, "snd_soc_register_card failed (%d)\n", err); return err; } From 3596abbd7bb34d159069511663962be18c191ee5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:43:14 +0000 Subject: [PATCH 368/791] ASoC: ti: ams-delta: use &pdev->dev instead of card->dev ams_delta_probe() will be updated when Card capsuling. To makes its review easy, use &pdev->dev instead of card->dev. These are same card->dev = &pdev->dev; No functional change, but is preparation for Card capsuling. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5lti0cu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ti/ams-delta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index 61252359d5cb..6a0a3a2073bf 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -562,12 +562,12 @@ static int ams_delta_probe(struct platform_device *pdev) card->dev = &pdev->dev; - handset_mute = devm_gpiod_get(card->dev, "handset_mute", + handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", GPIOD_OUT_HIGH); if (IS_ERR(handset_mute)) return PTR_ERR(handset_mute); - handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", + handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", GPIOD_OUT_HIGH); if (IS_ERR(handsfree_mute)) return PTR_ERR(handsfree_mute); From 3906ea776a08d13bd5ef221b1d8112a49988c644 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:11 -0700 Subject: [PATCH 369/791] ASoC: tlv320aic32x4: remove global header with platform data Commit 69d5b62c4bde ("ASoC: codec: tlv320aic32x4: Drop aic32x4_pdata usage") removed support for platform data, but left a global header file with #defines and platform data structure. Move the contents to the driver-private header. Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-1-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- include/sound/tlv320aic32x4.h | 43 -------------------------------- sound/soc/codecs/tlv320aic32x4.c | 5 +++- sound/soc/codecs/tlv320aic32x4.h | 27 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 44 deletions(-) delete mode 100644 include/sound/tlv320aic32x4.h diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h deleted file mode 100644 index b779d671a995..000000000000 --- a/include/sound/tlv320aic32x4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data - * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin - */ - -#ifndef _AIC32X4_PDATA_H -#define _AIC32X4_PDATA_H - -#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 -#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 -#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 -#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 -#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 - -#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 -#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 - -/* GPIO API */ -#define AIC32X4_MFPX_DEFAULT_VALUE 0xff - -#define AIC32X4_MFP1_DIN_DISABLED 0 -#define AIC32X4_MFP1_DIN_ENABLED 0x2 -#define AIC32X4_MFP1_GPIO_IN 0x4 - -#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 - -#define AIC32X4_MFP_GPIO_ENABLED 0x4 - -#define AIC32X4_MFP5_GPIO_DISABLED 0x0 -#define AIC32X4_MFP5_GPIO_INPUT 0x8 -#define AIC32X4_MFP5_GPIO_OUTPUT 0xc -#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 - -struct aic32x4_setup_data { - unsigned int gpio_func[5]; -}; -#endif diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index d85094557215..da582f2c940c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -28,10 +28,13 @@ #include #include #include -#include #include "tlv320aic32x4.h" +struct aic32x4_setup_data { + unsigned int gpio_func[5]; +}; + struct aic32x4_priv { struct regmap *regmap; u32 power_cfg; diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index f68a846ef61d..8eb9c6a4c99e 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -234,4 +234,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 #define AIC32X4_MAX_PLL_CLKIN 20000000 +#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 + +#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 + +/* GPIO API */ +#define AIC32X4_MFPX_DEFAULT_VALUE 0xff + +#define AIC32X4_MFP1_DIN_DISABLED 0 +#define AIC32X4_MFP1_DIN_ENABLED 0x2 +#define AIC32X4_MFP1_GPIO_IN 0x4 + +#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 + +#define AIC32X4_MFP_GPIO_ENABLED 0x4 + +#define AIC32X4_MFP5_GPIO_DISABLED 0x0 +#define AIC32X4_MFP5_GPIO_INPUT 0x8 +#define AIC32X4_MFP5_GPIO_OUTPUT 0xc +#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 + #endif /* _TLV320AIC32X4_H */ From 5143ae134636577d45de02de925da9f640259e6b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:12 -0700 Subject: [PATCH 370/791] ASoC: tlv320aic32x4: do not allocate gpio config separately Now that the driver only works with device tree we do not need to keep GPIO config separate from the driver structure. Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-2-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 47 +++++++++++++------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index da582f2c940c..87b155599e94 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -31,10 +31,6 @@ #include "tlv320aic32x4.h" -struct aic32x4_setup_data { - unsigned int gpio_func[5]; -}; - struct aic32x4_priv { struct regmap *regmap; u32 power_cfg; @@ -48,7 +44,8 @@ struct aic32x4_priv { struct regulator *supply_dv; struct regulator *supply_av; - struct aic32x4_setup_data *setup; + unsigned int gpio_func[5]; + struct device *dev; enum aic32x4_type type; @@ -959,41 +956,41 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) /* setup GPIO functions */ /* MFP1 */ - if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { + if (aic32x4->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { snd_soc_component_write(component, AIC32X4_DINCTL, - aic32x4->setup->gpio_func[0]); + aic32x4->gpio_func[0]); snd_soc_add_component_controls(component, aic32x4_mfp1, ARRAY_SIZE(aic32x4_mfp1)); } /* MFP2 */ - if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { + if (aic32x4->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { snd_soc_component_write(component, AIC32X4_DOUTCTL, - aic32x4->setup->gpio_func[1]); + aic32x4->gpio_func[1]); snd_soc_add_component_controls(component, aic32x4_mfp2, ARRAY_SIZE(aic32x4_mfp2)); } /* MFP3 */ - if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { + if (aic32x4->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { snd_soc_component_write(component, AIC32X4_SCLKCTL, - aic32x4->setup->gpio_func[2]); + aic32x4->gpio_func[2]); snd_soc_add_component_controls(component, aic32x4_mfp3, ARRAY_SIZE(aic32x4_mfp3)); } /* MFP4 */ - if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { + if (aic32x4->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { snd_soc_component_write(component, AIC32X4_MISOCTL, - aic32x4->setup->gpio_func[3]); + aic32x4->gpio_func[3]); snd_soc_add_component_controls(component, aic32x4_mfp4, ARRAY_SIZE(aic32x4_mfp4)); } /* MFP5 */ - if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { + if (aic32x4->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { snd_soc_component_write(component, AIC32X4_GPIOCTL, - aic32x4->setup->gpio_func[4]); + aic32x4->gpio_func[4]); snd_soc_add_component_controls(component, aic32x4_mfp5, ARRAY_SIZE(aic32x4_mfp5)); } @@ -1016,8 +1013,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (aic32x4->setup) - aic32x4_setup_gpios(component); + aic32x4_setup_gpios(component); clk_set_parent(clocks[0].clk, clocks[1].clk); clk_set_parent(clocks[2].clk, clocks[3].clk); @@ -1173,8 +1169,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (aic32x4->setup) - aic32x4_setup_gpios(component); + aic32x4_setup_gpios(component); clk_set_parent(clocks[0].clk, clocks[1].clk); clk_set_parent(clocks[2].clk, clocks[3].clk); @@ -1224,14 +1219,8 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) { - struct aic32x4_setup_data *aic32x4_setup; int ret; - aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), - GFP_KERNEL); - if (!aic32x4_setup) - return -ENOMEM; - ret = of_property_match_string(np, "clock-names", "mclk"); if (ret < 0) return -EINVAL; @@ -1248,9 +1237,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); } - if (of_property_read_u32_array(np, "aic32x4-gpio-func", - aic32x4_setup->gpio_func, 5) >= 0) - aic32x4->setup = aic32x4_setup; + for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) + aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; + of_property_read_u32_array(np, "aic32x4-gpio-func", + aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); + return 0; } From 4ef61518b03d32bea9b13728eebb0673aeeee52d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:13 -0700 Subject: [PATCH 371/791] ASoC: tlv320aic32x4: consolidate programming functions Consolidate setting up of GPIO functions instead of repeating almost the same code block 5 times. Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-3-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 88 ++++++++++++-------------------- 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 87b155599e94..6b3ddb89f692 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -222,25 +222,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, return 0; }; -static const struct snd_kcontrol_new aic32x4_mfp1[] = { - SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), -}; - -static const struct snd_kcontrol_new aic32x4_mfp2[] = { - SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), -}; - -static const struct snd_kcontrol_new aic32x4_mfp3[] = { - SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), -}; - -static const struct snd_kcontrol_new aic32x4_mfp4[] = { - SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), -}; - -static const struct snd_kcontrol_new aic32x4_mfp5[] = { - SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, - aic32x4_set_mfp5_gpio), +static const struct { + unsigned int reg; + struct snd_kcontrol_new ctrl; +} aic32x4_mfp_cfg[] = { + { + .reg = AIC32X4_DINCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), + }, + { + .reg = AIC32X4_DOUTCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), + }, + { + .reg = AIC32X4_SCLKCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), + }, + { + .reg = AIC32X4_MISOCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), + }, + { + .reg = AIC32X4_GPIOCTL, + .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, + aic32x4_set_mfp5_gpio), + }, }; /* 0dB min, 0.5dB steps */ @@ -955,44 +961,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); /* setup GPIO functions */ - /* MFP1 */ - if (aic32x4->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_DINCTL, - aic32x4->gpio_func[0]); - snd_soc_add_component_controls(component, aic32x4_mfp1, - ARRAY_SIZE(aic32x4_mfp1)); - } + BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); + for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { + if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) + continue; - /* MFP2 */ - if (aic32x4->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_DOUTCTL, - aic32x4->gpio_func[1]); - snd_soc_add_component_controls(component, aic32x4_mfp2, - ARRAY_SIZE(aic32x4_mfp2)); - } - - /* MFP3 */ - if (aic32x4->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_SCLKCTL, - aic32x4->gpio_func[2]); - snd_soc_add_component_controls(component, aic32x4_mfp3, - ARRAY_SIZE(aic32x4_mfp3)); - } - - /* MFP4 */ - if (aic32x4->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_MISOCTL, - aic32x4->gpio_func[3]); - snd_soc_add_component_controls(component, aic32x4_mfp4, - ARRAY_SIZE(aic32x4_mfp4)); - } - - /* MFP5 */ - if (aic32x4->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { - snd_soc_component_write(component, AIC32X4_GPIOCTL, - aic32x4->gpio_func[4]); - snd_soc_add_component_controls(component, aic32x4_mfp5, - ARRAY_SIZE(aic32x4_mfp5)); + snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, + aic32x4->gpio_func[i]); + snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); } } From 2ba6a4dba620bcee0f2210f532b25510a0872cf1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:14 -0700 Subject: [PATCH 372/791] ASoC: tlv320aic32x4: move regmap_config into i2c and spi drivers Move regmap_config definitions to be static const structures in tlv320aic32x4-i2c.c and tlv320aic32x4-spi.c instead of dynamically modifying a shared base regmap_config at runtime during probe. Export aic32x4_regmap_pages so both bus drivers can reference page ranges. In addition, validate regmap initialization immediately upon creation in both bus probe routines and remove the redundant error check from core aic32x4_probe. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-4-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4-i2c.c | 16 +++++++++++----- sound/soc/codecs/tlv320aic32x4-spi.c | 20 +++++++++++++------- sound/soc/codecs/tlv320aic32x4.c | 13 ++----------- sound/soc/codecs/tlv320aic32x4.h | 2 +- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index 449353d5f088..e031eaaa2f7f 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -16,17 +16,23 @@ #include "tlv320aic32x4.h" +static const struct regmap_config aic32x4_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AIC32X4_REFPOWERUP, + .ranges = aic32x4_regmap_pages, + .num_ranges = 1, +}; + static int aic32x4_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; - struct regmap_config config; enum aic32x4_type type; - config = aic32x4_regmap_config; - config.reg_bits = 8; - config.val_bits = 8; + regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - regmap = devm_regmap_init_i2c(i2c, &config); type = (uintptr_t)i2c_get_match_data(i2c); return aic32x4_probe(&i2c->dev, regmap, type); diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c index 92246243ff94..4f842260e325 100644 --- a/sound/soc/codecs/tlv320aic32x4-spi.c +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -16,19 +16,25 @@ #include "tlv320aic32x4.h" +static const struct regmap_config aic32x4_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = 0x01, + .max_register = AIC32X4_REFPOWERUP, + .ranges = aic32x4_regmap_pages, + .num_ranges = 1, +}; + static int aic32x4_spi_probe(struct spi_device *spi) { struct regmap *regmap; - struct regmap_config config; enum aic32x4_type type; - config = aic32x4_regmap_config; - config.reg_bits = 7; - config.pad_bits = 1; - config.val_bits = 8; - config.read_flag_mask = 0x01; + regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - regmap = devm_regmap_init_spi(spi, &config); type = (uintptr_t)spi_get_device_match_data(spi); return aic32x4_probe(&spi->dev, regmap, type); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 6b3ddb89f692..72be757f559c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -581,7 +581,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, }; -static const struct regmap_range_cfg aic32x4_regmap_pages[] = { +const struct regmap_range_cfg aic32x4_regmap_pages[] = { { .selector_reg = 0, .selector_mask = 0xff, @@ -591,13 +591,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { .range_max = AIC32X4_REFPOWERUP, }, }; - -const struct regmap_config aic32x4_regmap_config = { - .max_register = AIC32X4_REFPOWERUP, - .ranges = aic32x4_regmap_pages, - .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), -}; -EXPORT_SYMBOL(aic32x4_regmap_config); +EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) @@ -1326,9 +1320,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, struct device_node *np = dev->of_node; int ret; - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); if (aic32x4 == NULL) diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 8eb9c6a4c99e..95d010af3d5a 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -16,7 +16,7 @@ enum aic32x4_type { AIC32X4_TYPE_TAS2505, }; -extern const struct regmap_config aic32x4_regmap_config; +extern const struct regmap_range_cfg aic32x4_regmap_pages[]; int aic32x4_probe(struct device *dev, struct regmap *regmap, enum aic32x4_type type); void aic32x4_remove(struct device *dev); From dfefa7dc25ee58a38b3a6d8777ba952cda5aeaac Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:15 -0700 Subject: [PATCH 373/791] ASoC: tlv320aic32x4: do not make clocks bulk data static Declaring local clk_bulk_data structures as static inside functions is bad practice even if the driver is currently a singleton, because it relies on mutable function-static state and interferes with multi-instance safety or clean re-probing. Remove static from the clocks bulk data arrays across the driver. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-5-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 72be757f559c..690acfc005c2 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -717,7 +717,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned long adc_clock_rate, dac_clock_rate; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "pll" }, { .id = "nadc" }, { .id = "madc" }, @@ -886,7 +886,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "madc" }, { .id = "mdac" }, { .id = "bdiv" }, @@ -972,7 +972,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, @@ -1128,7 +1128,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) u32 tmp_reg; int ret; - static struct clk_bulk_data clocks[] = { + struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, { .id = "bdiv" }, From e86ba7c5f24fd0346284f96093aebc128859eb31 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:16 -0700 Subject: [PATCH 374/791] ASoC: tlv320aic32x4: factor out rate configuration helper Factor out sample-rate dependent parameter setup and processing block configuration into a separate helper function aic32x4_configure_rate. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-6-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 75 +++++++++++++++++++------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 690acfc005c2..8d3f2d5c6128 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -703,6 +703,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, return 0; } +static int aic32x4_configure_rate(struct snd_soc_component *component, + unsigned int rate, u8 *aosr, u8 *adc_rc, + u8 *dac_rc, u8 *dosr_inc) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 prb_rx, prb_tx; + + if (rate <= 48000) { + *aosr = 128; + *adc_rc = 6; + *dac_rc = 8; + *dosr_inc = 8; + prb_rx = 1; + prb_tx = 1; + } else if (rate <= 96000) { + *aosr = 64; + *adc_rc = 6; + *dac_rc = 8; + *dosr_inc = 4; + prb_rx = 1; + prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; + } else if (rate == 192000) { + *aosr = 32; + *adc_rc = 3; + *dac_rc = 4; + *dosr_inc = 2; + prb_rx = 13; + prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; + } else { + dev_err(component->dev, "Sampling rate %u not supported\n", rate); + return -EINVAL; + } + + if (aic32x4->type == AIC32X4_TYPE_TAS2505) + prb_rx = 0; + + return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); +} + static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned int sample_rate, unsigned int channels, unsigned int bit_depth) @@ -729,37 +768,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, if (ret) return ret; - if (sample_rate <= 48000) { - aosr = 128; - adc_resource_class = 6; - dac_resource_class = 8; - dosr_increment = 8; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 1, 1); - } else if (sample_rate <= 96000) { - aosr = 64; - adc_resource_class = 6; - dac_resource_class = 8; - dosr_increment = 4; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 1, 9); - } else if (sample_rate == 192000) { - aosr = 32; - adc_resource_class = 3; - dac_resource_class = 4; - dosr_increment = 2; - if (aic32x4->type == AIC32X4_TYPE_TAS2505) - aic32x4_set_processing_blocks(component, 0, 1); - else - aic32x4_set_processing_blocks(component, 13, 19); - } else { - dev_err(component->dev, "Sampling rate not supported\n"); - return -EINVAL; - } + ret = aic32x4_configure_rate(component, sample_rate, &aosr, + &adc_resource_class, &dac_resource_class, + &dosr_increment); + if (ret) + return ret; /* PCM over I2S is always 2-channel */ if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) From c3ac0aa6c3100f973b44bb0b0882f435075ed215 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:17 -0700 Subject: [PATCH 375/791] ASoC: tlv320aic32x4: clean up driver code formatting and logging Clean up coding style, SPDX comments, logging calls, and macro definitions across the tlv320aic32x4 driver files: - Convert SPDX comment blocks to // style in bus and clk drivers. - Replace printk(KERN_ERR/DEBUG ...) calls with dev_err/dev_dbg. - Replace msleep(10) with usleep_range(10000, 20000) in the clock driver. - Parenthesize parameters in AIC32X4_REG macro. - Clean up double blank lines and null pointer checks. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-7-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4-clk.c | 68 ++++--- sound/soc/codecs/tlv320aic32x4-i2c.c | 5 +- sound/soc/codecs/tlv320aic32x4-spi.c | 5 +- sound/soc/codecs/tlv320aic32x4.c | 260 +++++++++++++-------------- sound/soc/codecs/tlv320aic32x4.h | 3 +- 5 files changed, 158 insertions(+), 183 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index 5c0a76a4a106..deed61650e09 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Clock Tree for the Texas Instruments TLV320AIC32x4 * * Copyright 2019 Annaliese McDermond @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) struct clk_aic32x4 *pll = to_clk_aic32x4(hw); regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); + AIC32X4_PLLEN, 0); } static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) @@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) } static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, - struct clk_aic32x4_pll_muldiv *settings) + struct clk_aic32x4_pll_muldiv *settings) { /* Change to use regmap_bulk_read? */ unsigned int val; @@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, } static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, - struct clk_aic32x4_pll_muldiv *settings) + struct clk_aic32x4_pll_muldiv *settings) { int ret; /* Change to use regmap_bulk_write for some if not all? */ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLL_R_MASK, settings->r); + AIC32X4_PLL_R_MASK, settings->r); if (ret < 0) return ret; ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, - AIC32X4_PLL_P_MASK, - settings->p << AIC32X4_PLL_P_SHIFT); + AIC32X4_PLL_P_MASK, + settings->p << AIC32X4_PLL_P_SHIFT); if (ret < 0) return ret; @@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, return 0; } -static unsigned long clk_aic32x4_pll_calc_rate( - struct clk_aic32x4_pll_muldiv *settings, - unsigned long parent_rate) +static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, + unsigned long parent_rate) { u64 rate; /* * We scale j by 10000 to account for the decimal part of P and divide * it back out later. */ - rate = (u64) parent_rate * settings->r * - ((settings->j * 10000) + settings->d); + rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); - return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); + return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); } static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - unsigned long rate, unsigned long parent_rate) + unsigned long rate, unsigned long parent_rate) { u64 multiplier; @@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, * of the multiplier. This is because we can't do floating point * math in the kernel. */ - multiplier = (u64) rate * settings->p * 10000; + multiplier = (u64)rate * settings->p * 10000; do_div(multiplier, parent_rate); /* * J can't be over 64, so R can scale this. * R can't be greater than 4. */ - settings->r = ((u32) multiplier / 640000) + 1; + settings->r = ((u32)multiplier / 640000) + 1; if (settings->r > 4) return -1; do_div(multiplier, settings->r); @@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, return -1; /* Figure out the integer part, J, and the fractional part, D. */ - settings->j = (u32) multiplier / 10000; - settings->d = (u32) multiplier % 10000; + settings->j = (u32)multiplier / 10000; + settings->d = (u32)multiplier % 10000; return 0; } static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *pll = to_clk_aic32x4(hw); struct clk_aic32x4_pll_muldiv settings; @@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, } static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) + unsigned long rate, + unsigned long parent_rate) { struct clk_aic32x4 *pll = to_clk_aic32x4(hw); struct clk_aic32x4_pll_muldiv settings; @@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, return ret; /* 10ms is the delay to wait before the clocks are stable */ - msleep(10); + usleep_range(10000, 20000); return 0; } @@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; } - static const struct clk_ops aic32x4_pll_ops = { .prepare = clk_aic32x4_pll_prepare, .unprepare = clk_aic32x4_pll_unprepare, @@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) struct clk_aic32x4 *div = to_clk_aic32x4(hw); regmap_update_bits(div->regmap, div->reg, - AIC32X4_DIVEN, 0); + AIC32X4_DIVEN, 0); } static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); u8 divisor; @@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, } static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); unsigned int val; @@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { { .name = "pll", .parent_names = - (const char* []) { "mclk", "bclk", "gpio", "din" }, + (const char *[]) { "mclk", "bclk", "gpio", "din" }, .num_parents = 4, .ops = &aic32x4_pll_ops, .reg = 0, @@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { }, { .name = "ndac", - .parent_names = (const char * []) { "codec_clkin" }, + .parent_names = (const char *[]) { "codec_clkin" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_NDAC, }, { .name = "mdac", - .parent_names = (const char * []) { "ndac" }, + .parent_names = (const char *[]) { "ndac" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_MDAC, }, { .name = "nadc", - .parent_names = (const char * []) { "codec_clkin" }, + .parent_names = (const char *[]) { "codec_clkin" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_NADC, }, { .name = "madc", - .parent_names = (const char * []) { "nadc" }, + .parent_names = (const char *[]) { "nadc" }, .num_parents = 1, .ops = &aic32x4_div_ops, .reg = AIC32X4_MADC, @@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { }; static struct clk *aic32x4_register_clk(struct device *dev, - struct aic32x4_clkdesc *desc) + struct aic32x4_clkdesc *desc) { struct clk_init_data init; struct clk_aic32x4 *priv; @@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, init.flags = 0; priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); - if (priv == NULL) - return (struct clk *) -ENOMEM; + if (!priv) + return ERR_PTR(-ENOMEM); priv->dev = dev; priv->hw.init = &init; diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index e031eaaa2f7f..cbbb8ff3ee38 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -1,11 +1,10 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Copyright 2011-2019 NW Digital Radio * * Author: Annaliese McDermond * * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. - * */ #include diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c index 4f842260e325..cb615b2e4f02 100644 --- a/sound/soc/codecs/tlv320aic32x4-spi.c +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -1,11 +1,10 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +// SPDX-License-Identifier: GPL-2.0 +/* * Copyright 2011-2019 NW Digital Radio * * Author: Annaliese McDermond * * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. - * */ #include diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 8d3f2d5c6128..9e0e4aa2b09c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/sound/soc/codecs/tlv320aic32x4.c - * * Copyright 2011 Vista Silicon S.L. * * Author: Javier Martin @@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, }; static int mic_bias_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); @@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* Change Mic Bias Registor */ snd_soc_component_update_bits(component, AIC32X4_MICBIAS, - AIC32x4_MICBIAS_MASK, - AIC32X4_MICBIAS_LDOIN | - AIC32X4_MICBIAS_2075V); - printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); + AIC32x4_MICBIAS_MASK, + AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + dev_dbg(component->dev, "Mic Bias will be turned ON\n"); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_component_update_bits(component, AIC32X4_MICBIAS, - AIC32x4_MICBIAS_MASK, 0); - printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", - __func__); + AIC32x4_MICBIAS_MASK, 0); + dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); break; } return 0; } - static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_DOUTCTL); gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { - printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); return -EINVAL; } @@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_MISOCTL); gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { - printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); return -EINVAL; } @@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, }; static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); u8 val; @@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, val = snd_soc_component_read(component, AIC32X4_GPIOCTL); gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { - printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", - __func__); + dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); return -EINVAL; } @@ -276,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); static const struct snd_kcontrol_new aic32x4_snd_controls[] = { SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, - AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, - AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, - tlv_driver_gain), + AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, - AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, - tlv_driver_gain), + AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, - AIC32X4_HPRGAIN, 6, 0x01, 1), + AIC32X4_HPRGAIN, 6, 0x01, 1), SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, - AIC32X4_LORGAIN, 6, 0x01, 1), + AIC32X4_LORGAIN, 6, 0x01, 1), SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, - AIC32X4_RMICPGAVOL, 7, 0x01, 1), + AIC32X4_RMICPGAVOL, 7, 0x01, 1), SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, - AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), + AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, - AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), - SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, - 4, 0x07, 0), - SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, - 0, 0x03, 0), - SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, - 6, 0x03, 0), - SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, - 1, 0x1F, 0), - SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, - 0, 0x7F, 0), - SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, - 3, 0x1F, 0), - SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, - 3, 0x1F, 0), - SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, - 0, 0x1F, 0), - SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, - 0, 0x0F, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), }; static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -360,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), }; + static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), }; + static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), }; + static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), }; + static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), }; + static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), }; + static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), }; @@ -391,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), }; + static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), }; + static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), }; + static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), }; + static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), }; + static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), }; + static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), }; @@ -434,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1r_to_rmixer_controls), + in1r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2r_to_rmixer_controls), + in2r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in3r_to_rmixer_controls), + in3r_to_rmixer_controls), SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2l_to_rmixer_controls), + in2l_to_rmixer_controls), SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - cmr_to_rmixer_controls), + cmr_to_rmixer_controls), SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in1l_to_rmixer_controls), + in1l_to_rmixer_controls), SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in3l_to_rmixer_controls), + in3l_to_rmixer_controls), SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1l_to_lmixer_controls), + in1l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in2l_to_lmixer_controls), + in2l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in3l_to_lmixer_controls), + in3l_to_lmixer_controls), SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, - in1r_to_lmixer_controls), + in1r_to_lmixer_controls), SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - cml_to_lmixer_controls), + cml_to_lmixer_controls), SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in2r_to_lmixer_controls), + in2r_to_lmixer_controls), SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, - in3r_to_lmixer_controls), + in3r_to_lmixer_controls), SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), @@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_CBC_CFC: break; default: - printk(KERN_ERR "aic32x4: invalid clock provider\n"); + dev_err(component->dev, "invalid clock provider\n"); return -EINVAL; } @@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) AIC32X4_IFACE1_DATATYPE_SHIFT); break; default: - printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + dev_err(component->dev, "invalid DAI interface format\n"); return -EINVAL; } aic32x4->fmt = fmt; snd_soc_component_update_bits(component, AIC32X4_IFACE1, - AIC32X4_IFACE1_DATATYPE_MASK | - AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); + AIC32X4_IFACE1_DATATYPE_MASK | + AIC32X4_IFACE1_MASTER_MASK, + iface_reg_1); snd_soc_component_update_bits(component, AIC32X4_IFACE2, - AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + AIC32X4_DATA_OFFSET_MASK, iface_reg_2); snd_soc_component_update_bits(component, AIC32X4_IFACE3, - AIC32X4_BCLKINV_MASK, iface_reg_3); + AIC32X4_BCLKINV_MASK, iface_reg_3); return 0; } @@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) { snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); - snd_soc_component_write(component, AIC32X4_DOSRLSB, - (dosr & 0xff)); + snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); return 0; } static int aic32x4_set_processing_blocks(struct snd_soc_component *component, - u8 r_block, u8 p_block) + u8 r_block, u8 p_block) { struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); @@ -794,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, (min_mdac * dosr * sample_rate); for (mdac = min_mdac; mdac <= 128; ++mdac) { for (ndac = max_ndac; ndac > 0; --ndac) { - dac_clock_rate = ndac * mdac * dosr * - sample_rate; - if (dac_clock_rate == adc_clock_rate) { - if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) - continue; + dac_clock_rate = ndac * mdac * dosr * sample_rate; + if (dac_clock_rate != adc_clock_rate) + continue; - clk_set_rate(clocks[0].clk, - dac_clock_rate); + if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) + continue; - clk_set_rate(clocks[1].clk, - sample_rate * aosr * - madc); - clk_set_rate(clocks[2].clk, - sample_rate * aosr); - aic32x4_set_aosr(component, - aosr); + clk_set_rate(clocks[0].clk, dac_clock_rate); - clk_set_rate(clocks[3].clk, - sample_rate * dosr * - mdac); - clk_set_rate(clocks[4].clk, - sample_rate * dosr); - aic32x4_set_dosr(component, - dosr); + clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); + clk_set_rate(clocks[2].clk, sample_rate * aosr); + aic32x4_set_aosr(component, aosr); - clk_set_rate(clocks[5].clk, - sample_rate * channels * - bit_depth); + clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); + clk_set_rate(clocks[4].clk, sample_rate * dosr); + aic32x4_set_dosr(component, dosr); - return 0; - } + clk_set_rate(clocks[5].clk, + sample_rate * channels * bit_depth); + + return 0; } } } } - dev_err(component->dev, - "Could not set clocks to support sample rate.\n"); + dev_err(component->dev, "Could not set clocks to support sample rate.\n"); return -EINVAL; } static int aic32x4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); @@ -850,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, switch (params_physical_width(params)) { case 16: - iface1_reg |= (AIC32X4_WORD_LEN_16BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 20: - iface1_reg |= (AIC32X4_WORD_LEN_20BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 24: - iface1_reg |= (AIC32X4_WORD_LEN_24BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; case 32: - iface1_reg |= (AIC32X4_WORD_LEN_32BITS << - AIC32X4_IFACE1_DATALEN_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); break; } snd_soc_component_update_bits(component, AIC32X4_IFACE1, - AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); + AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); if (params_channels(params) == 1) { dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; @@ -878,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; } snd_soc_component_update_bits(component, AIC32X4_DACSETUP, - AIC32X4_DAC_CHAN_MASK, dacsetup_reg); + AIC32X4_DAC_CHAN_MASK, dacsetup_reg); return 0; } @@ -888,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) struct snd_soc_component *component = dai->component; snd_soc_component_update_bits(component, AIC32X4_DACMUTE, - AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); return 0; } @@ -1004,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) /* Power platform configuration */ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { snd_soc_component_write(component, AIC32X4_MICBIAS, - AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); + AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); } if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); @@ -1023,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) /* Mic PGA routing */ if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) snd_soc_component_write(component, AIC32X4_LMICPGANIN, - AIC32X4_LMICPGANIN_IN2R_10K); + AIC32X4_LMICPGANIN_IN2R_10K); else snd_soc_component_write(component, AIC32X4_LMICPGANIN, - AIC32X4_LMICPGANIN_CM1L_10K); + AIC32X4_LMICPGANIN_CM1L_10K); if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) snd_soc_component_write(component, AIC32X4_RMICPGANIN, - AIC32X4_RMICPGANIN_IN1L_10K); + AIC32X4_RMICPGANIN_IN1L_10K); else snd_soc_component_write(component, AIC32X4_RMICPGANIN, - AIC32X4_RMICPGANIN_CM1R_10K); + AIC32X4_RMICPGANIN_CM1R_10K); /* * Workaround: for an unknown reason, the ADC needs to be powered up @@ -1040,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) * a HW BUG or some kind of behavior not documented in the datasheet. */ tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); - snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | - AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, + tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); /* @@ -1084,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), SOC_SINGLE_TLV("HP Driver Gain Volume", - AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), + AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), + TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", - TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), + TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), }; @@ -1126,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { static struct snd_soc_dai_driver aic32x4_tas2505_dai = { .name = "tas2505-hifi", .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = AIC32X4_FORMATS,}, + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = AIC32X4_FORMATS, + }, .ops = &aic32x4_ops, .symmetric_rate = 1, }; @@ -1173,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); /* - * Enable the fast charging feature and ensure the needed 40ms ellapsed + * Enable the fast charging feature and ensure the needed 40ms elapsed * before using the analog circuits. */ snd_soc_component_write(component, TAS2505_REFPOWERUP, @@ -1199,8 +1179,7 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = .endianness = 1, }; -static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, - struct device_node *np) +static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) { int ret; @@ -1243,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) } static int aic32x4_setup_regulators(struct device *dev, - struct aic32x4_priv *aic32x4) + struct aic32x4_priv *aic32x4) { int ret = 0; @@ -1333,9 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, struct device_node *np = dev->of_node; int ret; - aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), - GFP_KERNEL); - if (aic32x4 == NULL) + aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); + if (!aic32x4) return -ENOMEM; aic32x4->dev = dev; @@ -1379,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, switch (aic32x4->type) { case AIC32X4_TYPE_TAS2505: ret = devm_snd_soc_register_component(dev, - &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); + &soc_component_dev_aic32x4_tas2505, + &aic32x4_tas2505_dai, 1); break; default: ret = devm_snd_soc_register_component(dev, - &soc_component_dev_aic32x4, &aic32x4_dai, 1); + &soc_component_dev_aic32x4, + &aic32x4_dai, 1); } if (ret) { diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 95d010af3d5a..cfab6f8ce5ac 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -3,7 +3,6 @@ * tlv320aic32x4.h */ - #ifndef _TLV320AIC32X4_H #define _TLV320AIC32X4_H @@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); /* tlv320aic32x4 register space (in decimal to match datasheet) */ -#define AIC32X4_REG(page, reg) ((page * 128) + reg) +#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) #define AIC32X4_PSEL AIC32X4_REG(0, 0) From 30df43c14050365b8e26ea428b75b0d33de77fdb Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 20 Jul 2026 14:40:40 +0700 Subject: [PATCH 376/791] ASoC: stm: stm32_adfsdm: Drop redundant error message devm_snd_soc_register_component() already logs the failure internally. Drop the redundant error message and return the original error directly. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260720074044.87528-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_adfsdm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 66efb9a0acb9..73c6460a6327 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &stm32_adfsdm_soc_platform, NULL, 0); - if (ret < 0) { - dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", - __func__); + if (ret < 0) return ret; - } pm_runtime_enable(&pdev->dev); From a6cdfb6230944082e7be64f9166eed64656b1162 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 20 Jul 2026 14:40:41 +0700 Subject: [PATCH 377/791] ASoC: stm: stm32_i2s: Drop redundant error messages Both devm_request_irq() and snd_dmaengine_pcm_register() already log failures internally. Drop the redundant error messages and return the original errors directly. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260720074044.87528-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_i2s.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index ae9e25657f3f..83b51893b37c 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, dev_name(&pdev->dev), i2s); - if (ret) { - dev_err(&pdev->dev, "irq request returned %d\n", ret); + if (ret) return ret; - } /* Reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); @@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); + return ret; ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); From e502adb1cdd6c26d7c8529bdebb776bc5902d88e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 20 Jul 2026 14:40:42 +0700 Subject: [PATCH 378/791] ASoC: stm: stm32_sai_sub: Drop redundant error messages Both devm_request_irq() and snd_dmaengine_pcm_register() already log failures internally. Drop the redundant error messages and return the original errors directly. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260720074044.87528-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index ea9e8bddd63f..9a201bdb306f 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, IRQF_SHARED, dev_name(&pdev->dev), sai); - if (ret) { - dev_err(&pdev->dev, "IRQ request returned %d\n", ret); + if (ret) goto err_unprepare_pclk; - } if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) { - ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); + if (ret) goto err_unprepare_pclk; - } ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); From 94495c842711b43ec82f2c1259d1a74a9b3378bb Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 20 Jul 2026 14:40:43 +0700 Subject: [PATCH 379/791] ASoC: stm: stm32_spdifrx: Drop redundant error messages Both devm_request_irq() and snd_dmaengine_pcm_register() already log failures internally. Drop the redundant error messages and return the original errors directly. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260720074044.87528-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_spdifrx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 2f83ca989e68..e0fef47a227e 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, dev_name(&pdev->dev), spdifrx); - if (ret) { - dev_err(&pdev->dev, "IRQ request returned %d\n", ret); + if (ret) return ret; - } rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) @@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); + return ret; ret = snd_soc_register_component(&pdev->dev, &stm32_spdifrx_component, From a14b50577898c0693a4b79f73f4c3d258056e019 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:46:01 +0000 Subject: [PATCH 380/791] ASoC: ux500: mop500: tidyup mop500_of_probe() parameter mop500.c will be updated when Card capsuling. To makes its review easy, tidyup mop500_of_probe() parameter. No functional change, but is preparation for cleanup driver. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ecgxi087.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ux500/mop500.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae6d326167d1..6d196b4b8802 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -68,10 +68,11 @@ static void mop500_of_node_put(void) of_node_put(mop500_dai_links[0].codecs->of_node); } -static int mop500_of_probe(struct platform_device *pdev, - struct device_node *np) +static int mop500_of_probe(struct snd_soc_card *card) { + struct device *dev = card->dev; struct device_node *codec_np, *msp_np[2]; + struct device_node *np = dev->of_node; int i; msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); @@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); if (!(msp_np[0] && msp_np[1] && codec_np)) { - dev_err(&pdev->dev, "Phandle missing or invalid\n"); + dev_err(dev, "Phandle missing or invalid\n"); for (i = 0; i < 2; i++) of_node_put(msp_np[i]); of_node_put(codec_np); @@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, mop500_dai_links[i].codecs->name = NULL; } - snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); + snd_soc_of_parse_card_name(card, "stericsson,card-name"); return 0; } static int mop500_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; int ret; dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); mop500_card.dev = &pdev->dev; - ret = mop500_of_probe(pdev, np); + ret = mop500_of_probe(&mop500_card); if (ret) return ret; From 1ed8d136f77884bb32d7fd69491795e862aa3edd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:46:04 +0000 Subject: [PATCH 381/791] ASoC: ux500: mop500_ab8500: tidyup mop500_ab8500_remove() It sets drvdata again in remove(), but it want to remove it. void mop500_ab8500_remove(...) { struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); ... snd_soc_card_set_drvdata(card, drvdata); } ^^^^^^^ Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87cxwhi083.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 2a459267f0f9..feb683c55d11 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) clk_put(drvdata->clk_ptr_ulpclk); clk_put(drvdata->clk_ptr_intclk); - snd_soc_card_set_drvdata(card, drvdata); + snd_soc_card_set_drvdata(card, NULL); } From 6f5f85d10e9374402fd8e0241e3b018d83cb1192 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Wed, 29 Jul 2026 11:33:13 +0200 Subject: [PATCH 382/791] ACPI/platform: add AWDZ8399 to serial-multi-instantiate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register the AWINIC AW88399 ACPI hardware ID "AWDZ8399" with the serial-multi-instantiate driver and add it to the ACPI scan ignore list so that the two I2C amplifier instances on Lenovo Legion laptops are enumerated as separate I2C client devices rather than a single ACPI platform device. The SMI node creates two instances named "aw88399-hda" with IRQ_RESOURCE_AUTO, matching the pattern used by CS35L41. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Acked-by: Rafael J. Wysocki (Intel) Acked-by: Ilpo Järvinen Co-developed-by: Yakov Till Signed-off-by: Yakov Till Signed-off-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/DS7PR19MB7724431AE60B3D2280E73492FCCA2@DS7PR19MB7724.namprd19.prod.outlook.com --- drivers/acpi/scan.c | 1 + drivers/platform/x86/serial-multi-instantiate.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9a7ac2eb9ce0..4f4552b1551b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1752,6 +1752,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * by the drivers/platform/x86/serial-multi-instantiate.c driver, which * knows which client device id to use for each resource. */ + {"AWDZ8399", }, {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 1a369334f9cb..3e89fcdd45f7 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -307,6 +307,15 @@ static void smi_remove(struct platform_device *pdev) smi_devs_unregister(smi); } +static const struct smi_node aw88399_hda = { + .instances = { + { "aw88399-hda", IRQ_RESOURCE_AUTO, 0 }, + { "aw88399-hda", IRQ_RESOURCE_AUTO, 0 }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + static const struct smi_node bsg1160_data = { .instances = { { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, @@ -405,6 +414,7 @@ static const struct smi_node tas2781_hda = { * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). */ static const struct acpi_device_id smi_acpi_ids[] = { + { "AWDZ8399", (unsigned long)&aw88399_hda }, { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, From de7027c9149fd374bdafdd1aa55310efca827544 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Wed, 29 Jul 2026 11:33:14 +0200 Subject: [PATCH 383/791] ALSA: hda/scodec: add AW88399 HDA side codec driver Add an HDA side codec driver for the AWINIC AW88399 smart amplifier, enabling its use as a companion amplifier on HDA systems where the chip is connected via I2C to the host and driven alongside a primary HDA codec (such as Realtek ALC287). The driver is structured after the existing side codec drivers: * aw88399_hda_i2c.c: I2C bus driver matching ACPI HID "AWDZ8399" and serial-multi-instantiate device name "aw88399-hda". Creates the regmap and passes it to the shared probe function, following the CS35L41/CS35L56/TAS2781 pattern. * aw88399_hda.c: Core driver implementing HDA component binding, playback hooks (using the shared library's start/stop functions), ACPI subsystem ID retrieval, and runtime/system power management. Includes per-model quirk infrastructure using ACPI subsystem ID matching; the quirk table is empty in this patch and populated in the next patch along with the corresponding Realtek fixups that activate the driver. The driver includes for shared definitions and depends on SND_SOC_AW88399_LIB for chip initialization, firmware loading, and playback control, avoiding any dependency on the full ASoC codec module. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Co-developed-by: Yakov Till Signed-off-by: Yakov Till Signed-off-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/DS7PR19MB77247D67E739956AA4611ACEFCCA2@DS7PR19MB7724.namprd19.prod.outlook.com --- sound/hda/codecs/side-codecs/Kconfig | 18 + sound/hda/codecs/side-codecs/Makefile | 4 + sound/hda/codecs/side-codecs/aw88399_hda.c | 330 ++++++++++++++++++ sound/hda/codecs/side-codecs/aw88399_hda.h | 36 ++ .../hda/codecs/side-codecs/aw88399_hda_i2c.c | 54 +++ 5 files changed, 442 insertions(+) create mode 100644 sound/hda/codecs/side-codecs/aw88399_hda.c create mode 100644 sound/hda/codecs/side-codecs/aw88399_hda.h create mode 100644 sound/hda/codecs/side-codecs/aw88399_hda_i2c.c diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index 1cfd83e251e4..f90f1dfcec68 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -13,6 +13,24 @@ config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST Documentation/dev-tools/kunit/. If in doubt, say "N". +config SND_HDA_SCODEC_AW88399 + tristate + select SND_HDA_GENERIC + +config SND_HDA_SCODEC_AW88399_I2C + tristate "Build AW88399 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select SND_HDA_SCODEC_AW88399 + select SND_SOC_AW88399_LIB + help + Say Y or M here to include AW88399 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_AW88399_I2C=m + config SND_HDA_SCODEC_CS35L41 tristate select SND_HDA_GENERIC diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile index 245e84f6a121..7dd010c68f78 100644 --- a/sound/hda/codecs/side-codecs/Makefile +++ b/sound/hda/codecs/side-codecs/Makefile @@ -3,6 +3,8 @@ subdir-ccflags-y += -I$(src)/../../common snd-hda-cirrus-scodec-y := cirrus_scodec.o snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o +snd-hda-scodec-aw88399-y := aw88399_hda.o +snd-hda-scodec-aw88399-i2c-y := aw88399_hda_i2c.o snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o @@ -16,6 +18,8 @@ snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o +obj-$(CONFIG_SND_HDA_SCODEC_AW88399) += snd-hda-scodec-aw88399.o +obj-$(CONFIG_SND_HDA_SCODEC_AW88399_I2C) += snd-hda-scodec-aw88399-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.c b/sound/hda/codecs/side-codecs/aw88399_hda.c new file mode 100644 index 000000000000..ed57d9a4a39d --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// AW88399 HDA side codec driver +// +// Based on cs35l41_hda.c and aw88399.c +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../generic.h" +#include "hda_component.h" +#include "aw88399_hda.h" + +#define AW88399_HDA_I2C_BASE_ADDR 0x34 + +static void aw88399_hda_playback_hook(struct device *dev, int action) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct aw88399 *core = aw88399->core; + int ret = 0; + + dev_dbg(aw88399->dev, "Playback action: %d\n", action); + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + pm_runtime_get_sync(dev); + aw88399->playing = true; + break; + case HDA_GEN_PCM_ACT_PREPARE: + if (core) + aw88399_start(core, AW88399_SYNC_START); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + if (aw88399->aw_dev) + ret = aw88399_stop(aw88399->aw_dev); + if (ret) + dev_err(aw88399->dev, "Failed to stop amplifier: %d\n", ret); + break; + case HDA_GEN_PCM_ACT_CLOSE: + if (aw88399->aw_dev) + aw88399_stop(aw88399->aw_dev); + aw88399->playing = false; + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + break; + default: + dev_warn(aw88399->dev, "Unsupported action: %d\n", action); + break; + } +} + +static int aw88399_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, aw88399->index); + if (!comp) + return -EINVAL; + + if (comp->dev) + return -EBUSY; + + comp->dev = dev; + + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + + comp->playback_hook = aw88399_hda_playback_hook; + + dev_info(aw88399->dev, + "AW88399 Bound - SSID: %s, channel: %d\n", + aw88399->acpi_subsystem_id, aw88399->channel); + + return 0; +} + +static void aw88399_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, aw88399->index); + if (comp && (comp->dev == dev)) + memset(comp, 0, sizeof(*comp)); + + dev_dbg(aw88399->dev, "Unbound from HDA codec\n"); +} + +static const struct component_ops aw88399_hda_comp_ops = { + .bind = aw88399_hda_bind, + .unbind = aw88399_hda_unbind, +}; + +static int aw88399_hda_index_from_i2c(struct aw88399_hda *aw88399) +{ + return to_i2c_client(aw88399->dev)->addr - AW88399_HDA_I2C_BASE_ADDR; +} + +static int aw88399_hda_init(struct aw88399_hda *aw88399) +{ + struct device *dev = aw88399->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct aw88399 *core; + int ret; + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + mutex_init(&core->lock); + core->reset_gpio = aw88399->reset_gpio; + core->regmap = aw88399->regmap; + core->bsts_unreliable = aw88399->bsts_unreliable; + + aw88399_hw_reset(core); + + ret = aw88399_init(core, i2c, aw88399->regmap); + if (ret) + return ret; + + /* Set channel BEFORE loading firmware so ACF parser sees correct value */ + if (core->aw_pa) + aw88399_dev_set_channel(core, aw88399->channel); + + ret = aw88399_request_firmware_file(core); + if (ret) + return ret; + + aw88399->core = core; + aw88399->aw_dev = core->aw_pa; + + return 0; +} + +struct aw88399_prop_model { + const char *ssid; + int (*apply_prop)(struct aw88399_hda *aw88399); +}; + +static const struct aw88399_prop_model aw88399_prop_model_table[] = { + { } +}; + +static int aw88399_hda_acpi_probe(struct aw88399_hda *aw88399) +{ + struct acpi_device *adev; + struct device *physdev; + const char *sub; + const struct aw88399_prop_model *model; + + aw88399->index = aw88399_hda_index_from_i2c(aw88399); + aw88399->channel = aw88399->index; + aw88399->acpi_subsystem_id = NULL; + + adev = acpi_dev_get_first_match_dev("AWDZ8399", NULL, -1); + if (!adev) { + dev_err(aw88399->dev, "Failed to find an ACPI device for AWDZ8399\n"); + return -ENODEV; + } + + physdev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + if (!physdev) + return -ENODEV; + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + put_device(physdev); + if (IS_ERR_OR_NULL(sub)) + return 0; + + aw88399->acpi_subsystem_id = devm_kstrdup(aw88399->dev, sub, GFP_KERNEL); + kfree(sub); + if (!aw88399->acpi_subsystem_id) + return -ENOMEM; + + for (model = aw88399_prop_model_table; model->ssid; model++) { + if (!strcasecmp(model->ssid, aw88399->acpi_subsystem_id)) { + dev_info(aw88399->dev, + "Applying properties for SSID %s\n", + aw88399->acpi_subsystem_id); + return model->apply_prop(aw88399); + } + } + + return 0; +} + +int aw88399_hda_probe(struct device *dev, struct regmap *regmap) +{ + struct aw88399_hda *aw88399; + int ret; + + aw88399 = devm_kzalloc(dev, sizeof(*aw88399), GFP_KERNEL); + if (!aw88399) + return -ENOMEM; + + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to obtain regmap\n"); + + aw88399->dev = dev; + aw88399->regmap = regmap; + dev_set_drvdata(dev, aw88399); + + aw88399->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88399->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(aw88399->reset_gpio), + "Failed to get reset GPIO\n"); + + ret = aw88399_hda_acpi_probe(aw88399); + if (ret) + return dev_err_probe(dev, ret, "ACPI probe failed\n"); + + ret = aw88399_hda_init(aw88399); + if (ret) + return dev_err_probe(dev, ret, "Chip initialization failed\n"); + + /* Enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = component_add(dev, &aw88399_hda_comp_ops); + if (ret) { + pm_runtime_disable(dev); + return dev_err_probe(dev, ret, "Failed to register component\n"); + } + + dev_info(dev, "AW88399 HDA side codec registered successfully\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(aw88399_hda_probe, "SND_HDA_SCODEC_AW88399"); + +void aw88399_hda_remove(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + + if (aw88399->aw_dev) + aw88399_stop(aw88399->aw_dev); + + component_del(dev, &aw88399_hda_comp_ops); + + dev_dbg(aw88399->dev, "AW88399 HDA side codec removed\n"); +} +EXPORT_SYMBOL_NS_GPL(aw88399_hda_remove, "SND_HDA_SCODEC_AW88399"); + +static int aw88399_hda_runtime_suspend(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + dev_dbg(aw88399->dev, "Runtime suspend\n"); + + if (aw88399->aw_dev && aw88399->playing) + aw88399_stop(aw88399->aw_dev); + + return 0; +} + +static int aw88399_hda_runtime_resume(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + dev_dbg(aw88399->dev, "Runtime resume\n"); + + if (aw88399->core && aw88399->aw_dev && aw88399->playing) + aw88399_start(aw88399->core, AW88399_SYNC_START); + + return 0; +} + +static int aw88399_hda_system_suspend(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + int ret; + + dev_dbg(aw88399->dev, "System suspend\n"); + + if (aw88399->aw_dev && aw88399->playing) + aw88399_stop(aw88399->aw_dev); + + if (aw88399->core) + aw88399->core->fw_needs_reload = true; + + ret = pm_runtime_force_suspend(dev); + if (ret) + dev_err(aw88399->dev, "Runtime force suspend failed: %d\n", ret); + + return ret; +} + +static int aw88399_hda_system_resume(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + int ret; + + dev_dbg(aw88399->dev, "System resume\n"); + + if (aw88399->aw_dev) + aw88399_hw_reset(aw88399->core); + + ret = pm_runtime_force_resume(dev); + if (ret) + dev_err(aw88399->dev, "Runtime force resume failed: %d\n", ret); + + return ret; +} + +const struct dev_pm_ops aw88399_hda_pm_ops = { + RUNTIME_PM_OPS(aw88399_hda_runtime_suspend, aw88399_hda_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(aw88399_hda_system_suspend, aw88399_hda_system_resume) +}; +EXPORT_SYMBOL_NS_GPL(aw88399_hda_pm_ops, "SND_HDA_SCODEC_AW88399"); + +MODULE_DESCRIPTION("AW88399 HDA driver"); +MODULE_AUTHOR("Yakov Till "); +MODULE_AUTHOR("Marco Giunta "); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("aw88399_acf.bin"); diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.h b/sound/hda/codecs/side-codecs/aw88399_hda.h new file mode 100644 index 000000000000..ca0aa5279298 --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * AW88399 HDA side codec driver + */ + +#ifndef __AW88399_HDA_H__ +#define __AW88399_HDA_H__ + +#include +#include +#include + +struct aw88399; +struct aw_device; + +struct aw88399_hda { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct aw_device *aw_dev; + struct aw88399 *core; + bool bsts_unreliable; + + const char *acpi_subsystem_id; + int index; + int channel; + + bool playing; +}; + +int aw88399_hda_probe(struct device *dev, struct regmap *regmap); +void aw88399_hda_remove(struct device *dev); + +extern const struct dev_pm_ops aw88399_hda_pm_ops; + +#endif /* __AW88399_HDA_H__ */ diff --git a/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c b/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c new file mode 100644 index 000000000000..186e13adbc6c --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// AW88399 HDA I2C driver +// +// Based on cs35l41_hda_i2c.c +// + +#include +#include +#include + +#include "aw88399_hda.h" + +static int aw88399_hda_i2c_probe(struct i2c_client *clt) +{ + if (!strstr(dev_name(&clt->dev), "AWDZ8399")) + return -ENODEV; + + return aw88399_hda_probe(&clt->dev, + devm_regmap_init_i2c(clt, &aw88399_remap_config)); +} + +static void aw88399_hda_i2c_remove(struct i2c_client *clt) +{ + aw88399_hda_remove(&clt->dev); +} + +static const struct i2c_device_id aw88399_hda_i2c_id[] = { + { .name = "aw88399-hda" }, + { } +}; + +static const struct acpi_device_id aw88399_acpi_hda_match[] = { + { "AWDZ8399", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, aw88399_acpi_hda_match); + +static struct i2c_driver aw88399_hda_i2c_driver = { + .driver = { + .name = "aw88399-hda", + .acpi_match_table = aw88399_acpi_hda_match, + .pm = &aw88399_hda_pm_ops, + }, + .probe = aw88399_hda_i2c_probe, + .remove = aw88399_hda_i2c_remove, + .id_table = aw88399_hda_i2c_id, +}; +module_i2c_driver(aw88399_hda_i2c_driver); + +MODULE_DESCRIPTION("HDA AW88399 I2C driver"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_AW88399"); +MODULE_AUTHOR("Yakov Till "); +MODULE_LICENSE("GPL"); From 494978ae8243d28dd0fe7b160591158a3a8c45b1 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Wed, 29 Jul 2026 11:33:15 +0200 Subject: [PATCH 384/791] ALSA: hda/realtek: enable AW88399 on Lenovo Legion Pro Enable audio output through the AW88399 woofer amplifiers on Lenovo Legion laptops by adding the necessary Realtek ALC287 fixups and AW88399 per-model quirks. Realtek fixups (alc269.c): * ALC287_FIXUP_AW88399_I2C_2: registers the AW88399 as a two-instance I2C companion codec using comp_generic_fixup, matching ACPI HID "AWDZ8399". * ALC287_FIXUP_LENOVO_LEGION_AW88399: forces DAC 0x02 for the bass speaker pin 0x17, as the default DAC 0x06 lacks volume controls. Also applies internal microphone boost calibration via alc269_fixup_limit_int_mic_boost and disables unused pin 0x1d to match the Windows driver's pin configuration. Chained to ALC287_FIXUP_AW88399_I2C_2. Per-model quirks (aw88399_hda.c): * Channel swap: the I2C wiring on these Legion models is reversed (0x34 is physically the right speaker, 0x35 is the left). The quirk swaps the channel assignment to correct L/R audio. * BSTS status bypass: the AW88399's boost-finished status bit (BSTS, SYSST register bit 9) does not reliably assert on this hardware. Register dumps during normal playback show both amplifiers reporting BSTS=0 on both channels despite clean audio output. The quirk sets the bsts_unreliable flag, introduced in commit b4530a3e4895 ("ASoC: aw88399: add per-instance BSTS status bypass flag"), so the startup status check skips the BSTS requirement on these devices. The R9000P ADR10 entries use HDA_CODEC_QUIRK and are placed before the existing SND_PCI_QUIRK for 17aa:38bb (Yoga S780-14.5 Air) to ensure the codec SSID match takes priority over the shared PCI SSID, following the pattern established by e.g. commit 0f3a822ae225 ("ALSA: hda/realtek: Fix quirk matching for Legion Pro 7"), commit dd074f04e046 ("ALSA: hda/realtek: Fix Legion 7 16ITHG6 speaker amp binding"). All other entries also use HDA_CODEC_QUIRK for consistency. Supported models (Lenovo vendor ID 0x17aa): * 0x3906: Legion Pro 7i 16IAX10H / Y9000P IAX10 (Intel) * 0x3907: Legion Pro 7i 16IAX10H / Y9000P IAX10 (Intel) * 0x3927: Legion R9000P ADR10 (AMD) * 0x3928: Legion R9000P ADR10 (AMD) * 0x3938: Legion Pro 7 16AFR10H (AMD) * 0x3939: Legion Pro 7 16AFR10H (AMD) Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Co-developed-by: Yakov Till Signed-off-by: Yakov Till Signed-off-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/DS7PR19MB7724ACD7C8D1BE71451E1AEEFCCA2@DS7PR19MB7724.namprd19.prod.outlook.com --- sound/hda/codecs/realtek/alc269.c | 50 ++++++++++++++++++++++ sound/hda/codecs/side-codecs/aw88399_hda.c | 42 ++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 218b897a5df4..d0dfe1509b60 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3262,6 +3262,34 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc) comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); } +static void aw88399_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "AWDZ8399", "-%s:00-aw88399-hda.%d", 2); +} + +static void alc287_fixup_legion_16iax10h_aw88399(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const struct hda_pintbl pincfgs[] = { + { 0x1d, 0x411111f0 }, /* unused bogus pin */ + { } + }; + + /* + * Force DAC 0x02 for the bass speaker 0x17, as the default 0x06 lacks volume controls. + */ + static const hda_nid_t conn[] = { 0x02 }; + + alc269_fixup_limit_int_mic_boost(codec, fix, action); + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + } +} + static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); @@ -4231,6 +4259,8 @@ enum { ALC236_FIXUP_DELL_HP_POP_NOISE, ALC274_FIXUP_HP_89E9_GPIO, ALC274_FIXUP_HP_VERBS, + ALC287_FIXUP_AW88399_I2C_2, + ALC287_FIXUP_LENOVO_LEGION_AW88399, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6917,6 +6947,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_HP_89E9_GPIO, }, + [ALC287_FIXUP_AW88399_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = aw88399_fixup_i2c_two, + }, + [ALC287_FIXUP_LENOVO_LEGION_AW88399] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16iax10h_aw88399, + .chained = true, + .chain_id = ALC287_FIXUP_AW88399_I2C_2, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7977,6 +8017,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), + /* Legion R9000P ADR10 shares PCI SSID 17aa:38bb with Yoga S780-14.5 Air AMD quad AAC; + * use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x3927, "Legion R9000P ADR10", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3928, "Legion R9000P ADR10", ALC287_FIXUP_LENOVO_LEGION_AW88399), SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), @@ -8004,6 +8049,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), + HDA_CODEC_QUIRK(0x17aa, 0x3906, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3907, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), @@ -8013,6 +8060,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + HDA_CODEC_QUIRK(0x17aa, 0x3938, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3939, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), HDA_CODEC_QUIRK(0x17aa, 0x394c, "Lenovo Yoga Slim 7 14AGP11", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), @@ -8316,6 +8365,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, {.id = ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, .name = "alc256-honor-mrb-xxx-m1020-audio"}, + {.id = ALC287_FIXUP_LENOVO_LEGION_AW88399, .name = "alc287-lenovo-legion-aw88399"}, {} }; #define ALC225_STANDARD_PINS \ diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.c b/sound/hda/codecs/side-codecs/aw88399_hda.c index ed57d9a4a39d..42de68faddbc 100644 --- a/sound/hda/codecs/side-codecs/aw88399_hda.c +++ b/sound/hda/codecs/side-codecs/aw88399_hda.c @@ -140,12 +140,54 @@ static int aw88399_hda_init(struct aw88399_hda *aw88399) return 0; } +static int aw88399_swap_channels(struct aw88399_hda *aw88399) +{ + /* + * Certain Lenovo Legion laptops have their + * I2C wiring reversed: 0x34 is physically the right speaker, + * 0x35 is the left. Swap channels to correct L/R assignment. + * This is a model-specific hardware wiring issue, not a driver bug. + */ + aw88399->channel = 1 - aw88399->channel; + dev_dbg(aw88399->dev, + "Channel swap applied: index %d -> channel %d\n", + aw88399->index, aw88399->channel); + return 0; +} + +static int aw88399_skip_bsts_check(struct aw88399_hda *aw88399) +{ + /* + * BSTS (boost-finished) status bit does not reliably report on + * some hardware. On certain Lenovo Legion laptops, both amps + * report BSTS=0 (boost not finished) during normal playback + * despite clean audio output. Skip BSTS in the startup status + * check to avoid false init failures. + */ + aw88399->bsts_unreliable = true; + dev_dbg(aw88399->dev, "BSTS status check disabled\n"); + return 0; +} + +static int aw88399_apply_legion_quirks(struct aw88399_hda *aw88399) +{ + aw88399_swap_channels(aw88399); + aw88399_skip_bsts_check(aw88399); + return 0; +} + struct aw88399_prop_model { const char *ssid; int (*apply_prop)(struct aw88399_hda *aw88399); }; static const struct aw88399_prop_model aw88399_prop_model_table[] = { + { "17AA3906", aw88399_apply_legion_quirks }, + { "17AA3907", aw88399_apply_legion_quirks }, + { "17AA3927", aw88399_apply_legion_quirks }, + { "17AA3928", aw88399_apply_legion_quirks }, + { "17AA3938", aw88399_apply_legion_quirks }, + { "17AA3939", aw88399_apply_legion_quirks }, { } }; From 5b106b40ed22098f2bc049efb645148d038444bd Mon Sep 17 00:00:00 2001 From: Bob Song Date: Thu, 30 Jul 2026 09:53:01 +0800 Subject: [PATCH 385/791] ALSA: hda/realtek: add missing error checks for COEF index reads in alc269 alc_read_coef_idx() and alc_read_coefex_idx() can return -1 on error via snd_hda_codec_read(). Several codec initialization and shutdown functions save these return values and later write them back to hardware registers without checking for errors, potentially corrupting COEF register state on a read failure. Add error checks in: - alc282_init() and alc282_shutup(): check coef78 before write-back - alc285_hp_init(): check coef38/coef0d/coef36 before update, check val before write-back, and break polling loop on error - alc294_hp_init(): break polling loop on read error Signed-off-by: Bob Song Link: https://patch.msgid.link/20260730015302.253008-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d0dfe1509b60..9c5945c6904b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -296,7 +296,8 @@ static void alc282_init(struct hda_codec *codec) msleep(100); /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x78, coef78); + if (coef78 != -1) + alc_write_coef_idx(codec, 0x78, coef78); } static void alc282_shutup(struct hda_codec *codec) @@ -333,7 +334,8 @@ static void alc282_shutup(struct hda_codec *codec) alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x78, coef78); + if (coef78 != -1) + alc_write_coef_idx(codec, 0x78, coef78); } static const struct coef_fw alc283_coefs[] = { @@ -585,15 +587,19 @@ static void alc285_hp_init(struct hda_codec *codec) alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */ val = alc_read_coefex_idx(codec, 0x58, 0x00); - for (i = 0; i < 20 && val & 0x8000; i++) { + for (i = 0; i < 20 && val != -1 && val & 0x8000; i++) { msleep(50); val = alc_read_coefex_idx(codec, 0x58, 0x00); } /* Wait for depop procedure finish */ - alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ - alc_update_coef_idx(codec, 0x38, 1<<4, coef38); - alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); - alc_update_coef_idx(codec, 0x36, 3<<13, coef36); + if (val != -1) + alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ + if (coef38 != -1) + alc_update_coef_idx(codec, 0x38, 1<<4, coef38); + if (coef0d != -1) + alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); + if (coef36 != -1) + alc_update_coef_idx(codec, 0x36, 3<<13, coef36); msleep(50); alc_update_coef_idx(codec, 0x4a, 1<<15, 0); @@ -858,7 +864,7 @@ static void alc294_hp_init(struct hda_codec *codec) /* Wait for depop procedure finish */ val = alc_read_coefex_idx(codec, 0x58, 0x01); - for (i = 0; i < 20 && val & 0x0080; i++) { + for (i = 0; i < 20 && val != -1 && val & 0x0080; i++) { msleep(50); val = alc_read_coefex_idx(codec, 0x58, 0x01); } From 3b36ac93739e7b119a30affaee9c2dd41f24efbb Mon Sep 17 00:00:00 2001 From: Bob Song Date: Thu, 30 Jul 2026 09:53:02 +0800 Subject: [PATCH 386/791] ALSA: hda/realtek: add missing NULL check for codec->bus->pci In alc269_probe(), codec->bus->pci is dereferenced without a NULL check for the ALC236 vendor ID case. Add the missing check, consistent with the existing pattern used elsewhere in the same function. Signed-off-by: Bob Song Link: https://patch.msgid.link/20260730015302.253008-2-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 9c5945c6904b..327f89b59639 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8977,6 +8977,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ if (codec->core.vendor_id == 0x10ec0236 && + codec->bus->pci && codec->bus->pci->vendor != PCI_VENDOR_ID_AMD) spec->en_3kpull_low = false; break; From 522d7baa39e913700782cd22514ec04ac80ca036 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 21 Jul 2026 15:54:42 -0700 Subject: [PATCH 387/791] ASoC: fsl: dma: use platform helpers and devm cleanup Convert fsl_soc_dma_probe() to managed APIs. Replace the open-coded of_address_to_resource()/of_iomap() of the DMA channel registers with devm_platform_ioremap_resource(), and irq_of_parse_and_map() with platform_get_irq() (which returns a negative errno instead of 0). Switch the allocation to devm_kzalloc() and register the component via the devm variant, dropping the now-unneeded error-path cleanup and the manual fsl_soc_dma_remove(). The SSI node's register resource is still read via of_address_to_resource() to compute the SSI FIFO physical addresses (dma->ssi_stx_phys / ssi_srx_phys); only the DMA controller window is mapped. The DMA controller register window is owned solely by this driver, so the new region request from devm_platform_ioremap_resource() cannot conflict with another claimant, and it is mapped exactly once (no double mapping). The local channel pointer is declared as void __iomem * so the devm_platform_ioremap_resource() result can be stored before assignment to dma->channel. No functional change; built for powerpc (allmodconfig + CONFIG_SND_SOC_FSL_DMA) with LLVM=1 and sound/soc/fsl/fsl_dma.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260721225442.817787-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 68 ++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index b12474880185..b1b341132dd6 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include @@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct device_node *ssi_np; struct resource res; + void __iomem *channel; const uint32_t *iprop; + int irq; int ret; + channel = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(channel)) + return PTR_ERR(channel); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->dai.name = DRV_NAME; + dma->dai.open = fsl_dma_open; + dma->dai.close = fsl_dma_close; + dma->dai.hw_params = fsl_dma_hw_params; + dma->dai.hw_free = fsl_dma_hw_free; + dma->dai.pointer = fsl_dma_pointer; + dma->dai.pcm_new = fsl_dma_new; + + dma->channel = channel; + dma->irq = irq; + /* Find the SSI node that points to us. */ ssi_np = find_ssi_node(np); if (!ssi_np) { @@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) return ret; } - dma = kzalloc_obj(*dma); - if (!dma) { - of_node_put(ssi_np); - return -ENOMEM; - } - - dma->dai.name = DRV_NAME; - dma->dai.open = fsl_dma_open; - dma->dai.close = fsl_dma_close; - dma->dai.hw_params = fsl_dma_hw_params; - dma->dai.hw_free = fsl_dma_hw_free; - dma->dai.pointer = fsl_dma_pointer; - dma->dai.pcm_new = fsl_dma_new; - /* Store the SSI-specific information that we need */ dma->ssi_stx_phys = res.start + REG_SSI_STX0; dma->ssi_srx_phys = res.start + REG_SSI_SRX0; iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); + of_node_put(ssi_np); if (iprop) dma->ssi_fifo_depth = be32_to_cpup(iprop); else /* Older 8610 DTs didn't have the fifo-depth property */ dma->ssi_fifo_depth = 8; - of_node_put(ssi_np); - - ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "could not register platform\n"); - kfree(dma); - return ret; - } - - dma->channel = of_iomap(np, 0); - dma->irq = irq_of_parse_and_map(np, 0); - - dev_set_drvdata(&pdev->dev, dma); - - return 0; -} - -static void fsl_soc_dma_remove(struct platform_device *pdev) -{ - struct dma_object *dma = dev_get_drvdata(&pdev->dev); - - iounmap(dma->channel); - irq_dispose_mapping(dma->irq); - kfree(dma); + return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); } static const struct of_device_id fsl_soc_dma_ids[] = { @@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { .of_match_table = fsl_soc_dma_ids, }, .probe = fsl_soc_dma_probe, - .remove = fsl_soc_dma_remove, }; module_platform_driver(fsl_soc_dma_driver); From ef9c8eb307046afa797befd84e6c741cd8954dfc Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 16 Jul 2026 14:15:36 +0800 Subject: [PATCH 388/791] ASoC: rt1320-sdw: Add settings to support more base clock frequency Add settings to support more base clock frequency on different platform. Signed-off-by: Jack Yu Link: https://patch.msgid.link/20260716061536.1563252-1-jack.yu@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 36 ++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt1320-sdw.h | 7 +++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index a30fb575918d..3a5eebcfefdd 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -836,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { static bool rt1320_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0xc000 ... 0xc086: case 0xc400 ... 0xc409: case 0xc480 ... 0xc48f: @@ -946,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) static bool rt1320_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0xc000: case 0xc003: case 0xc081: @@ -3383,6 +3385,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, return 0; } +static int rt1320_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); + unsigned int clk_base; + + regmap_read(rt1320->regmap, 0x004d, &clk_base); + dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); + + switch (clk_base) { + case RT1320_CLK_FREQ_19_2_MHZ: + case RT1320_CLK_FREQ_24MHZ: + regmap_write(rt1320->regmap, 0xc600, 0x04); + regmap_write(rt1320->regmap, 0xc601, 0x83); + regmap_write(rt1320->regmap, 0xc602, 0x1f); + regmap_write(rt1320->regmap, 0xc603, 0x40); + break; + case RT1320_CLK_FREQ_24_576MHZ: + case RT1320_CLK_FREQ_22_5792MHZ: + regmap_write(rt1320->regmap, 0xc600, 0x07); + regmap_write(rt1320->regmap, 0xc601, 0x80); + regmap_write(rt1320->regmap, 0xc602, 0x0f); + regmap_write(rt1320->regmap, 0xc603, 0x40); + break; + default: + return -EINVAL; + } + + return 0; +} + /* * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now @@ -3390,12 +3423,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, static const struct sdw_slave_ops rt1320_slave_ops = { .read_prop = rt1320_read_prop, .update_status = rt1320_update_status, + .bus_config = rt1320_bus_config, }; static int rt1320_sdw_component_probe(struct snd_soc_component *component) { - int ret; struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret; rt1320->component = component; diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index bc0f78e03529..df9ccdf92d36 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -175,6 +175,13 @@ enum rt1320_rw_type { RT1320_PARAM_READ = 3, }; +enum { + RT1320_CLK_FREQ_19_2_MHZ = 1, + RT1320_CLK_FREQ_24MHZ = 2, + RT1320_CLK_FREQ_24_576MHZ = 3, + RT1320_CLK_FREQ_22_5792MHZ = 4, +}; + struct rt1320_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; From 596f78db19528b8007fe5515d479babdc99c8455 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 17:10:54 -0700 Subject: [PATCH 389/791] ASoC: amd: acp: Use pcim_iomap_region() in acp-pci Convert acp-pci to the pcim-managed PCI life-cycle. Replace pci_enable_device() with pcim_enable_device() and fold the open-coded pci_request_regions() + devm_ioremap() pair into a single pcim_iomap_region() call for BAR0, which reserves and iomaps the register window. This lets the driver drop the manual pci_release_regions() and pci_disable_device() calls from the probe error path; pcim releases the device and region automatically on detach or probe failure. The error check moves from a NULL test to IS_ERR(), since pcim_iomap_region() returns an IOMEM_ERR_PTR on failure. The child platform devices only use devm_ioremap() on their sub-range of BAR0 (no request_mem_region), so reserving the full BAR0 here does not conflict with them. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260720001054.1439409-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-pci.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index f83708755ed1..98771323eace 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id if (!chip) return -ENOMEM; - if (pci_enable_device(pci)) + if (pcim_enable_device(pci)) return dev_err_probe(&pci->dev, -ENODEV, "pci_enable_device failed\n"); - ret = pci_request_regions(pci, "AMD ACP3x audio"); - if (ret < 0) { - dev_err(&pci->dev, "pci_request_regions failed\n"); - ret = -ENOMEM; - goto disable_pci; - } - pci_set_master(pci); chip->acp_rev = pci->revision; @@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); - ret = -EINVAL; - goto release_regions; + return -EINVAL; } chip->flag = flag; addr = pci_resource_start(pci, 0); - chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); - if (!chip->base) { - ret = -ENOMEM; - goto release_regions; - } + chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); chip->addr = addr; chip->acp_hw_ops_init(chip); ret = acp_hw_init(chip); if (ret) - goto release_regions; + goto de_init; ret = devm_request_irq(dev, pci->irq, irq_handler, IRQF_SHARED, "ACP_I2S_IRQ", chip); @@ -214,10 +204,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id de_init: acp_hw_deinit(chip); -release_regions: - pci_release_regions(pci); -disable_pci: - pci_disable_device(pci); return ret; }; From 65ebc8347a9cfdbf6133adfb018c7243ce5d30f6 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 23 Jul 2026 18:10:11 +0700 Subject: [PATCH 390/791] ASoC: starfive: jh7110-pwmdac: Remove unnecessary goto The error path after jh7110_pwmdac_runtime_resume() failure only performs a single cleanup operation before returning. Remove the unnecessary goto and return directly after calling pm_runtime_disable(), simplifying the control flow without changing the behavior. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260723111014.54071-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/starfive/jh7110_pwmdac.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c index a603dd17931c..562936981cf0 100644 --- a/sound/soc/starfive/jh7110_pwmdac.c +++ b/sound/soc/starfive/jh7110_pwmdac.c @@ -486,16 +486,13 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) pm_runtime_enable(dev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = jh7110_pwmdac_runtime_resume(&pdev->dev); - if (ret) - goto err_pm_disable; + if (ret) { + pm_runtime_disable(&pdev->dev); + return ret; + } } return 0; - -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; } static void jh7110_pwmdac_remove(struct platform_device *pdev) From db233669245d967c76efb8a6e59a4f19903ca35b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 23 Jul 2026 18:10:12 +0700 Subject: [PATCH 391/791] ASoC: starfive: jh7110-pwmdac: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260723111014.54071-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/starfive/jh7110_pwmdac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c index 562936981cf0..5953c3a4e373 100644 --- a/sound/soc/starfive/jh7110_pwmdac.c +++ b/sound/soc/starfive/jh7110_pwmdac.c @@ -477,11 +477,11 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) &jh7110_pwmdac_component, &jh7110_pwmdac_dai, 1); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); + return ret; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); + return ret; pm_runtime_enable(dev->dev); if (!pm_runtime_enabled(&pdev->dev)) { From de621dacea09c2417b3a6995c61ecdfba72fd485 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 23 Jul 2026 18:10:13 +0700 Subject: [PATCH 392/791] ASoC: starfive: jh7110_tdm: Remove unnecessary goto The error path after jh7110_tdm_runtime_resume() failure only performs a single cleanup operation before returning. Remove the unnecessary goto and return directly after calling pm_runtime_disable(), simplifying the control flow without changing the behavior. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260723111014.54071-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/starfive/jh7110_tdm.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index afdcde7df91a..5365ebe44471 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -615,16 +615,13 @@ static int jh7110_tdm_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = jh7110_tdm_runtime_resume(&pdev->dev); - if (ret) - goto err_pm_disable; + if (ret) { + pm_runtime_disable(&pdev->dev); + return ret; + } } return 0; - -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; } static void jh7110_tdm_dev_remove(struct platform_device *pdev) From 523c01b12ec141926ae39b9b538c2ca12e899225 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 23 Jul 2026 18:10:14 +0700 Subject: [PATCH 393/791] ASoC: starfive: jh7110_tdm: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260723111014.54071-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/starfive/jh7110_tdm.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index 5365ebe44471..f7522bfbe8e6 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, tdm->clks[5].id = "tdm"; ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); - if (ret) { - dev_err(&pdev->dev, "Failed to get tdm clocks\n"); + if (ret) return ret; - } tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); if (IS_ERR(tdm->resets)) { @@ -589,28 +587,22 @@ static int jh7110_tdm_probe(struct platform_device *pdev) tdm->dev = &pdev->dev; ret = jh7110_tdm_clk_reset_get(pdev, tdm); - if (ret) { - dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); + if (ret) return ret; - } jh7110_tdm_init_params(tdm); dev_set_drvdata(&pdev->dev, tdm); ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, &jh7110_tdm_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register dai\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &jh7110_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_COMPAT); - if (ret) { - dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); + if (ret) return ret; - } pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { From 088c4404b3d780c16bc8d49d3ea072043a750f10 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 28 Jul 2026 10:53:28 +0200 Subject: [PATCH 394/791] ASoC: qcom: sc8280xp: allow setting m2is clocks for SM8[456]50 boards Extend the sc8280xp card to add settings to setup I2S clock and line properties. Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260728-topic-sm8x50-next-hdk-i2s-v1-1-2393a0fe4aa9@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index a9304784d41e..c0a5dbda1185 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -360,6 +360,12 @@ static const struct snd_soc_common sm8450_priv_data = { .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, }; static const struct snd_soc_common sm8550_priv_data = { @@ -367,6 +373,12 @@ static const struct snd_soc_common sm8550_priv_data = { .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, }; static const struct snd_soc_common sm8650_priv_data = { @@ -374,6 +386,12 @@ static const struct snd_soc_common sm8650_priv_data = { .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, + /* I2S Connected to HDMI */ + .mi2s_mclk_enable = true, + .mi2s_bclk_enable = true, + .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, }; static const struct snd_soc_common sm8750_priv_data = { From ffd02a377a442c960bfc2151eaf7f8c04a5ea0ee Mon Sep 17 00:00:00 2001 From: Padhia Luo Date: Thu, 30 Jul 2026 15:14:53 +0800 Subject: [PATCH 395/791] ALSA: hda/realtek: Fix speaker mute LED on Lenovo ThinkBook 14 G8+ IPH On the ThinkBook 14 G8+ IPH (SSID 17aa:393e, ALC287) the F1 speaker mute LED never lights up, while the F4 mic mute LED works. Both LEDs are platform LEDs registered by lenovo-wmi-hotkey-utilities and default to the audio-mute / audio-micmute triggers, so the speaker LED only follows a control carrying SNDRV_CTL_ELEM_ACCESS_SPK_LED. No control on this machine has that flag set, so snd_ctl_led never attaches anything and /sys/class/sound/ctl-led/speaker/card0/list stays empty. The mic LED is unaffected because MIC_LED is set from the SOF topology on the DMIC control, which does not go through the codec fixups at all. The pin configuration of this machine matches the ThinkPad pin quirk that selects ALC285_FIXUP_THINKPAD_HEADSET_JACK - pin_config_match() masks out the sequence/association nibbles, so 0x14=0x90170120 still matches the 0x90170110 in the table. That fixup chains into ALC269_FIXUP_THINKPAD_ACPI, but hda_fixup_thinkpad_acpi() returns early because is_thinkpad() is false: a ThinkBook exposes neither LEN0068/LEN0268 nor IBM0068. Therefore snd_hda_gen_add_mute_led_cdev() is never called and spec->vmaster_mute_led stays 0. The vendor fallback SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI) would have handled this correctly - the machine does expose LHK2019 and VPC2004, so is_ideapad() is true - but it never runs: the pin quirk has already set codec->fixup_id, and snd_hda_pick_fixup() returns immediately in that case. Add an SSID quirk selecting a fixup that keeps everything the machine currently gets (headset jack handling plus the X1 Gen7 DAC routing) and additionally runs the ideapad ACPI setup. It chains into ALC287_FIXUP_LENOVO_YOGA_PRO7, which already combines alc285_fixup_thinkpad_x1_gen7 with ALC269_FIXUP_LENOVO_XPAD_ACPI, so the resulting chain differs from the current one only by the added ideapad step and cannot regress the analog output or the headset jack. Tested on 7.1.5 on the affected machine: the speaker LED group is now populated at probe time without any userspace help, and the F1 LED follows the mute state. Compared against a boot with the previous fixup selection, the mixer control list (names and numids) and the registered jack input devices are identical. Note that the underlying mismatch is not specific to this SSID. Any Lenovo non-ThinkPad whose pins collide with a ThinkPad pin quirk loses its mute LED the same way. Letting hda_fixup_thinkpad_acpi() fall back to the ideapad check would cover the whole class at once, but that touches a helper shared with every ThinkPad, so this patch only fixes the machine that was actually tested. Signed-off-by: Padhia Luo Link: https://patch.msgid.link/20260730071453.19636-1-lcj20010426@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 327f89b59639..f7877127e0e4 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4178,6 +4178,7 @@ enum { ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_YOGA_BOOK_9I, ALC287_FIXUP_LENOVO_YOGA_PRO7, + ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -6269,6 +6270,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, }, + [ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + /* Same as ALC285_FIXUP_THINKPAD_HEADSET_JACK, except that the + * mute LEDs are driven through ideapad_laptop rather than + * thinkpad_acpi. + */ + .chain_id = ALC287_FIXUP_LENOVO_YOGA_PRO7, + }, [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_shutup, @@ -8068,6 +8079,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), HDA_CODEC_QUIRK(0x17aa, 0x3938, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), HDA_CODEC_QUIRK(0x17aa, 0x3939, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + SND_PCI_QUIRK(0x17aa, 0x393e, "Lenovo ThinkBook 14 G8+ IPH", ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK), HDA_CODEC_QUIRK(0x17aa, 0x394c, "Lenovo Yoga Slim 7 14AGP11", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), From 6993ae546defd80467309695fce9ae4a1bcfefbe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Jul 2026 10:44:32 +0200 Subject: [PATCH 396/791] ALSA: hda: Drop unneeded calculation of index at get_jack_mode_name() get_jack_mode_name() tries to identify the (potential) index number of the control element to be created, but this index number isn't actually used, since the index is set automatically at instantiating the controls. Drop the unneeded index retrieval and calculation as a cleanup. Along with the change, find_kctl_name() is no longer used, hence drop this function as well. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260730084513.327992-2-tiwai@suse.de --- sound/hda/codecs/generic.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 660a9f2c0ded..9f6f44cdbe1e 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -2695,30 +2695,13 @@ static const struct snd_kcontrol_new out_jack_mode_enum = { .put = out_jack_mode_put, }; -static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - const struct snd_kcontrol_new *kctl; - int i; - - snd_array_for_each(&spec->kctls, i, kctl) { - if (!strcmp(kctl->name, name) && kctl->index == idx) - return true; - } - return false; -} - static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, char *name, size_t name_len) { struct hda_gen_spec *spec = codec->spec; - int idx = 0; - snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); + snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, NULL); strlcat(name, " Jack Mode", name_len); - - for (; find_kctl_name(codec, name, idx); idx++) - ; } static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) From f817bac425c52fe29cd6794071160ab60acfc400 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Jul 2026 10:44:33 +0200 Subject: [PATCH 397/791] ALSA: hda: Drop index handling from snd_hda_get_pin_label() Now no one calls snd_hda_get_pin_label() with the index pointer, so let's drop the index handling from this helper function as a code cleanup. This results in reduction of unneeded code. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260730084513.327992-3-tiwai@suse.de --- sound/hda/codecs/generic.c | 2 +- sound/hda/common/auto_parser.c | 66 +++++------------------------- sound/hda/common/hda_auto_parser.h | 2 +- sound/hda/common/jack.c | 2 +- 4 files changed, 14 insertions(+), 58 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 9f6f44cdbe1e..a4623dd67f81 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -2700,7 +2700,7 @@ static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, { struct hda_gen_spec *spec = codec->spec; - snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, NULL); + snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len); strlcat(name, " Jack Mode", name_len); } diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c index 5bc95d3116ff..b3f16f616396 100644 --- a/sound/hda/common/auto_parser.c +++ b/sound/hda/common/auto_parser.c @@ -601,9 +601,9 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return -1; } -/* get a unique suffix or an index number */ +/* get a unique suffix */ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) + int num_pins) { static const char * const channel_sfx[] = { " Front", " Surround", " CLFE", " Side" @@ -615,11 +615,8 @@ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, return NULL; if (num_pins == 1) return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; + if (num_pins > ARRAY_SIZE(channel_sfx)) return ""; - } return channel_sfx[i]; } @@ -638,27 +635,9 @@ static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid) return ""; } -static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t *pins, int num_pins) -{ - int i, j, idx = 0; - - const char *pfx = check_output_pfx(codec, nid); - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return -1; - for (j = 0; j < i; j++) - if (pfx == check_output_pfx(codec, pins[j])) - idx++; - - return idx; -} - static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) + const char *name, char *label, int maxlen) { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); int attr = snd_hda_get_input_pin_attr(def_conf); @@ -671,19 +650,11 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, if (cfg) { /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); + sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs); + if (!sfx) + sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs); if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = get_hp_label_index(codec, nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0 && indexp) - *indexp = idx; sfx = ""; - } } snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); return 1; @@ -699,7 +670,6 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, * @cfg: the parsed pin configuration * @label: the string buffer to store * @maxlen: the max length of string buffer (including termination) - * @indexp: the pointer to return the index number (for multiple ctls) * * Get a label for the given pin. This function works for both input and * output pins. When @cfg is given as non-NULL, the function tries to get @@ -708,47 +678,33 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, * This function tries to give a unique label string for the pin as much as * possible. For example, when the multiple line-outs are present, it adds * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. */ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) + char *label, int maxlen) { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; bool hdmi; - if (indexp) - *indexp = 0; if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) return 0; switch (get_defcfg_device(def_conf)) { case AC_JACK_LINE_OUT: return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); + label, maxlen); case AC_JACK_SPEAKER: return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); + label, maxlen); case AC_JACK_HP_OUT: return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); + label, maxlen); case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: hdmi = is_hdmi_cfg(def_conf); name = hdmi ? "HDMI" : "SPDIF"; - if (cfg && indexp) - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t pin = cfg->dig_out_pins[i]; - unsigned int c; - if (pin == nid) - break; - c = snd_hda_codec_get_pincfg(codec, pin); - if (hdmi == is_hdmi_cfg(c)) - (*indexp)++; - } break; default: if (cfg) { diff --git a/sound/hda/common/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h index 87af3d8c02f7..3c5f3ad40074 100644 --- a/sound/hda/common/hda_auto_parser.h +++ b/sound/hda/common/hda_auto_parser.h @@ -46,7 +46,7 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, int input); int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); + char *label, int maxlen); enum { INPUT_PIN_ATTR_UNUSED, /* pin not connected */ diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c index e0a5cc38540b..c4338f03a54d 100644 --- a/sound/hda/common/jack.c +++ b/sound/hda/common/jack.c @@ -612,7 +612,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, if (base_name) strscpy(name, base_name, sizeof(name)); else - snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL); + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name)); if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); From df3bdb5533314be6270699ef841d13363b7b1093 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 24 Jul 2026 18:25:43 +0800 Subject: [PATCH 398/791] ASoC: tas2781: Optimize calibration to avoid full device reboot after calibration Some clients have specified a new requirement: complete device restarts should be avoided after calibration. Only a hot boot of the Smart Audio Amplifier is required. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260724102543.2067-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9e6f0ad5f05d..8eb9ead2951e 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -617,6 +617,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + int i; guard(mutex)(&priv->codec_lock); if (priv->chip_id == TAS2563) @@ -624,6 +625,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, else tas2781_calib_stop_put(priv); + /* + * Set reloading-firmware flag after calibration, the flag will work + * during next playback, then set to the program id after reloading + * firmware. + */ + for (i = 0; i < priv->ndev; i++) + priv->tasdevice[i].cur_prog = -1; + return 1; } From c24f4797d8e59a7697d0c1152ece30c6bc9c3fc9 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 29 Jul 2026 11:22:27 +0800 Subject: [PATCH 399/791] ASoC: SDCA: export sdca_find_entity_by_label() helper Export the sdca_find_entity_by_label() helper so that codec drivers can locate SDCA entities by their labels. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260729032227.3750770-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 2 ++ sound/soc/sdca/sdca_functions.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index b1489178b0ef..fb931ae735a2 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1469,5 +1469,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, struct sdca_cluster *sdca_id_find_cluster(struct device *dev, struct sdca_function_data *function, const int id); +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, + const char *entity_label); #endif diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 77940bd6b33c..d9703927325d 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1617,7 +1617,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return 0; } -static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, const char *entity_label) { struct sdca_entity *entity = NULL; @@ -1646,6 +1646,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * return NULL; } +EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, const int id) @@ -1686,7 +1687,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, return ret; } - clock_entity = find_sdca_entity_by_label(function, clock_label); + clock_entity = sdca_find_entity_by_label(function, clock_label); if (!clock_entity) { dev_err(dev, "%s: failed to find clock with label %s\n", entity->label, clock_label); @@ -1871,7 +1872,7 @@ static int find_sdca_entity_connection(struct device *dev, return ret; } - connected_entity = find_sdca_entity_by_label(function, connected_label); + connected_entity = sdca_find_entity_by_label(function, connected_label); if (!connected_entity) { dev_err(dev, "%s: failed to find entity with label %s\n", entity->label, connected_label); From 772e3409961f155d023855d02946ce6e8287593d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 29 Jul 2026 11:22:37 +0800 Subject: [PATCH 400/791] ASoC: SDCA: export sdca_asoc_populate_rate_format() helper Export populate_rate_format() as sdca_asoc_populate_rate_format() so that it can be used by codec drivers. The codec driver could get rate and format information for the IT/OT entity. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260729032237.3750805-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 7 +++++++ sound/soc/sdca/sdca_asoc.c | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index ca35d5a44370..d3024c3b38b9 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -25,6 +25,8 @@ struct snd_soc_dai_driver; struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +struct snd_soc_pcm_stream; +struct sdca_entity; /* convenient macro to handle the mono volume in 7.8 fixed format representation */ #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ @@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, const struct snd_soc_dai_ops *ops); +int sdca_asoc_populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream); + int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, struct sdca_function_data *function, struct snd_pcm_substream *substream, diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index b4dedba719dc..9a6c0036b7be 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -1231,7 +1231,7 @@ static u64 width_find_mask(unsigned int bits) } } -static int populate_rate_format(struct device *dev, +int sdca_asoc_populate_rate_format(struct device *dev, struct sdca_function_data *function, struct sdca_entity *entity, struct snd_soc_pcm_stream *stream) @@ -1292,6 +1292,7 @@ static int populate_rate_format(struct device *dev, return 0; } +EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); /** * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function @@ -1344,7 +1345,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct stream->channels_min = 1; stream->channels_max = SDCA_MAX_CHANNEL_COUNT; - ret = populate_rate_format(dev, function, entity, stream); + ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); if (ret) return ret; From 4f08e2b888962d0265a3af10f1d11fbe3419280d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 29 Jul 2026 14:35:31 +0700 Subject: [PATCH 401/791] ASoC: sprd: sprd-mcdt: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Reviewed-by: Baolin Wang Link: https://patch.msgid.link/20260729073532.56468-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sprd/sprd-mcdt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c index 5f3a2a7bce31..d9a7bbd6c0b6 100644 --- a/sound/soc/sprd/sprd-mcdt.c +++ b/sound/soc/sprd/sprd-mcdt.c @@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, 0, "sprd-mcdt", mcdt); - if (ret) { - dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); + if (ret) return ret; - } sprd_mcdt_init_chans(mcdt, res); From 595201d3910886e323733ea21b5517d7a751b8c1 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 29 Jul 2026 14:35:32 +0700 Subject: [PATCH 402/791] ASoC: sprd: sprd-pcm-dma: Drop redundant error messages The called functions already log failures where appropriate. Remove the error log here to avoid duplicate error messages. Signed-off-by: bui duc phuc Reviewed-by: Baolin Wang Link: https://patch.msgid.link/20260729073532.56468-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sprd/sprd-pcm-dma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index cbf5bf82d96e..f509a4601de2 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, NULL, 0); - if (ret) - dev_err(&pdev->dev, "could not register platform:%d\n", ret); return ret; } From a95cfed54bf9970924a257e51d47a60810410c5d Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Wed, 29 Jul 2026 15:31:54 -0400 Subject: [PATCH 403/791] ASoC: dt-bindings: qcom,sm8250: Add compatible string for SM8475 Add compatible string for the sound card found in the SM8475 SoC. Signed-off-by: Esteban Urrutia Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260729-sm8475-asoc-v1-1-3edad8aa7628@proton.me Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index bd5f5a6268a3..d12ac528c33f 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -26,6 +26,7 @@ properties: - qcom,eliza-sndcard - qcom,hawi-sndcard - qcom,kaanapali-sndcard + - qcom,sm8475-sndcard - qcom,sm8550-sndcard - qcom,sm8650-sndcard - qcom,sm8750-sndcard From 1ad05292c01ba9f9c4a61921ef370b906608b6d9 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Wed, 29 Jul 2026 15:31:55 -0400 Subject: [PATCH 404/791] ASoC: qcom: sc8280xp: Add support for SM8475 With this, SM8475 topologies can have their own firmware folder. Signed-off-by: Esteban Urrutia Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729-sm8475-asoc-v1-2-3edad8aa7628@proton.me Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index a9304784d41e..f0f3cd8fca38 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -362,6 +362,13 @@ static const struct snd_soc_common sm8450_priv_data = { .wcd_jack = true, }; +static const struct snd_soc_common sm8475_priv_data = { + .driver_name = "sm8475", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + static const struct snd_soc_common sm8550_priv_data = { .driver_name = "sm8550", .dapm_widgets = sc8280xp_dapm_widgets, @@ -395,6 +402,7 @@ static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, + { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, From abc7daad426ef93665ef6fbc1b3cd05a814e721e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:50 +0200 Subject: [PATCH 405/791] ASoC: Intel: catpt: Wrap the store firmware-context procedure All store/restore firmware operations are located in the loader.c file. All except the "store firmware context" procedure which is manually called during the runtime suspend, device.c file. Adding a wrapper alters functional flow slightly - DMA channel is requested after the DXSTATE IPC rather than before it but this has no real impact on the procedure. At the same time, such approach limits number of symbols exposed in the core.h file and improves code cohesiveness: all catpt_dma_xxx() definitions in dsp.c, all their usages in loader.c. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 4 +--- sound/soc/intel/catpt/device.c | 33 +++------------------------- sound/soc/intel/catpt/loader.c | 39 +++++++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 3881164422b8..f68807c454c9 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, int catpt_first_boot_firmware(struct catpt_dev *cdev); int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); -int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); -int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); -int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); +int catpt_store_firmware_context(struct catpt_dev *cdev); int catpt_coredump(struct catpt_dev *cdev); #include diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index b176aebea9d5..e36eea8b5408 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -28,44 +28,17 @@ static int catpt_do_suspend(struct device *dev) { struct catpt_dev *cdev = dev_get_drvdata(dev); - struct dma_chan *chan; int ret; - chan = catpt_dma_request_config_chan(cdev); - if (IS_ERR(chan)) - return PTR_ERR(chan); - memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); - if (ret) { - ret = CATPT_IPC_RET(ret); - goto release_dma_chan; - } - - ret = catpt_dsp_stall(cdev, true); if (ret) - goto release_dma_chan; + return CATPT_IPC_RET(ret); - ret = catpt_store_memdumps(cdev, chan); - if (ret) { - dev_err(cdev->dev, "store memdumps failed: %d\n", ret); - goto release_dma_chan; - } - - ret = catpt_store_module_states(cdev, chan); - if (ret) { - dev_err(cdev->dev, "store module states failed: %d\n", ret); - goto release_dma_chan; - } - - ret = catpt_store_streams_context(cdev, chan); - if (ret) - dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); - -release_dma_chan: - dma_release_channel(chan); + ret = catpt_store_firmware_context(cdev); if (ret) return ret; + return catpt_dsp_power_down(cdev); } diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index c577f2e17ddf..880f62896997 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) return __request_region(root, addr, size, NULL, 0); } -int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; @@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } +int catpt_store_firmware_context(struct catpt_dev *cdev) +{ + struct dma_chan *chan; + int ret; + + chan = catpt_dma_request_config_chan(cdev); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + ret = catpt_dsp_stall(cdev, true); + if (ret) + goto exit; + + ret = catpt_store_memdumps(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store memdumps failed: %d\n", ret); + goto exit; + } + + ret = catpt_store_module_states(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store module states failed: %d\n", ret); + goto exit; + } + + ret = catpt_store_streams_context(cdev, chan); + if (ret) + dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); +exit: + dma_release_channel(chan); + return ret; +} + static int catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { From 05d3ba6258026e2c8c9feefefd63ebc82319a301 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:51 +0200 Subject: [PATCH 406/791] ASoC: Intel: catpt: Drop redundant else-if If the preceding if-statement ends with return, there is no need for else-if. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 880f62896997..79742dceef53 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -652,10 +652,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) if (!ret) { dev_err(cdev->dev, "firmware ready timeout\n"); return -ETIMEDOUT; - /* Wake up does not mean FW is ready, an exception could occur. */ - } else if (!cdev->ipc.ready) { - return -EREMOTEIO; } + /* Wake up does not mean FW is ready, an exception could occur. */ + if (!cdev->ipc.ready) + return -EREMOTEIO; /* update sram pg & clock once done booting */ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); From f6c65bf0acc99079e5a9b43d4079ee2a8fe68332 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:52 +0200 Subject: [PATCH 407/791] ASoC: Intel: catpt: Drop redundant signature argument Initial design assumed the mechanism could be reused for loading external modules with signatures differing from the Intel's constant. No users with such characteristics ever appeared rendering the 'signature' argument useless. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 79742dceef53..775781489ded 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -569,8 +569,7 @@ static int catpt_load_firmware(struct catpt_dev *cdev, } static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, - const char *name, const char *signature, - bool restore) + const char *name, bool restore) { struct catpt_fw_hdr *fw; struct firmware *img; @@ -583,7 +582,7 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, return ret; fw = (struct catpt_fw_hdr *)img->data; - if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { + if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "firmware signature mismatch\n"); ret = -EINVAL; goto release_fw; @@ -617,8 +616,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) if (IS_ERR(chan)) return PTR_ERR(chan); - ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, - FW_SIGNATURE, restore); + ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, restore); if (ret) goto release_dma_chan; From 71d1229972e2fc298dff3b40e0b81a11544a8caf Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:53 +0200 Subject: [PATCH 408/791] ASoC: Intel: catpt: Rename module header struct Goal is to match the name of its equivalent on the firmware side. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 775781489ded..06c8b043e292 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -26,7 +26,7 @@ struct catpt_fw_hdr { u32 reserved[4]; } __packed; -struct catpt_fw_mod_hdr { +struct catpt_fw_module_hdr { char signature[FW_SIGNATURE_SIZE]; u32 mod_size; u32 blocks; @@ -357,7 +357,7 @@ static int catpt_load_block(struct catpt_dev *cdev, static int catpt_restore_basefw(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *basefw) + struct catpt_fw_module_hdr *basefw) { u32 offset = sizeof(*basefw); int ret, i; @@ -400,7 +400,7 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, static int catpt_restore_module(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *mod) + struct catpt_fw_module_hdr *mod) { u32 offset = sizeof(*mod); int i; @@ -441,7 +441,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, static int catpt_load_module(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_mod_hdr *mod) + struct catpt_fw_module_hdr *mod) { struct catpt_module_type *type; u32 offset = sizeof(*mod); @@ -497,10 +497,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, fw, sizeof(*fw), false); for (i = 0; i < fw->modules; i++) { - struct catpt_fw_mod_hdr *mod; + struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + offset); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); @@ -543,10 +543,10 @@ static int catpt_load_firmware(struct catpt_dev *cdev, fw, sizeof(*fw), false); for (i = 0; i < fw->modules; i++) { - struct catpt_fw_mod_hdr *mod; + struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + offset); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); From e692a538a421602743df8d479631b55ea7d8cea4 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:54 +0200 Subject: [PATCH 409/791] ASoC: Intel: catpt: Rename firmware loading functions To make the firmware loading proceduce easier to understand, especially around restoring DRAM context, rename the following: catpt_load_images -> catpt_request_dma_load_firmware catpt_load_image -> catpt_request_load_firmware catpt_restore_fwimage -> catpt_restore_dram_rodata catpt_restore_memdumps -> catpt_restore_dram_data catpt_store_memdumps -> catpt_store_dram_data For the exact same reason, update a number of comments related to the subject. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 06c8b043e292..274af8fb8828 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -138,7 +138,7 @@ static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *ch return 0; } -static int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -184,7 +184,7 @@ int catpt_store_firmware_context(struct catpt_dev *cdev) if (ret) goto exit; - ret = catpt_store_memdumps(cdev, chan); + ret = catpt_store_dram_data(cdev, chan); if (ret) { dev_err(cdev->dev, "store memdumps failed: %d\n", ret); goto exit; @@ -232,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) { int i; @@ -267,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) return 0; } -static int catpt_restore_fwimage(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_block_hdr *blk) +static int catpt_restore_dram_rodata(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_block_hdr *blk) { struct resource r1 = {}; int i; @@ -365,7 +365,7 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, basefw, sizeof(*basefw), false); - /* restore basefw image */ + /* Restore IRAM and .rodata for DRAM based on the firmware image. */ for (i = 0; i < basefw->blocks; i++) { struct catpt_fw_block_hdr *blk; @@ -377,8 +377,8 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, blk, false); break; default: - ret = catpt_restore_fwimage(cdev, chan, paddr + offset, - blk); + ret = catpt_restore_dram_rodata(cdev, chan, paddr + offset, + blk); break; } @@ -390,8 +390,8 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, offset += sizeof(*blk) + blk->size; } - /* then proceed with memory dumps */ - ret = catpt_restore_memdumps(cdev, chan); + /* Then proceed with DRAM .data saved before D3. */ + ret = catpt_restore_dram_data(cdev, chan); if (ret) dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); @@ -568,8 +568,8 @@ static int catpt_load_firmware(struct catpt_dev *cdev, return 0; } -static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, - const char *name, bool restore) +static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, + const char *name, bool restore) { struct catpt_fw_hdr *fw; struct firmware *img; @@ -607,7 +607,7 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, return ret; } -static int catpt_load_images(struct catpt_dev *cdev, bool restore) +static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) { struct dma_chan *chan; int ret; @@ -616,7 +616,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) if (IS_ERR(chan)) return PTR_ERR(chan); - ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, restore); + ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); if (ret) goto release_dma_chan; @@ -636,7 +636,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) catpt_dsp_stall(cdev, true); - ret = catpt_load_images(cdev, restore); + ret = catpt_request_dma_load_firmware(cdev, restore); if (ret) { dev_err(cdev->dev, "load binaries failed: %d\n", ret); return ret; From ae6540c4909a0f347c43d0b996c512360867f539 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:55 +0200 Subject: [PATCH 410/791] ASoC: Intel: catpt: Streamline wording of offset variables Two words represent is currently: 'offset' and 'off'. Be cohesive and use one instead. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 48 ++++++++++++++++------------------ 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 274af8fb8828..e7ba9e1e60ae 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -359,7 +359,7 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_module_hdr *basefw) { - u32 offset = sizeof(*basefw); + u32 off = sizeof(*basefw); int ret, i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -369,16 +369,14 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, for (i = 0; i < basefw->blocks; i++) { struct catpt_fw_block_hdr *blk; - blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); switch (blk->ram_type) { case CATPT_RAM_TYPE_IRAM: - ret = catpt_load_block(cdev, chan, paddr + offset, - blk, false); + ret = catpt_load_block(cdev, chan, paddr + off, blk, false); break; default: - ret = catpt_restore_dram_rodata(cdev, chan, paddr + offset, - blk); + ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); break; } @@ -387,7 +385,7 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, return ret; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } /* Then proceed with DRAM .data saved before D3. */ @@ -402,7 +400,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_module_hdr *mod) { - u32 offset = sizeof(*mod); + u32 off = sizeof(*mod); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -412,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, struct catpt_fw_block_hdr *blk; int ret; - blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); switch (blk->ram_type) { case CATPT_RAM_TYPE_INSTANCE: @@ -423,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, ALIGN(blk->size, 4)); break; default: - ret = catpt_load_block(cdev, chan, paddr + offset, + ret = catpt_load_block(cdev, chan, paddr + off, blk, false); break; } @@ -433,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, return ret; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } return 0; @@ -444,7 +442,7 @@ static int catpt_load_module(struct catpt_dev *cdev, struct catpt_fw_module_hdr *mod) { struct catpt_module_type *type; - u32 offset = sizeof(*mod); + u32 off = sizeof(*mod); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -456,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, struct catpt_fw_block_hdr *blk; int ret; - blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); + ret = catpt_load_block(cdev, chan, paddr + off, blk, true); if (ret) { dev_err(cdev->dev, "load block failed: %d\n", ret); return ret; @@ -473,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, type->state_size = blk->size; } - offset += sizeof(*blk) + blk->size; + off += sizeof(*blk) + blk->size; } /* init module type static info */ @@ -490,7 +488,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_hdr *fw) { - u32 offset = sizeof(*fw); + u32 off = sizeof(*fw); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -500,7 +498,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_module_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); @@ -512,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, switch (mod->module_id) { case CATPT_MODID_BASE_FW: - ret = catpt_restore_basefw(cdev, chan, paddr + offset, - mod); + ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); break; default: - ret = catpt_restore_module(cdev, chan, paddr + offset, - mod); + ret = catpt_restore_module(cdev, chan, paddr + off, mod); break; } @@ -526,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, return ret; } - offset += sizeof(*mod) + mod->mod_size; + off += sizeof(*mod) + mod->mod_size; } return 0; @@ -536,7 +532,7 @@ static int catpt_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t paddr, struct catpt_fw_hdr *fw) { - u32 offset = sizeof(*fw); + u32 off = sizeof(*fw); int i; print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, @@ -546,7 +542,7 @@ static int catpt_load_firmware(struct catpt_dev *cdev, struct catpt_fw_module_hdr *mod; int ret; - mod = (struct catpt_fw_module_hdr *)((u8 *)fw + offset); + mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); if (strncmp(fw->signature, mod->signature, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "module signature mismatch\n"); @@ -556,13 +552,13 @@ static int catpt_load_firmware(struct catpt_dev *cdev, if (mod->module_id > CATPT_MODID_LAST) return -EINVAL; - ret = catpt_load_module(cdev, chan, paddr + offset, mod); + ret = catpt_load_module(cdev, chan, paddr + off, mod); if (ret) { dev_err(cdev->dev, "load module failed: %d\n", ret); return ret; } - offset += sizeof(*mod) + mod->mod_size; + off += sizeof(*mod) + mod->mod_size; } return 0; From a0acf55be73414db280372e2ad5aae705b9de403 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:56 +0200 Subject: [PATCH 411/791] ASoC: Intel: catpt: Streamline runtime-variables naming Mimic naming pattern commonly found in the ASoC code: - 'rtd' in case of struct snd_soc_pcm_runtime - 'runtime' in case of struct snd_pcm_runtime Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 8fb0efb67eb1..12a5226cf12c 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { static struct catpt_stream_template * catpt_get_stream_template(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); enum catpt_stream_type type; type = cpu_dai->driver->id; @@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, static void catpt_arrange_page_table(struct snd_pcm_substream *substream, struct snd_dma_buffer *pgtbl) { - struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); int i, pages; - pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); + pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); for (i = 0; i < pages; i++) { u32 pfn, offset; @@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dma_buffer *dmab; struct catpt_stream_runtime *stream; struct catpt_audio_format afmt; @@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, memset(&rinfo, 0, sizeof(rinfo)); rinfo.page_table_addr = stream->pgtbl.addr; - rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); - rinfo.size = rtm->dma_bytes; + rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); + rinfo.size = runtime->dma_bytes; rinfo.offset = 0; rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); @@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, struct catpt_notify_position *pos) { struct snd_pcm_substream *substream = stream->substream; - struct snd_pcm_runtime *r = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t dsppos, newpos; int ret; - dsppos = bytes_to_frames(r, pos->stream_position); + dsppos = bytes_to_frames(runtime, pos->stream_position); if (!stream->prepared) goto exit; @@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, if (stream->template->type != CATPT_STRM_TYPE_RENDER) goto exit; - if (dsppos >= r->buffer_size / 2) - newpos = r->buffer_size / 2; + if (dsppos >= runtime->buffer_size / 2) + newpos = runtime->buffer_size / 2; else newpos = 0; /* @@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, * (buffer half consumed) update wp to allow stream progression. */ ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, - frames_to_bytes(r, newpos), + frames_to_bytes(runtime, newpos), false, false); if (ret) { dev_err(cdev->dev, "update position for stream %d failed: %d\n", @@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { }; static int catpt_component_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtm) + struct snd_soc_pcm_runtime *rtd) { struct catpt_dev *cdev = dev_get_drvdata(component->dev); - snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, cdev->dev, catpt_pcm_hardware.buffer_bytes_max, catpt_pcm_hardware.buffer_bytes_max); @@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, static int catpt_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - if (!rtm->dai_link->no_pcm) + if (!rtd->dai_link->no_pcm) snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); return 0; } @@ -626,13 +626,13 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct catpt_stream_runtime *stream; struct catpt_dev *cdev = dev_get_drvdata(component->dev); u32 pos; - if (rtm->dai_link->no_pcm) + if (rtd->dai_link->no_pcm) return 0; stream = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { .trigger = catpt_dai_trigger, }; -static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, +static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct catpt_ssp_device_format devfmt; struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; From 4075b9d256ef5c22688e2ae812c1b89ac791182a Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 29 Jul 2026 13:00:57 +0200 Subject: [PATCH 412/791] ASoC: Intel: catpt: Streamline control-variables naming Two naming patterns exist currently in the code: 'kcontrol' and 'kctl'. Pick one and stick with it. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260729110057.342447-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 12a5226cf12c..f23c580ba051 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) return CATPT_IPC_RET(ret); } -static int catpt_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = CATPT_CHANNELS_MAX; From c50ed4627e333934aaf0473a822169786c83dce5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 11:24:44 +0300 Subject: [PATCH 413/791] ASoC: SOF: ipc4: Add decoder for RESOURCE_EVENT notifications from firmware Decode and print out the content of currently supported RESOURCE_EVENT notifications from firmware along with the needed data structures and definitions. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730082444.4828-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 53 ++++++++++++++++++++- sound/soc/sof/ipc4.c | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 4554e5e8cab5..e747741f35c0 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -536,12 +536,63 @@ enum sof_ipc4_notification_type { SOF_IPC4_NOTIFY_TYPE_LAST, }; +enum sof_ipc4_resource_type { + SOF_IPC4_MODULE_INSTANCE, + SOF_IPC4_PIPELINE, + SOF_IPC4_GATEWAY, + SOF_IPC4_EDF_TASK, + SOF_IPC4_INVALID_RESOURCE_TYPE, +}; + +enum sof_ipc4_event_type { + /* Underrun detected by the Mixer */ + SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, + /* Error caught during data processing */ + SOF_IPC4_PROCESS_DATA_ERROR = 3, + /* Underrun detected by gateway. */ + SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, + /* Overrun detected by gateway */ + SOF_IPC4_GATEWAY_OVERRUN_DETECTED, +}; + +/** + * struct sof_ipc4_process_data_error_event_data - process data error event payload + * @error_code: Error code returned by data processing function + */ +struct sof_ipc4_process_data_error_event_data { + uint32_t error_code; +}; + +/** + * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload + * @eos_flag: Indicates EndOfStream + * @data_mixed: Data processed by module (in bytes) + * @expected_data_mixed: Expected data to be processed (in bytes) + */ +struct sof_ipc4_mixer_underrun_event_data { + uint32_t eos_flag; + uint32_t data_mixed; + uint32_t expected_data_mixed; +}; + +/** + * union sof_ipc4_resource_event_data - resource event specific payload + * @dws: Raw event data payload as six dwords + * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload + * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload + */ +union sof_ipc4_resource_event_data { + uint32_t dws[6]; + struct sof_ipc4_process_data_error_event_data process_data_error; + struct sof_ipc4_mixer_underrun_event_data mixer_underrun; +}; + struct sof_ipc4_notify_resource_data { uint32_t resource_type; uint32_t resource_id; uint32_t event_type; uint32_t reserved; - uint32_t data[6]; + union sof_ipc4_resource_event_data data; } __packed __aligned(4); #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index c9c6c0c52c62..e77f73390bd8 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, 16, 4, ipc_data, size, false); } +static const char *sof_ipc4_resource_type_str(u32 type) +{ + switch (type) { + case SOF_IPC4_MODULE_INSTANCE: + return "resource: MODULE_INSTANCE"; + case SOF_IPC4_PIPELINE: + return "resource: PIPELINE"; + case SOF_IPC4_GATEWAY: + return "resource: GATEWAY"; + case SOF_IPC4_EDF_TASK: + return "resource: EDF_TASK"; + case SOF_IPC4_INVALID_RESOURCE_TYPE: + return "Resource is invalid"; + default: + return "Unknown resource type"; + } +} + +static const char *sof_ipc4_resource_event_type_str(u32 event_type) +{ + switch (event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + return "event: MIXER_UNDERRUN_DETECTED"; + case SOF_IPC4_PROCESS_DATA_ERROR: + return "event: PROCESS_DATA_ERROR"; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + return "event: GATEWAY_UNDERRUN_DETECTED"; + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + return "event: GATEWAY_OVERRUN_DETECTED"; + default: + return "Unknown event type"; + } +} + +static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; + + /* Print event details */ + switch (data->event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.mixer_underrun.eos_flag, + data->data.mixer_underrun.data_mixed, + data->data.mixer_underrun.expected_data_mixed); + break; + case SOF_IPC4_PROCESS_DATA_ERROR: + dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.process_data_error.error_code); + break; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u)\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type); + break; + default: + dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, + data->data.dws[0], data->data.dws[1], data->data.dws[2], + data->data.dws[3], data->data.dws[4], data->data.dws[5]); + break; + } + + /* Print resource details */ + if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { + u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); + u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); + + dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, module_id, instance_id); + } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { + dev_dbg(sdev->dev, "%s (%u), id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, data->resource_id); + } +} + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) break; case SOF_IPC4_NOTIFY_RESOURCE_EVENT: data_size = sizeof(struct sof_ipc4_notify_resource_data); + handler_func = sof_ipc4_resource_event_handler; break; case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); From ebc60f85331979a73772da07e6356cf4155d677d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Jul 2026 18:14:03 +0200 Subject: [PATCH 414/791] ALSA: hda: Add hda_append_suffix() local helper As strlcat() shall be deprecated in future, provide an alternative just for a simple purpose -- append a suffix string to the given string buffer -- and use it at appropriate places. The code isn't really efficient, but we don't ask for speed here, so let it be. Link: https://lore.kernel.org/amolHJpiluNmBsDU@dev Reviewed-by: Ian Bridges Tested-by: Ian Bridges Link: https://patch.msgid.link/20260730161518.641254-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/generic.c | 4 ++-- sound/hda/common/hda_local.h | 7 +++++++ sound/hda/common/jack.c | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index a4623dd67f81..6120f797b45a 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -2701,7 +2701,7 @@ static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, struct hda_gen_spec *spec = codec->spec; snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len); - strlcat(name, " Jack Mode", name_len); + hda_append_suffix(name, " Jack Mode", name_len); } static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) @@ -5678,7 +5678,7 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, break; } } - strlcat(str, sfx, len); + hda_append_suffix(str, sfx, len); } /* copy PCM stream info from @default_str, and override non-NULL entries diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index 98b2c4acebc2..947a3152fdb5 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -723,4 +723,11 @@ void snd_hda_codec_display_power(struct hda_codec *codec, bool enable); #define codec_dbg(codec, fmt, args...) \ dev_dbg(hda_codec_dev(codec), fmt, ##args) +/* append a suffix string safely; equivalent with strlcat() */ +static inline void hda_append_suffix(char *str, const char *suffix, size_t size) +{ + size_t len = strnlen(str, size); + strscpy(str + len, suffix, size - len); +} + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c index c4338f03a54d..1d6b0f0e6f27 100644 --- a/sound/hda/common/jack.c +++ b/sound/hda/common/jack.c @@ -615,7 +615,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name)); if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ - strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); + hda_append_suffix(name, " Phantom", sizeof(name)); err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; From fc29dfa93b4154a1ab9a52875c058cb48b8736d4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:19 +0200 Subject: [PATCH 415/791] ALSA: 6fire: Use auto-cleanup for firmware loading Clean up the code for managing the firmware loading in the 6fire driver with __free(firmware) and __free(kfree), so that the loaded firmware and the name string are cleaned up automatically. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-2-tiwai@suse.de --- sound/usb/6fire/firmware.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 123c1c6539b8..d9dd8f44b047 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -194,23 +194,20 @@ static int usb6fire_fw_ezusb_upload( int ret; u8 data; struct usb_device *device = interface_to_usbdev(intf); - const struct firmware *fw = NULL; - struct ihex_record *rec = kmalloc_obj(struct ihex_record); + struct ihex_record *rec __free(kfree) = kmalloc_obj(struct ihex_record); if (!rec) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { - kfree(rec); dev_err(&intf->dev, "error requesting ezusb firmware %s.\n", fwname); return ret; } ret = usb6fire_fw_ihex_init(fw, rec); if (ret < 0) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "error validating ezusb firmware %s.\n", fwname); return ret; @@ -219,8 +216,6 @@ static int usb6fire_fw_ezusb_upload( data = 0x01; /* stop ezusb cpu */ ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); if (ret) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "unable to upload ezusb firmware %s: begin message.\n", fwname); @@ -231,8 +226,6 @@ static int usb6fire_fw_ezusb_upload( ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address, rec->data, rec->len); if (ret) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "unable to upload ezusb firmware %s: data urb.\n", fwname); @@ -240,8 +233,6 @@ static int usb6fire_fw_ezusb_upload( } } - release_firmware(fw); - kfree(rec); if (postdata) { /* write data after firmware has been uploaded */ ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr, postdata, postlen); @@ -270,19 +261,18 @@ static int usb6fire_fw_fpga_upload( int ret; int i; struct usb_device *device = interface_to_usbdev(intf); - u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); + u8 *buffer __free(kfree) = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); const char *c; const char *end; - const struct firmware *fw; if (!buffer) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { dev_err(&intf->dev, "unable to get fpga firmware %s.\n", fwname); - kfree(buffer); return -EIO; } @@ -291,8 +281,6 @@ static int usb6fire_fw_fpga_upload( ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0); if (ret) { - kfree(buffer); - release_firmware(fw); dev_err(&intf->dev, "unable to upload fpga firmware: begin urb.\n"); return ret; @@ -304,15 +292,11 @@ static int usb6fire_fw_fpga_upload( ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { - release_firmware(fw); - kfree(buffer); dev_err(&intf->dev, "unable to upload fpga firmware: fw urb.\n"); return ret; } } - release_firmware(fw); - kfree(buffer); ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0); if (ret) { From bff808bc58172af982d10d200ceb30d5bdb6a8b4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:20 +0200 Subject: [PATCH 416/791] ALSA: hda: ca0132: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-3-tiwai@suse.de --- sound/hda/codecs/ca0132.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index 3fe11983d6ca..61c9fb42b1de 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -8530,10 +8530,9 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) static bool ca0132_download_dsp_images(struct hda_codec *codec) { - bool dsp_loaded = false; struct ca0132_spec *spec = codec->spec; const struct dsp_image_seg *dsp_os_image; - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; /* * Alternate firmwares for different variants. The Recon3Di apparently * can use the default firmware, but I'll leave the option in case @@ -8573,15 +8572,10 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { codec_err(codec, "ca0132 DSP load image failed\n"); - goto exit_download; + return false; } - dsp_loaded = dspload_wait_loaded(codec); - -exit_download: - release_firmware(fw_entry); - - return dsp_loaded; + return dspload_wait_loaded(codec); } static void ca0132_download_dsp(struct hda_codec *codec) From f37eed135b97501ae06cfea4b9bb119af499f716 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:21 +0200 Subject: [PATCH 417/791] ALSA: hda: intel: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-4-tiwai@suse.de --- sound/hda/controllers/intel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 9a5c61f43011..8f592032ac15 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2389,7 +2389,7 @@ static int azx_probe_continue(struct azx *chip) #ifdef CONFIG_SND_HDA_PATCH_LOADER if (patch[dev] && *patch[dev]) { - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; dev_info(&pci->dev, "Applying patch firmware '%s'\n", patch[dev]); @@ -2398,7 +2398,6 @@ static int azx_probe_continue(struct azx *chip) "Cannot load firmware, continue without patching\n"); } else { err = snd_hda_load_patch(&chip->bus, fw->size, fw->data); - release_firmware(fw); if (err < 0) goto out_free; } From ba2ad78c193bb422f4a984c19a92c3cf19e354b9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:22 +0200 Subject: [PATCH 418/791] ALSA: msnd: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-5-tiwai@suse.de --- sound/isa/msnd/msnd_pinnacle.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 0d5f4461a7bc..eac6e22362cc 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -367,7 +367,8 @@ static int snd_msnd_init_sma(struct snd_msnd *chip) static int upload_dsp_code(struct snd_card *card) { struct snd_msnd *chip = card->private_data; - const struct firmware *init_fw = NULL, *perm_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; + const struct firmware *perm_fw __free(firmware) = NULL; int err; outb(HPBLKSEL_0, chip->io + HP_BLKS); @@ -375,28 +376,21 @@ static int upload_dsp_code(struct snd_card *card) err = request_firmware(&init_fw, INITCODEFILE, card->dev); if (err < 0) { dev_err(card->dev, LOGNAME ": Error loading " INITCODEFILE); - goto cleanup1; + return err; } err = request_firmware(&perm_fw, PERMCODEFILE, card->dev); if (err < 0) { dev_err(card->dev, LOGNAME ": Error loading " PERMCODEFILE); - goto cleanup; + return err; } memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size); if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) { dev_warn(card->dev, LOGNAME ": Error uploading to DSP\n"); - err = -ENODEV; - goto cleanup; + return -ENODEV; } dev_info(card->dev, LOGNAME ": DSP firmware uploaded\n"); - err = 0; - -cleanup: - release_firmware(perm_fw); -cleanup1: - release_firmware(init_fw); - return err; + return 0; } #ifdef MSND_CLASSIC From 81d3f401548921eeca98efa98248144d18672146 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:23 +0200 Subject: [PATCH 419/791] ALSA: hda: cs35l41: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with auto-cleanup with __free(firmware). A NULL clear is added at cs35l41_request_firmware_file() for avoiding the double-free. Note that the driver still keeps a few manual firmware releases because it retries with different firmware files when one of firmware pairs fails. Only the code refactoring, no functional changes. Cc: patches@opensource.cirrus.com Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-6-tiwai@suse.de --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 33 ++++++++-------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 237059ef22f5..c8eba0638ada 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -170,6 +170,7 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, char *s, c; int ret = 0; + *firmware = NULL; if (spkid > -1 && ssid && amp_name) *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], @@ -528,8 +529,8 @@ static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) { - const struct firmware *tuning_param_file = NULL; - char *tuning_param_filename = NULL; + const struct firmware *tuning_param_file __free(firmware) = NULL; + char *tuning_param_filename __free(kfree) = NULL; int ret; ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, @@ -548,19 +549,16 @@ static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_ cs35l41_set_default_tuning_params(cs35l41); } - release_firmware(tuning_param_file); - kfree(tuning_param_filename); - return ret; } static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) { - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; + const struct firmware *coeff_firmware __free(firmware) = NULL; + const struct firmware *wmfw_firmware __free(firmware) = NULL; struct cs_dsp *dsp = &cs35l41->cs_dsp; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; + char *coeff_filename __free(kfree) = NULL; + char *wmfw_filename __free(kfree) = NULL; int ret; if (!cs35l41->halo_initialized) { @@ -592,20 +590,13 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, cs35l41_hda_fw_ids[cs35l41->firmware_type]); - if (ret) - goto err; + if (ret) { + cs35l41_set_default_tuning_params(cs35l41); + return ret; + } cs35l41_hda_apply_calibration(cs35l41); - -err: - if (ret) - cs35l41_set_default_tuning_params(cs35l41); - release_firmware(wmfw_firmware); - release_firmware(coeff_firmware); - kfree(wmfw_filename); - kfree(coeff_filename); - - return ret; + return 0; } static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) From 185841c94accce919607e9e4ea59baf3ddec6b99 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:24 +0200 Subject: [PATCH 420/791] ALSA: hda: cs35l56: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with auto-cleanup. By the use of __free(firmware), we can replace the manual mutex locks with guard() gracefully, too. Only the code refactoring, no functional changes. Cc: patches@opensource.cirrus.com Reviewed-by: Richard Fitzgerald Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-7-tiwai@suse.de --- sound/hda/codecs/side-codecs/cs35l56_hda.c | 35 ++++++---------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index 78c2cf387a00..bc207ab5b020 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -527,18 +527,6 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, base_name, NULL, NULL, "bin"); } -static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, - char *wmfw_filename, - const struct firmware *coeff_firmware, - char *coeff_filename) -{ - release_firmware(wmfw_firmware); - kfree(wmfw_filename); - - release_firmware(coeff_firmware); - kfree(coeff_filename); -} - static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) { int ret; @@ -561,10 +549,10 @@ static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) { - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; + const struct firmware *coeff_firmware __free(firmware) = NULL; + const struct firmware *wmfw_firmware __free(firmware) = NULL; + char *coeff_filename __free(kfree) = NULL; + char *wmfw_filename __free(kfree) = NULL; unsigned int preloaded_fw_ver; bool firmware_missing; int ret; @@ -606,14 +594,14 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (firmware_missing) { if (!wmfw_firmware) { dev_err(cs35l56->base.dev, ".%s file required but not found\n", "wmfw"); - goto err_fw_release; + return; } else if (!coeff_firmware) { dev_err(cs35l56->base.dev, ".%s file required but not found\n", "bin"); - goto err_fw_release; + return; } } - mutex_lock(&cs35l56->base.irq_lock); + guard(mutex)(&cs35l56->base.irq_lock); /* * If the firmware hasn't been patched it must be shutdown before @@ -624,14 +612,14 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (firmware_missing && (wmfw_firmware || coeff_firmware)) { ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) - goto err; + return; } ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, "misc"); if (ret) { dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); - goto err; + return; } if (wmfw_filename) @@ -679,11 +667,6 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) err_powered_up: if (!cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); -err: - mutex_unlock(&cs35l56->base.irq_lock); -err_fw_release: - cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); } static void cs35l56_hda_dsp_work(struct work_struct *work) From 0585a0c7ab8f00f9f978eb33674af08c8d017957 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:25 +0200 Subject: [PATCH 421/791] ALSA: sscape: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-8-tiwai@suse.de --- sound/isa/sscape.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index ce8a59650e38..9b615b588cdf 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -526,7 +526,7 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data, static int sscape_upload_bootblock(struct snd_card *card) { struct soundscape *sscape = get_card_soundscape(card); - const struct firmware *init_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; int data = 0; int ret; @@ -537,8 +537,6 @@ static int sscape_upload_bootblock(struct snd_card *card) } ret = upload_dma_data(sscape, init_fw->data, init_fw->size); - release_firmware(init_fw); - guard(spinlock_irqsave)(&sscape->lock); if (ret == 0) data = host_read_ctrl_unsafe(sscape->io_base, 100); @@ -562,7 +560,7 @@ static int sscape_upload_bootblock(struct snd_card *card) static int sscape_upload_microcode(struct snd_card *card, int version) { struct soundscape *sscape = get_card_soundscape(card); - const struct firmware *init_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; char name[14]; int err; @@ -579,8 +577,6 @@ static int sscape_upload_microcode(struct snd_card *card, int version) dev_info(card->dev, "sscape: MIDI firmware loaded %zu KBs\n", init_fw->size >> 10); - release_firmware(init_fw); - return err; } From 5f8fc08a65fe4272a554ae3826340d5176a808c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:26 +0200 Subject: [PATCH 422/791] ALSA: wavefront: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-9-tiwai@suse.de --- sound/isa/wavefront/wavefront_fx.c | 23 +++++++---------------- sound/isa/wavefront/wavefront_synth.c | 4 +--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index beca35ce04f3..6d95ea338ea9 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -232,41 +232,32 @@ snd_wavefront_fx_start (snd_wavefront_t *dev) { unsigned int i; int err; - const struct firmware *firmware = NULL; + const struct firmware *firmware __free(firmware) = NULL; if (dev->fx_initialized) return 0; err = request_firmware(&firmware, "yamaha/yss225_registers.bin", dev->card->dev); - if (err < 0) { - err = -1; - goto out; - } + if (err < 0) + return -1; for (i = 0; i + 1 < firmware->size; i += 2) { if (firmware->data[i] >= 8 && firmware->data[i] < 16) { outb(firmware->data[i + 1], dev->base + firmware->data[i]); } else if (firmware->data[i] == WAIT_IDLE) { - if (!wavefront_fx_idle(dev)) { - err = -1; - goto out; - } + if (!wavefront_fx_idle(dev)) + return -1; } else { dev_err(dev->card->dev, "invalid address in register data\n"); - err = -1; - goto out; + return -1; } } dev->fx_initialized = 1; - err = 0; - -out: - release_firmware(firmware); - return err; + return 0; } MODULE_FIRMWARE("yamaha/yss225_registers.bin"); diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 2f57a6795d22..0c8f5cf26455 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -2053,7 +2053,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path) const unsigned char *buf; int len, err; int section_cnt_downloaded = 0; - const struct firmware *firmware; + const struct firmware *firmware __free(firmware) = NULL; err = request_firmware(&firmware, path, dev->card->dev); if (err < 0) { @@ -2108,11 +2108,9 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path) section_cnt_downloaded++; } - release_firmware(firmware); return 0; failure: - release_firmware(firmware); dev_err(dev->card->dev, "firmware download failed!!!\n"); return 1; } From d380f0920bee6763efe4cf29a35fa3729a493709 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:27 +0200 Subject: [PATCH 423/791] ALSA: asihpi: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-10-tiwai@suse.de --- sound/pci/asihpi/hpidspcd.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c index b1b5a131f626..6d2c27e47d07 100644 --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -23,7 +23,7 @@ struct dsp_code_private { short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, u32 *os_error_code) { - const struct firmware *firmware; + const struct firmware *firmware __free(firmware) = NULL; struct pci_dev *dev = os_data; struct code_header header; char fw_name[20]; @@ -37,11 +37,11 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, if (err || !firmware) { dev_err(&dev->dev, "%d, request_firmware failed for %s\n", err, fw_name); - goto error1; + goto error; } if (firmware->size < sizeof(header)) { dev_err(&dev->dev, "Header size too small %s\n", fw_name); - goto error2; + goto error; } memcpy(&header, firmware->data, sizeof(header)); @@ -51,7 +51,7 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dev_err(&dev->dev, "Invalid firmware header size %d != file %zd\n", header.size, firmware->size); - goto error2; + goto error; } if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) { @@ -59,7 +59,7 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dev_err(&dev->dev, "Incompatible firmware version DSP image %X != Driver %X\n", header.version, HPI_VER); - goto error2; + goto error; } if (header.version != HPI_VER) { @@ -72,19 +72,17 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dsp_code->pvt = kmalloc_obj(*dsp_code->pvt); if (!dsp_code->pvt) { err_ret = HPI_ERROR_MEMORY_ALLOC; - goto error2; + goto error; } dsp_code->pvt->dev = dev; - dsp_code->pvt->firmware = firmware; + dsp_code->pvt->firmware = no_free_ptr(firmware); dsp_code->header = header; dsp_code->block_length = header.size / sizeof(u32); dsp_code->word_count = sizeof(header) / sizeof(u32); return 0; -error2: - release_firmware(firmware); -error1: +error: dsp_code->block_length = 0; return err_ret; } From 1d9a75c9732193d38cc13c287f14948ef921b2ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:28 +0200 Subject: [PATCH 424/791] ALSA: cs46xx: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-11-tiwai@suse.de --- sound/pci/cs46xx/cs46xx_lib.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 1c11023184ac..19a6927c079d 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -387,7 +387,7 @@ static int load_firmware(struct snd_cs46xx *chip, unsigned int nums, fwlen, fwsize; const __le32 *fwdat; struct dsp_module_desc *module = NULL; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; char fw_path[32]; sprintf(fw_path, "cs46xx/%s", fw_name); @@ -395,10 +395,8 @@ static int load_firmware(struct snd_cs46xx *chip, if (err < 0) return err; fwsize = fw->size / 4; - if (fwsize < 2) { - err = -EINVAL; - goto error; - } + if (fwsize < 2) + return -EINVAL; err = -ENOMEM; module = kzalloc_obj(*module); @@ -454,14 +452,12 @@ static int load_firmware(struct snd_cs46xx *chip, } *module_ret = module; - release_firmware(fw); return 0; error_inval: err = -EINVAL; error: free_module_desc(module); - release_firmware(fw); return err; } @@ -500,22 +496,18 @@ MODULE_FIRMWARE("cs46xx/ba1"); static int load_firmware(struct snd_cs46xx *chip) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int i, size, err; err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev); if (err < 0) return err; - if (fw->size != sizeof(*chip->ba1)) { - err = -EINVAL; - goto error; - } + if (fw->size != sizeof(*chip->ba1)) + return -EINVAL; chip->ba1 = vmalloc(sizeof(*chip->ba1)); - if (!chip->ba1) { - err = -ENOMEM; - goto error; - } + if (!chip->ba1) + return -ENOMEM; memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1)); @@ -524,11 +516,9 @@ static int load_firmware(struct snd_cs46xx *chip) for (i = 0; i < BA1_MEMORY_COUNT; i++) size += chip->ba1->memory[i].size; if (size > BA1_DWORD_SIZE * 4) - err = -EINVAL; + return -EINVAL; - error: - release_firmware(fw); - return err; + return 0; } static __maybe_unused int snd_cs46xx_download_image(struct snd_cs46xx *chip) From b804214907f4738167277f77aa33abbd69db5301 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:29 +0200 Subject: [PATCH 425/791] ALSA: korg1212: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-12-tiwai@suse.de --- sound/pci/korg1212/korg1212.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index d16acf83668a..f4d8402f835f 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1980,7 +1980,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) __maybe_unused unsigned ioport_size; __maybe_unused unsigned iomem2_size; struct snd_korg1212 *korg1212 = card->private_data; - const struct firmware *dsp_code; + const struct firmware *dsp_code __free(firmware) = NULL; err = pcim_enable_device(pci); if (err < 0) @@ -2147,10 +2147,8 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->dma_dsp = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, dsp_code->size); - if (!korg1212->dma_dsp) { - release_firmware(dsp_code); + if (!korg1212->dma_dsp) return -ENOMEM; - } K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", korg1212->dma_dsp->area, korg1212->dma_dsp->addr, dsp_code->size, @@ -2158,8 +2156,6 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) memcpy(korg1212->dma_dsp->area, dsp_code->data, dsp_code->size); - release_firmware(dsp_code); - rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); if (rc) From cbabe7774ad77ef6fcb088077f6331c2c7fdf7e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:30 +0200 Subject: [PATCH 426/791] ALSA: mixart: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-13-tiwai@suse.de --- sound/pci/mixart/mixart_hwdep.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 439e3ff05fe1..aa8d5bae30d0 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -560,12 +560,11 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr) "miXart8.xlx", "miXart8.elf", "miXart8AES.xlx" }; char path[32]; - - const struct firmware *fw_entry; int i, err; for (i = 0; i < 3; i++) { sprintf(path, "mixart/%s", fw_files[i]); + const struct firmware *fw_entry __free(firmware) = NULL; if (request_firmware(&fw_entry, path, &mgr->pci->dev)) { dev_err(&mgr->pci->dev, "miXart: can't load firmware %s\n", path); @@ -573,7 +572,6 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr) } /* fake hwdep dsp record */ err = mixart_dsp_load(mgr, i, fw_entry); - release_firmware(fw_entry); if (err < 0) return err; mgr->dsp_loaded |= 1 << i; From 26c602eea1f8ac9154c44e5e206f35335f16e025 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:31 +0200 Subject: [PATCH 427/791] ALSA: pcxhr: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-14-tiwai@suse.de --- sound/pci/pcxhr/pcxhr_hwdep.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c index 249805065f61..54f3950579aa 100644 --- a/sound/pci/pcxhr/pcxhr_hwdep.c +++ b/sound/pci/pcxhr/pcxhr_hwdep.c @@ -367,7 +367,6 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) }; char path[32]; - const struct firmware *fw_entry; int i, err; int fw_set = mgr->fw_file_set; @@ -375,6 +374,7 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) if (!fw_files[fw_set][i]) continue; sprintf(path, "pcxhr/%s", fw_files[fw_set][i]); + const struct firmware *fw_entry __free(firmware) = NULL; if (request_firmware(&fw_entry, path, &mgr->pci->dev)) { dev_err(&mgr->pci->dev, "pcxhr: can't load firmware %s\n", @@ -383,7 +383,6 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) } /* fake hwdep dsp record */ err = pcxhr_dsp_load(mgr, i, fw_entry); - release_firmware(fw_entry); if (err < 0) return err; mgr->dsp_loaded |= 1 << i; From cefb2f905bb13aea37e5cbab179b602c5817a1c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jul 2026 10:37:32 +0200 Subject: [PATCH 428/791] ALSA: sh: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260729083735.120219-15-tiwai@suse.de --- sound/sh/aica.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 8196e1bf0416..078fc7fc08f9 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -518,8 +518,9 @@ static const struct snd_kcontrol_new snd_aica_pcmvolume_control = { static int load_aica_firmware(void) { int err; - const struct firmware *fw_entry; spu_reset(); + + const struct firmware *fw_entry __free(firmware) = NULL; err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); if (unlikely(err)) return err; @@ -527,7 +528,6 @@ static int load_aica_firmware(void) spu_disable(); spu_memload(0, fw_entry->data, fw_entry->size); spu_enable(); - release_firmware(fw_entry); return err; } From c663014e452d69d5f34b8b16fecf1590b3696082 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 28 Jul 2026 10:46:43 +0200 Subject: [PATCH 429/791] ASoC: qcom: audioreach: compute active channel maps from channel_map The Qualcom SM8650 based Ayaneo Pocket S2 gaming device has a set of 2 WSA speakers connected on the WSA2 lines. But the Audioreach DSP only handles WSA2 in pair with the WSA interface by using the upper bits of the active_channels_mask for WSA2 and the lower bits for WSA: /-------------------------------------------------\ | Bits | 3 | 2 | 1 | 0 | |-------------------------------------------------| | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | \-------------------------------------------------/ Setting only the WSA2 upper bits is perfectly valid and functional but the current Audioreach code builds the bitmask from the channels count with: active_channels_mask = (1 << num_channels) - 1; In order to enable the WSA2 bits the channel count should be 4, but the lower WSA bits are then also enabled and the DSP errors out when trying to play on the disabled WSA interface. A solution would've been to add a fake WSA2 topology element which would be translated into the top bits only, but it's not clean and add some special exceptions in the generic Audioreach code. The solution suggested by Srinivas is to use the channel mapping to set this bitmask. This works but makes all the other calls using the channel mapping fail because the DSP requires the channel_mapping table to start from index 0 and using num_channel length in order to apply the mapping on the active_channels_mask bits in order. So we need to skip the empty channel mapping entries in all other users of the channel_map to build valid channel_mapping tables. This should not break any other usecases since the default channel mapping always start from index 0, and will add flexibilty to allow some special non linear mapping for other interfaces as well. Suggested-by: Srinivas Kandagatla Tested-by: Srinivas Kandagatla Reviewed-by: Srinivas Kandagatla Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260728-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v3-1-b29f44720178@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index e6e9eb2e85aa..0cc840aca69d 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -703,6 +703,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; int size = ic_sz + ep_sz + fs_sz + pm_sz; void *p; + int i; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); if (IS_ERR(pkt)) @@ -741,7 +742,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, intf_cfg->cfg.lpaif_type = module->hw_interface_type; intf_cfg->cfg.intf_index = module->hw_interface_idx; - intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; + intf_cfg->cfg.active_channels_mask = 0; + /* Convert the physical channel mapping into a bit field */ + for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) + if (cfg->channel_map[i]) + intf_cfg->cfg.active_channels_mask |= BIT(i); + p += ic_sz; pm_cfg = p; @@ -840,7 +846,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, uint32_t num_channels = cfg->num_channels; int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + APM_MODULE_PARAM_DATA_SIZE; - int i; + int i, j; void *p; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); @@ -860,8 +866,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, media_format->sample_rate = cfg->sample_rate; media_format->bit_width = cfg->bit_width; media_format->num_channels = cfg->num_channels; - for (i = 0; i < num_channels; i++) - media_format->channel_mapping[i] = cfg->channel_map[i]; + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { + if (!cfg->channel_map[i]) + continue; + media_format->channel_mapping[j++] = cfg->channel_map[i]; + } return q6apm_send_cmd_sync(graph->apm, pkt, 0); } @@ -1080,6 +1090,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, struct apm_pcm_module_media_fmt_cmd *cfg; struct apm_module_param_data *param_data; int payload_size; + int i, j; if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); @@ -1113,7 +1124,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->num_channels = mcfg->num_channels; media_cfg->q_factor = mcfg->bit_width - 1; media_cfg->bits_per_sample = mcfg->bit_width; - memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; + media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; + } return q6apm_send_cmd_sync(graph->apm, pkt, 0); } @@ -1163,6 +1179,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, struct payload_media_fmt_pcm *cfg; struct media_format *header; int rc, payload_size; + int i, j; void *p; if (num_channels > 4) { @@ -1202,7 +1219,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, cfg->q_factor = mcfg->bit_width - 1; cfg->endianness = PCM_LITTLE_ENDIAN; cfg->num_channels = mcfg->num_channels; - memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; + cfg->channel_mapping[j++] = mcfg->channel_map[i]; + } } else { rc = audioreach_set_compr_media_format(header, p, mcfg); if (rc) @@ -1279,7 +1301,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; int op_sz, cm_sz, ex_sz; struct apm_module_param_data *param_data; - int rc, i, payload_size; + int rc, i, payload_size, j; struct gpr_pkt *pkt; void *p; @@ -1320,14 +1342,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; cm_cfg->cfg.num_channels = num_channels * 2; - for (i = 0; i < num_channels; i++) { + /* Convert the physical mapping to a logical mapping of the channels */ + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { + if (!mcfg->channel_map[i]) + continue; /* * Map speakers into Vsense and then Isense of each channel. * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: * [1, 2, 3, 4] */ - cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; - cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; + cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; + cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; + + ++j; } p += cm_sz; From b7b0a445ace2a15beb2bceaccb0213214f42f339 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 28 Jul 2026 10:46:44 +0200 Subject: [PATCH 430/791] ASoC: dt-bindings: qcom,sm8250: Add Ayaneo Pocket S2 sound card Document the bindings for the sound card on the Ayaneo Pocket S2 which uses the special speaker connection incompatible with the default SM8650 sound card. Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260728-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v3-2-b29f44720178@linaro.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index bd5f5a6268a3..0998b2abd8bb 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -31,6 +31,7 @@ properties: - qcom,sm8750-sndcard - const: qcom,sm8450-sndcard - enum: + - ayaneo,pocket-s2-sndcard - fairphone,fp4-sndcard - fairphone,fp5-sndcard - qcom,apq8096-sndcard From 968d38918ae85e67069099ea456e3e13fea70081 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 28 Jul 2026 10:46:45 +0200 Subject: [PATCH 431/791] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping The WSA Speakers are connected on the WSA2 interface, but the WSA and WSA2 links are handled as a single dai and DSP interface, so we need to specify the channel mapping of the Ayaneo Pocket S2 for the WSA dai in order to have functional playback and avoid DSP errors. Let's add a special entry for the Ayaneo Pocket S2 adding a prepare callback in order to set the proper channel mapping. Reviewed-by: Srinivas Kandagatla Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260728-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v3-3-b29f44720178@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index a9304784d41e..4ccff32a413b 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -14,6 +14,7 @@ #include "qdsp6/q6afe.h" #include "qdsp6/q6apm.h" #include "qdsp6/q6prm.h" +#include "qdsp6/q6dsp-common.h" #include "common.h" #include "sdw.h" @@ -50,6 +51,7 @@ struct snd_soc_common { bool mi2s_mclk_enable; bool mi2s_bclk_enable; bool wcd_jack; + int (*snd_prepare)(struct snd_pcm_substream *substream); }; struct sc8280xp_snd_data { @@ -213,12 +215,58 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return 0; } +/* + * WSA and WSA2 are handled as a single interface with the + * following channels mask: + * __________________________________________________ + * | Bits | 3 | 2 | 1 | 0 | + * --------------------------------------------------- + * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | + * --------------------------------------------------- + * + * The Ayaneo Pocket S2 speakers are connected only to + * the WSA2 interface and the WSA interface is not enabled. + * + * Set the channel mapping on the WSA2 channels only. + */ +static const unsigned int ayaneo_ps2_channels_mapping[] = { + 0, /* WSA Ch1 */ + 0, /* WSA Ch2 */ + PCM_CHANNEL_FL, /* WSA2 Ch1 */ + PCM_CHANNEL_FR /* WSA2 Ch2 */ +}; + +static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + unsigned int channels = substream->runtime->channels; + + if (cpu_dai->id != WSA_CODEC_DMA_RX_0) + return 0; + + if (channels != 2) + return -EINVAL; + + return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + ARRAY_SIZE(ayaneo_ps2_channels_mapping), + ayaneo_ps2_channels_mapping); +} + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + if (data->snd_soc_common_priv->snd_prepare) { + int ret; + + ret = data->snd_soc_common_priv->snd_prepare(substream); + if (ret) + return ret; + } + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); } @@ -293,6 +341,14 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static struct snd_soc_common ayaneo_ps2_priv_data = { + .driver_name = "ayaneo-ps2", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .snd_prepare = ayaneo_ps2_snd_prepare, + .wcd_jack = true, +}; + static const struct snd_soc_common eliza_priv_data = { .driver_name = "eliza", .dapm_widgets = sc8280xp_dapm_widgets, @@ -384,6 +440,7 @@ static const struct snd_soc_common sm8750_priv_data = { }; static const struct of_device_id snd_sc8280xp_dt_match[] = { + { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, From 74a66323e1489cfccecf6dfcfa1df4a914676bc2 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 31 Jul 2026 16:41:14 +0800 Subject: [PATCH 432/791] ASoC: rt722: reinitialize rt722_sdca_jack_init() after reset Check whether the .set_jack callback has already been invoked before the reset. If so, call rt722_sdca_jack_init() again to restore the jack settings. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260731084114.4142106-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index decf9407ab6d..e8c63cc9fe7f 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1892,6 +1892,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) rt722_sdca_amp_preset(rt722); rt722_sdca_jack_preset(rt722); + if (rt722->hs_jack && (!rt722->first_hw_init)) + rt722_sdca_jack_init(rt722); + if (rt722->first_hw_init) { regcache_cache_bypass(rt722->regmap, false); regcache_mark_dirty(rt722->regmap); From 5a25f27f51185e24fc408db17aab89ef3cd33be3 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:36 +0300 Subject: [PATCH 433/791] ASoC: SOF: ipc4-topology: Remove dp_ from all module memory attributes Remove dp-prefix from all module instance's memory attributes and related data structures. The attributes are not anymore exclusively for Data Processing module instances, but generic for all module instances. However, the module init payload is still only for DP module instances. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 14 +++++++------- sound/soc/sof/sof-audio.h | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 8ac7dde32f77..7808a679e25d 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, offsetof(struct snd_sof_widget, comp_domain)}, {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_domain_id)}, + offsetof(struct snd_sof_widget, domain_id)}, {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_heap_bytes)}, + offsetof(struct snd_sof_widget, heap_bytes)}, {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, dp_stack_bytes)}, + offsetof(struct snd_sof_widget, stack_bytes)}, }; static const struct sof_topology_token gain_tokens[] = { @@ -3118,7 +3118,7 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, /* Add object array objects after ext_init */ - /* Add dp_memory_data if comp_domain indicates DP */ + /* Add memory_data if comp_domain indicates DP */ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | @@ -3127,9 +3127,9 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, sizeof(u32))); ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; - dp_mem_data->domain_id = swidget->dp_domain_id; - dp_mem_data->stack_bytes = swidget->dp_stack_bytes; - dp_mem_data->heap_bytes = swidget->dp_heap_bytes; + dp_mem_data->domain_id = swidget->domain_id; + dp_mem_data->stack_bytes = swidget->stack_bytes; + dp_mem_data->heap_bytes = swidget->heap_bytes; ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 138e5fcc2dd0..ae95efc9be1c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -459,10 +459,10 @@ struct snd_sof_widget { /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ u32 comp_domain; - /* The values below are added to mod_init pay load if comp_domain indicates DP component */ - u32 dp_domain_id; /* DP process userspace domain ID */ - u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ - u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ + /* Module instance's memory configuration. */ + u32 domain_id; /* Module instance's userspace domain ID */ + u32 stack_bytes; /* Module instance's stack size requirement */ + u32 heap_bytes; /* Module instance's heap size requirement */ struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ From 18d0619ced261e7960efd05125dce604d1d9ac89 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:37 +0300 Subject: [PATCH 434/791] ASoC: SOF: ipc4-topology: Fix SOF_TKN_COMP_STACK_BYTES_REQUIREMENT id The was inconsistency with SOF_TKN_COMP_STACK_BYTES_REQUIREMENT and SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT token ids in the Linux driver code with SOF FW topology code. This commit fixes the Linux side to match tools/topology/topology2/include/common/tokens.conf Link: https://github.com/thesofproject/sof/blob/main/tools/topology/topology2/include/common/tokens.conf#L30 Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index cc694a397987..d42adbef0478 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -111,8 +111,8 @@ #define SOF_TKN_COMP_SCHED_DOMAIN 418 #define SOF_TKN_COMP_DOMAIN_ID 419 -#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 -#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 +#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 +#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 /* SSP */ #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 From 52db046c388d32f8b99caa4312ae3712c1cce3e7 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:38 +0300 Subject: [PATCH 435/791] ASoC: SOF: ipc4: Add SOF_IPC4_GLB_CREATE_PIPELINE payload macros and structs Adds SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY macros to set extension bit in SOF_IPC4_GLB_CREATE_PIPELINE indicating presence of the payload, and all necessary macros and structs to create the payload. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 76 ++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 4554e5e8cab5..6fbf62c4075c 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) +#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) + /* pipeline set state ipc msg */ #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) @@ -654,13 +658,83 @@ enum sof_ipc4_mod_init_ext_obj_id { SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, }; -/* DP module memory configuration data object for ext_init object array */ +/* DP module memory configuration data object for object array */ struct sof_ipc4_mod_init_ext_dp_memory_data { u32 domain_id; /* userspace domain ID */ u32 stack_bytes; /* stack size in bytes, 0 means default size */ u32 heap_bytes; /* stack size in bytes, 0 means default size */ } __packed __aligned(4); +/* + * This set of macros are very similar to the set above, but these are + * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. + * + * Macros for creating struct sof_ipc4_glb_pipe_payload payload with + * its associated data. struct sof_ipc4_glb_pipe_payload should be the + * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, + * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. + * + * The macros below apply to sof_ipc4_glb_pipe_payload.word0 + */ +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) + +struct sof_ipc4_glb_pipe_payload { + u32 word0; + u32 rsvd1; + u32 rsvd2; +} __packed __aligned(4); + +/* + * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary + * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit + * indicates that an array object follows struct + * sof_ipc4_glb_pipe_payload. + * + * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct + * sof_ipc4_glb_pipe_ext_object indicates if the array is continued + * with another object. The header has also fields to identify the + * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's + * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not + * including the header itself. + * + * The macros below apply to sof_ipc4_glb_pipe_ext_object.header + */ +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) + +struct sof_ipc4_glb_pipe_ext_object { + u32 header; + u32 data[]; +} __packed __aligned(4); + +enum sof_ipc4_glb_pipe_ext_obj_id { + SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, + SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, + SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, +}; + +/* Pipeline memory configuration data object for ext_init object array */ +struct sof_ipc4_glb_pipe_ext_obj_memory_data { + u32 domain_id; /* userspace domain ID */ + u32 stack_bytes; /* stack size in bytes */ + u32 heap_bytes; /* heap size in bytes */ +} __packed __aligned(4); + /** @}*/ #endif From 0244c162d1a5e4aef501feeafbe93ac439a330e6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:39 +0300 Subject: [PATCH 436/791] ASoC: SOF: ipc4-topology: Add payload to pipeline create messages Start adding payloads to pipeline create messages. The payload contains information for payload specific memory configuration. All non DP module instances within the same pipeline share the same memory attributes and access the same resources. The new logic sums interim, lifetime, and shared heap memory requirements together and picks the highest stack requirement of all module instances belonging to a pipeline. These pipeline specific attributes are sent as struct sof_ipc4_glb_pipe_payload payload in pipeline's create message. The idea is to pass common memory configuration for all the Low Latency modules in the pipeline in pipeline create message payload. The Data Processing module instances will still have an individual memory configuration in struct sof_ipc4_mod_init_ext_dp_memory_data payloads as before. In their payload everything is as it was before, all attributes are copied directly from their topology attributes. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 112 +++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 7808a679e25d..02948b2a809b 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1371,6 +1371,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * pipeline = pipe_widget->private; pipeline->mem_usage += total; + /* + * If this is not a Data Processing module instance, add the + * required heap sizes to the sum of all module instances belonging + * to the same pipeline, and find the maximum stack requirement + * among all module instances belonging to the same pipeline. + */ + if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { + pipe_widget->heap_bytes += swidget->heap_bytes; + if (pipe_widget->stack_bytes < swidget->stack_bytes) + pipe_widget->stack_bytes = swidget->stack_bytes; + + dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", + swidget->widget->name, pipe_widget->widget->name, + pipe_widget->heap_bytes, pipe_widget->stack_bytes); + } + /* Update base_config->cpc from the module manifest */ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); @@ -1688,6 +1704,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage = 0; + pipe_widget->heap_bytes = 0; + pipe_widget->stack_bytes = 0; if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { if (pipeline->use_chain_dma) { @@ -3085,11 +3103,11 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return 0; } -static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct sof_ipc4_msg *msg, - void *ipc_data, u32 ipc_size, - void **new_data) +static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_msg *msg, + void *ipc_data, u32 ipc_size, + void **new_data) { struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; struct sof_ipc4_module_init_ext_init *ext_init; @@ -3113,13 +3131,14 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, /* Add ext_init first and set objects array flag to 1 */ ext_init = (struct sof_ipc4_module_init_ext_init *)payload; - ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); /* Add object array objects after ext_init */ /* Add memory_data if comp_domain indicates DP */ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { + ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | @@ -3132,7 +3151,6 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, dp_mem_data->heap_bytes = swidget->heap_bytes; ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); } - /* If another array object is added, remember clear previous OBJ_LAST bit */ /* Calculate final size and check that it fits to max payload size */ @@ -3156,6 +3174,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, return new_size; } +static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u32 *payload, u32 *ext_pos, + struct sof_ipc4_glb_pipe_ext_object **hdr) +{ + struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; + + *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; + (*hdr)->header = + SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | + SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), + sizeof(u32))); + *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); + mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; + mem_data->domain_id = swidget->domain_id; + mem_data->stack_bytes = swidget->stack_bytes; + mem_data->heap_bytes = swidget->heap_bytes; + *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); + + dev_dbg(sdev->dev, + "%s; domain_id %u stack %u heap %u bytes", + swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, + mem_data->heap_bytes); +} + +static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_msg *msg, + void **new_data) +{ + struct sof_ipc4_glb_pipe_payload *payload_hdr; + struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; + u32 *payload; + u32 ext_pos; + + payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ + payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; + payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; + ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); + + sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); + /* Add following array objects here */ + + /* Mark end of object array */ + hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; + + /* Put total payload size in words to the payload header */ + payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); + *new_data = payload; + + /* Update msg extension bits according to the payload changes */ + msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; + + dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, + payload_hdr->word0); + + return ext_pos * sizeof(int32_t); +} + static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; @@ -3309,8 +3390,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->widget->name, swidget->pipeline_id, module_id, swidget->instance_id, swidget->core); - ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, - &ext_data); + ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, + &ext_data); if (ret < 0) goto fail; @@ -3322,6 +3403,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, swidget->instance_id, swidget->core); + + msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; + ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, + &ext_data); + if (ret < 0) + goto fail; + + if (ret > 0) { + ipc_size = ret; + ipc_data = ext_data; + } } msg->data_size = ipc_size; @@ -3379,6 +3471,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->widget->name); pipeline->mem_usage = 0; + swidget->heap_bytes = 0; + swidget->stack_bytes = 0; pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); swidget->instance_id = -EINVAL; From e5b0daa6f9744967c04645945c3a5efbc43f3c92 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:40 +0300 Subject: [PATCH 437/791] ASoC: SOF: ipc4-topology: Fix sof_ipc4_mod_init_ext_dp_memory_data comments Fix a copy-paste error in struct sof_ipc4_mod_init_ext_dp_memory_data datamember comments. And while at it, drop the overly specific notes on the datamember values. The values are coming from topology and and what to do with them is decided in SOF FW. Its a bad idea to try to document their meaning in detail here. The Linux driver is only passing the values. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 6fbf62c4075c..b49a74007bd7 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -660,9 +660,9 @@ enum sof_ipc4_mod_init_ext_obj_id { /* DP module memory configuration data object for object array */ struct sof_ipc4_mod_init_ext_dp_memory_data { - u32 domain_id; /* userspace domain ID */ - u32 stack_bytes; /* stack size in bytes, 0 means default size */ - u32 heap_bytes; /* stack size in bytes, 0 means default size */ + u32 domain_id; /* userspace domain ID */ + u32 stack_bytes; /* required stack size in bytes */ + u32 heap_bytes; /* required heap size in bytes */ } __packed __aligned(4); /* @@ -732,7 +732,7 @@ enum sof_ipc4_glb_pipe_ext_obj_id { struct sof_ipc4_glb_pipe_ext_obj_memory_data { u32 domain_id; /* userspace domain ID */ u32 stack_bytes; /* stack size in bytes */ - u32 heap_bytes; /* heap size in bytes */ + u32 heap_bytes; /* heap size in bytes */ } __packed __aligned(4); /** @}*/ From 221f3b29366ec9f8579a8366ba438726c596ff61 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:41 +0300 Subject: [PATCH 438/791] ASoC: SOF: ipc4-topology: Refactor sof_ipc4_widget_mod_init_msg_payload() Refactor sof_ipc4_widget_mod_init_msg_payload() to be easier to extend. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 65 ++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 02948b2a809b..45f434c86cf9 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3103,27 +3103,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return 0; } +static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u32 *payload, u32 *ext_pos, + struct sof_ipc4_module_init_ext_object **hdr) +{ + /* Add memory_data if comp_domain indicates DP */ + if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { + struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; + + *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; + (*hdr)->header = + SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | + SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), + sizeof(u32))); + *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); + dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; + dp_mem_data->domain_id = swidget->domain_id; + dp_mem_data->stack_bytes = swidget->stack_bytes; + dp_mem_data->heap_bytes = swidget->heap_bytes; + *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); + } +} + static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg, void *ipc_data, u32 ipc_size, void **new_data) { - struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; struct sof_ipc4_module_init_ext_init *ext_init; - struct sof_ipc4_module_init_ext_object *hdr; + struct sof_ipc4_module_init_ext_object *hdr = NULL; int new_size; u32 *payload; u32 ext_pos; - /* For the moment the only reason for adding init_ext_init payload is DP - * memory data. If both stack and heap size are 0 (= use default), then - * there is no need for init_ext_init payload. + /* + * Only DP widgets currently add init-ext objects here. Avoid allocating + * a max-sized payload buffer for widgets that will immediately return 0. */ - if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { - msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; + if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) return 0; - } payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); if (!payload) @@ -3135,23 +3155,22 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, /* Add object array objects after ext_init */ - /* Add memory_data if comp_domain indicates DP */ - if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { - ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); - hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; - hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | - SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | - SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), - sizeof(u32))); - ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); - dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; - dp_mem_data->domain_id = swidget->domain_id; - dp_mem_data->stack_bytes = swidget->stack_bytes; - dp_mem_data->heap_bytes = swidget->heap_bytes; - ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); + /* Add following object array items here */ + + if (!hdr) { + /* + * NOTE: Remove this early bail out, when struct + * sof_ipc4_module_init_ext_init alone has some + * function. + */ + kfree(payload); + return 0; } - /* If another array object is added, remember clear previous OBJ_LAST bit */ + + ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; /* Calculate final size and check that it fits to max payload size */ new_size = ext_pos * sizeof(u32) + ipc_size; From b627da43035744ca4d691fbf56eef60268319873 Mon Sep 17 00:00:00 2001 From: Andrey Golovko Date: Mon, 27 Jul 2026 12:33:09 +0300 Subject: [PATCH 439/791] ASoC: tas2783-sdw: drop stale regcache on uninitialized re-attach When the peripheral re-attaches after the SoundWire controller was power-gated during system suspend (s2idle reaching S0i3 on AMD ACP), the amplifier has lost all of its register and DSP state. tas_update_status() handles that by re-running tas_io_init(), which writes the device's TAS2783_SW_RESET register - a vendor register write that clears the device's register file and DSP state, not a SoundWire reset, so no re-enumeration is involved - and re-downloads the firmware. Before doing any of that, it syncs back a register cache that still holds the pre-suspend values. That sync is useless, since the reset immediately wipes whatever it wrote, and it leaves the cache claiming that the amplifier is already powered up and unmuted. Subsequent read-modify-write updates - DAPM amplifier power-up, SDCA PDE transitions at stream start - then see "no change" and skip the hardware write. Playback runs without a single error while the speakers stay silent. Unbinding and rebinding the driver restores audio, since probe starts from a fresh cache. Drop the cache instead of syncing it when an uninitialized device attaches, so that later accesses see the real hardware state. Reordering the sync after tas_io_init() and marking the cache dirty is not a workable alternative here: tas_regmap has no .writeable_reg, so the cache accepts every register up to .max_register, including ones for which tas2783_sdca_mbq_size() returns 0. regmap_sdw_mbq_size() rejects those with -EINVAL, so the replay fails on the first such register and takes initialization down with it. Cached user settings fall back to hardware defaults across such a power loss, which seems clearly preferable to a silent amplifier - the device is being reset and its firmware reloaded at this point anyway. Tested on an ASUS ProArt PX13 HN7306EAC (AMD Strix Halo, ACP7.0, two TAS2783 amplifiers plus RT721 on SoundWire link 1): the speakers work after an s2idle resume with ~51 s of S0i3 residency, where previously they stayed silent despite a complete firmware re-download. Fixes: 4cc9bd8d7b32 ("ASoc: tas2783A: Add soundwire based codec driver") Reported-by: Antoine Monnet Closes: https://lore.kernel.org/all/c66ae00a-e878-4af0-a05a-272e9574eaa5@montane.tech/ Signed-off-by: Andrey Golovko Link: https://patch.msgid.link/3e2751d1fb027bed0f09c88e5e56da8f@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index db58c50e8a83..f96a53a08175 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1216,7 +1216,6 @@ static s32 tas_update_status(struct sdw_slave *slave, { struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev); struct device *dev = &slave->dev; - int ret; dev_dbg(dev, "Peripheral status = %s", status == SDW_SLAVE_UNATTACHED ? "unattached" : @@ -1232,14 +1231,23 @@ static s32 tas_update_status(struct sdw_slave *slave, if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED) return 0; - /* updated the cache data to device */ regcache_cache_only(tas_dev->regmap, false); - ret = regcache_sync(tas_dev->regmap); - if (ret) { - regcache_cache_only(tas_dev->regmap, true); - regcache_mark_dirty(tas_dev->regmap); - return ret; - } + + /* + * The device is attaching uninitialized: either this is the first + * attach, or it lost power (and with it all register and DSP state) + * while the controller was power-gated during system suspend. The + * cache still holds the pre-suspend values, and tas_io_init() below + * resets the device via TAS2783_SW_RESET anyway, so syncing it back + * is both useless and harmful: later read-modify-write updates would + * compare against stale data and skip the hardware write. + * + * Drop the cache instead, so that subsequent accesses see the real + * hardware state. Syncing after the reset is not an option either: + * the cache accepts registers for which tas2783_sdca_mbq_size() + * returns 0, and writing those back fails with -EINVAL. + */ + regcache_drop_region(tas_dev->regmap, 0, UINT_MAX); /* perform I/O transfers required for Slave initialization */ return tas_io_init(&slave->dev, slave); From 4efcd9d0f88bf0cee1fcb15d5b8a6687eabcb53f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:23 +0700 Subject: [PATCH 440/791] ASoC: codecs: sigmadsp: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/sigmadsp.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 2e08fde3989c..b7dbeb237447 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -5,6 +5,7 @@ * Copyright 2009-2014 Analog Devices Inc. */ +#include #include #include #include @@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, uint8_t *data; int ret = 0; - mutex_lock(&sigmadsp->lock); + guard(mutex)(&sigmadsp->lock); data = ucontrol->value.bytes.data; @@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, ctrl->cached = true; } - mutex_unlock(&sigmadsp->lock); - return ret; } @@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); int ret = 0; - mutex_lock(&sigmadsp->lock); + guard(mutex)(&sigmadsp->lock); if (!ctrl->cached) { ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, @@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, ctrl->num_bytes); } - mutex_unlock(&sigmadsp->lock); - return ret; } @@ -677,10 +674,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, return; changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); if (active && changed > 0) { - mutex_lock(&sigmadsp->lock); - if (ctrl->cached) - sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); - mutex_unlock(&sigmadsp->lock); + scoped_guard(mutex, &sigmadsp->lock) { + if (ctrl->cached) + sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); + } } } From 891f008aa31d02a5860d117ac747c652f1d5b4b4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:24 +0700 Subject: [PATCH 441/791] ASoC: codecs: sta350: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/sta350.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 99c7f7ac807b..2ba35076732b 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ +#include #include #include #include @@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsigned int cfud, val; - int i, ret = 0; + int i; - mutex_lock(&sta350->coeff_lock); + guard(mutex)(&sta350->coeff_lock); /* preserve reserved bits in STA350_CFUD */ regmap_read(sta350->regmap, STA350_CFUD, &cfud); @@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, regmap_write(sta350->regmap, STA350_CFUD, cfud); regmap_write(sta350->regmap, STA350_CFADDR2, index); - if (numcoef == 1) { + if (numcoef == 1) regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); - } else if (numcoef == 5) { + else if (numcoef == 5) regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); - } else { - ret = -EINVAL; - goto exit_unlock; - } + else + return -EINVAL; for (i = 0; i < 3 * numcoef; i++) { regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); ucontrol->value.bytes.data[i] = val; } -exit_unlock: - mutex_unlock(&sta350->coeff_lock); - - return ret; + return 0; } static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, From d6c5e4accf3897a5e4565da73da0387b3d2a944a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:25 +0700 Subject: [PATCH 442/791] ASoC: codecs: sta32x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 652c6e3a9e63..d6de739212f9 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsigned int cfud, val; - int i, ret = 0; + int i; - mutex_lock(&sta32x->coeff_lock); + guard(mutex)(&sta32x->coeff_lock); /* preserve reserved bits in STA32X_CFUD */ regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); @@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, regmap_write(sta32x->regmap, STA32X_CFUD, cfud); regmap_write(sta32x->regmap, STA32X_CFADDR2, index); - if (numcoef == 1) { + if (numcoef == 1) regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); - } else if (numcoef == 5) { + else if (numcoef == 5) regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); - } else { - ret = -EINVAL; - goto exit_unlock; - } + else + return -EINVAL; + for (i = 0; i < 3 * numcoef; i++) { regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); ucontrol->value.bytes.data[i] = val; } -exit_unlock: - mutex_unlock(&sta32x->coeff_lock); - - return ret; + return 0; } static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, From a41eb6d666c2e77978fd880634ad0af2a5494659 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:26 +0700 Subject: [PATCH 443/791] ASoC: codecs: tas2781: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-comlib-i2c.c | 5 ++-- sound/soc/codecs/tas2781-i2c.c | 33 +++++++++++---------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c index e24d56a14cfd..79b5f5b04c74 100644 --- a/sound/soc/codecs/tas2781-comlib-i2c.c +++ b/sound/soc/codecs/tas2781-comlib-i2c.c @@ -6,6 +6,7 @@ // // Author: Shenghao Ding +#include #include #include #include @@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, /* Codec Lock Hold to ensure that codec_probe and firmware parsing and * loading do not simultaneously execute. */ - mutex_lock(&tas_priv->codec_lock); + guard(mutex)(&tas_priv->codec_lock); if (tas_priv->name_prefix) scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", @@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", ret); - /* Codec Lock Release*/ - mutex_unlock(&tas_priv->codec_lock); return ret; } EXPORT_SYMBOL_GPL(tascodec_init); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 8eb9ead2951e..1ea0a0f9d1ec 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -13,6 +13,7 @@ // Author: Kevin Lu // +#include #include #include #include @@ -852,12 +853,12 @@ static int tasdevice_digital_gain_get( unsigned char data[4]; int ret; - mutex_lock(&tas_dev->codec_lock); + guard(mutex)(&tas_dev->codec_lock); /* Read the primary device */ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); if (ret) { dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); - goto out; + return ret; } target = get_unaligned_be32(&data[0]); @@ -877,8 +878,7 @@ static int tasdevice_digital_gain_get( /* find out the member same as or closer to the current volume */ ucontrol->value.integer.value[0] = abs(target - ar_l) <= abs(target - ar_r) ? l : r; -out: - mutex_unlock(&tas_dev->codec_lock); + return 0; } @@ -891,29 +891,26 @@ static int tasdevice_digital_gain_put( struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); int vol = ucontrol->value.integer.value[0]; - int status = 0, max = mc->max, rc = 1; + int status = 0, max = mc->max; int i, ret; unsigned int reg = mc->reg; unsigned int volrd, volwr; unsigned char data[4]; vol = clamp(vol, 0, max); - mutex_lock(&tas_dev->codec_lock); + guard(mutex)(&tas_dev->codec_lock); /* Read the primary device */ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); if (ret) { dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); - rc = -1; - goto out; + return -1; } volrd = get_unaligned_be32(&data[0]); volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); - if (volrd == volwr) { - rc = 0; - goto out; - } + if (volrd == volwr) + return 0; for (i = 0; i < tas_dev->ndev; i++) { ret = tasdevice_dev_bulk_write(tas_dev, i, reg, @@ -927,10 +924,9 @@ static int tasdevice_digital_gain_put( } if (status) - rc = -1; -out: - mutex_unlock(&tas_dev->codec_lock); - return rc; + return -1; + + return 1; } static const struct snd_kcontrol_new tasdevice_cali_controls[] = { @@ -1774,13 +1770,10 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); int state = 0; - /* Codec Lock Hold */ - mutex_lock(&tas_priv->codec_lock); + guard(mutex)(&tas_priv->codec_lock); if (event == SND_SOC_DAPM_PRE_PMD) state = 1; tasdevice_tuning_switch(tas_priv, state); - /* Codec Lock Release*/ - mutex_unlock(&tas_priv->codec_lock); return 0; } From 12a6af9195502987ef29abeb05cfa9a08f332474 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:27 +0700 Subject: [PATCH 444/791] ASoC: codecs: tas2783: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 60 ++++++++++++++++------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index db58c50e8a83..8ebed797acb5 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -12,6 +12,7 @@ // Author: Baojun Xu // Author: Kevin Lu +#include #include #include #include @@ -693,12 +694,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) return 0; } - mutex_lock(&tas_dev->calib_lock); - ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, - tas_dev->cali_data.read_sz); - if (!ret) - tas2783_set_calib_params_to_device(tas_dev, tmp_val); - mutex_unlock(&tas_dev->calib_lock); + scoped_guard(mutex, &tas_dev->calib_lock) { + ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, + tas_dev->cali_data.read_sz); + if (!ret) + tas2783_set_calib_params_to_device(tas_dev, tmp_val); + } return ret; } @@ -927,22 +928,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(tas_dev->dev, "clear latch failed, err=%d", ret); - mutex_lock(&tas_dev->pde_lock); - /* - * Sometimes, there is error returned during power on. - * So added retry logic to ensure power on so that - * port prepare succeeds - */ - do { - ret = regmap_write(tas_dev->regmap, - SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, - TAS2783_SDCA_CTL_REQ_POW_STATE, 0), - TAS2783_SDCA_POW_STATE_ON); - if (!ret) - break; - usleep_range(2000, 2200); - } while (retry--); - mutex_unlock(&tas_dev->pde_lock); + scoped_guard(mutex, &tas_dev->pde_lock) { + /* + * Sometimes, there is error returned during power on. + * So added retry logic to ensure power on so that + * port prepare succeeds + */ + do { + ret = regmap_write(tas_dev->regmap, + SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, + TAS2783_SDCA_CTL_REQ_POW_STATE, 0), + TAS2783_SDCA_POW_STATE_ON); + if (!ret) + break; + usleep_range(2000, 2200); + } while (retry--); + } + if (ret) return ret; @@ -966,7 +968,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - s32 ret; struct snd_soc_component *component = dai->component; struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component); @@ -975,14 +976,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); - mutex_lock(&tas_dev->pde_lock); - ret = regmap_write(tas_dev->regmap, - SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, - TAS2783_SDCA_CTL_REQ_POW_STATE, 0), - TAS2783_SDCA_POW_STATE_OFF); - mutex_unlock(&tas_dev->pde_lock); - - return ret; + guard(mutex)(&tas_dev->pde_lock); + return regmap_write(tas_dev->regmap, + SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, + TAS2783_SDCA_CTL_REQ_POW_STATE, 0), + TAS2783_SDCA_POW_STATE_OFF); } static const struct snd_soc_dai_ops tas_dai_ops = { From 58a8bdbfe568b342646e3f101a906cfe5209295d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:28 +0700 Subject: [PATCH 445/791] ASoC: codecs: tas5805m: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas5805m.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index bcc8cab8d667..f76e04b403b5 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -12,6 +12,7 @@ // // It has been simplified a little and reworked for the 5.x ALSA SoC API. +#include #include #include #include @@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); ucontrol->value.integer.value[0] = tas5805m->vol[0]; ucontrol->value.integer.value[1] = tas5805m->vol[1]; - mutex_unlock(&tas5805m->lock); return 0; } @@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - int ret = 0; if (!(volume_is_valid(ucontrol->value.integer.value[0]) && volume_is_valid(ucontrol->value.integer.value[1]))) return -EINVAL; - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || tas5805m->vol[1] != ucontrol->value.integer.value[1]) { tas5805m->vol[0] = ucontrol->value.integer.value[0]; @@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, tas5805m->is_powered); if (tas5805m->is_powered) tas5805m_refresh(tas5805m); - ret = 1; + return 1; } - mutex_unlock(&tas5805m->lock); - return ret; + return 0; } static const struct snd_kcontrol_new tas5805m_snd_controls[] = { @@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); /* We mustn't issue any I2C transactions until the I2S * clock is stable. Furthermore, we must allow a 5ms * delay after the first set of register writes to @@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) tas5805m->is_powered = true; tas5805m_refresh(tas5805m); - mutex_unlock(&tas5805m->lock); } static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, @@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, dev_dbg(component->dev, "DSP shutdown\n"); cancel_work_sync(&tas5805m->work); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); if (tas5805m->is_powered) { tas5805m->is_powered = false; @@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); } - mutex_unlock(&tas5805m->lock); } return 0; @@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component); - mutex_lock(&tas5805m->lock); + guard(mutex)(&tas5805m->lock); dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", mute, tas5805m->is_powered); tas5805m->is_muted = mute; if (tas5805m->is_powered) tas5805m_refresh(tas5805m); - mutex_unlock(&tas5805m->lock); return 0; } From dfee22ec48a5e3f0c55914516d2269a3d172c491 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:29 +0700 Subject: [PATCH 446/791] ASoC: codecs: tlv320dac33: Use guard() for mutex & spin locks Clean up the code using guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 87 ++++++++++++++-------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 223c49dfc450..9bd2ddd8dacd 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -7,6 +7,7 @@ * Copyright: (C) 2009 Nokia Corporation */ +#include #include #include #include @@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int unsigned int value) { struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - int ret; - mutex_lock(&dac33->mutex); - ret = dac33_write(component, reg, value); - mutex_unlock(&dac33->mutex); + guard(mutex)(&dac33->mutex); - return ret; + return dac33_write(component, reg, value); } #define DAC33_I2C_ADDR_AUTOINC 0x80 @@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); int ret = 0; - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); /* Safety check */ if (unlikely(power == dac33->chip_power)) { dev_dbg(component->dev, "Trying to set the same power state: %s\n", power ? "ON" : "OFF"); - goto exit; + return ret; } if (power) { @@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto exit; + return ret; } if (dac33->reset_gpiod) { @@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret < 0) { dev_err(&dac33->i2c->dev, "Failed to set reset GPIO: %d\n", ret); - goto exit; + return ret; } } @@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret < 0) { dev_err(&dac33->i2c->dev, "Failed to set reset GPIO: %d\n", ret); - goto exit; + return ret; } } @@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to disable supplies: %d\n", ret); - goto exit; + return ret; } dac33->chip_power = 0; } -exit: - mutex_unlock(&dac33->mutex); return ret; } @@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_component *component = dac33->component; unsigned int delay; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) DAC33_THRREG(dac33->nsample)); /* Take the timestamps */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp2 = ktime_to_us(ktime_get()); - dac33->t_stamp1 = dac33->t_stamp2; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + dac33->t_stamp2 = ktime_to_us(ktime_get()); + dac33->t_stamp1 = dac33->t_stamp2; + } dac33_write16(component, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); @@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) break; case DAC33_FIFO_MODE7: /* Take the timestamp */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp1 = ktime_to_us(ktime_get()); - /* Move back the timestamp with drain time */ - dac33->t_stamp1 -= dac33->mode7_us_to_lthr; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + dac33->t_stamp1 = ktime_to_us(ktime_get()); + /* Move back the timestamp with drain time */ + dac33->t_stamp1 -= dac33->mode7_us_to_lthr; + } dac33_write16(component, DAC33_PREFILL_MSB, DAC33_THRREG(DAC33_MODE7_MARGIN)); @@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_component *component = dac33->component; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: /* Take the timestamp */ - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp2 = ktime_to_us(ktime_get()); - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) + dac33->t_stamp2 = ktime_to_us(ktime_get()); dac33_write16(component, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample)); @@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) dac33 = container_of(work, struct tlv320dac33_priv, work); component = dac33->component; - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); switch (dac33->state) { case DAC33_PREFILL: dac33->state = DAC33_PLAYBACK; @@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) dac33_write(component, DAC33_FIFO_CTRL_A, reg); break; } - mutex_unlock(&dac33->mutex); } static irqreturn_t dac33_interrupt_handler(int irq, void *dev) { struct snd_soc_component *component = dev; struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - unsigned long flags; - spin_lock_irqsave(&dac33->lock, flags); - dac33->t_stamp1 = ktime_to_us(ktime_get()); - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) + dac33->t_stamp1 = ktime_to_us(ktime_get()); /* Do not schedule the workqueue in Mode7 */ if (dac33->fifo_mode != DAC33_FIFO_MODE7) @@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, return -EINVAL; } - mutex_lock(&dac33->mutex); + guard(mutex)(&dac33->mutex); if (!dac33->chip_power) { /* * Chip is not powered yet. * Do the init in the dac33_set_bias_level later. */ - mutex_unlock(&dac33->mutex); return 0; } @@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, break; } - mutex_unlock(&dac33->mutex); - return 0; } @@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( unsigned int time_delta, uthr; int samples_out, samples_in, samples; snd_pcm_sframes_t delay = 0; - unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_BYPASS: break; case DAC33_FIFO_MODE1: - spin_lock_irqsave(&dac33->lock, flags); - t0 = dac33->t_stamp1; - t1 = dac33->t_stamp2; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + t0 = dac33->t_stamp1; + t1 = dac33->t_stamp2; + } t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ if (!t1) - goto out; + return 0; if (t0 > t1) { /* @@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( } break; case DAC33_FIFO_MODE7: - spin_lock_irqsave(&dac33->lock, flags); - t0 = dac33->t_stamp1; - uthr = dac33->uthr; - spin_unlock_irqrestore(&dac33->lock, flags); + scoped_guard(spinlock_irqsave, &dac33->lock) { + t0 = dac33->t_stamp1; + uthr = dac33->uthr; + } t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ if (!t0) - goto out; + return 0; if (t_now <= t0) { /* * Either the timestamps are messed or equal. Report * maximum delay */ - delay = uthr; - goto out; + return uthr; } time_delta = t_now - t0; @@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( dac33->fifo_mode); break; } -out: + return delay; } From 315b15203f6de9784ce62b0ba4ee5550b49dd5bf Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:30 +0700 Subject: [PATCH 447/791] ASoC: codecs: tscs42xx: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tscs42xx.c | 61 ++++++++++++------------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index dba581857920..02082ef790b4 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) return ret; } - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); - goto exit; + return ret; } if (!plls_locked(component)) { dev_err(component->dev, "Failed to lock plls\n"); ret = -ENOMSG; - goto exit; + return ret; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - return ret; + return 0; } static int power_down_audio_plls(struct snd_soc_component *component) @@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); int ret; - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); ret = snd_soc_component_update_bits(component, R_PLLCTL1C, RM_PLLCTL1C_PDB_PLL1, RV_PLLCTL1C_PDB_PLL1_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); - goto exit; + return ret; } ret = snd_soc_component_update_bits(component, R_PLLCTL1C, RM_PLLCTL1C_PDB_PLL2, RV_PLLCTL1C_PDB_PLL2_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); - goto exit; + return ret; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - return ret; + return 0; } static int coeff_ram_get(struct snd_kcontrol *kcontrol, @@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, (struct coeff_ram_ctl *)kcontrol->private_value; struct soc_bytes_ext *params = &ctl->bytes_ext; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); memcpy(ucontrol->value.bytes.data, &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); - mutex_unlock(&tscs42xx->coeff_ram_lock); - return 0; } @@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, unsigned int coeff_cnt = params->max / COEFF_SIZE; int ret; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); tscs42xx->coeff_ram_synced = false; memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], ucontrol->value.bytes.data, params->max); - mutex_lock(&tscs42xx->pll_lock); + guard(mutex)(&tscs42xx->pll_lock); if (plls_locked(component)) { ret = write_coeff_ram(component, tscs42xx->coeff_ram, @@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, if (ret < 0) { dev_err(component->dev, "Failed to flush coeff ram cache (%d)\n", ret); - goto exit; + return ret; } tscs42xx->coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->pll_lock); - - mutex_unlock(&tscs42xx->coeff_ram_lock); - - return ret; + return 0; } /* Input L Capture Route */ @@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); int ret; - mutex_lock(&tscs42xx->coeff_ram_lock); + guard(mutex)(&tscs42xx->coeff_ram_lock); if (!tscs42xx->coeff_ram_synced) { ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, COEFF_RAM_COEFF_COUNT); if (ret < 0) - goto exit; + return ret; tscs42xx->coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs42xx->coeff_ram_lock); - - return ret; + return 0; } static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { @@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, return ret; } - mutex_lock(&tscs42xx->audio_params_lock); + guard(mutex)(&tscs42xx->audio_params_lock); tscs42xx->samplerate = rate; - mutex_unlock(&tscs42xx->audio_params_lock); - return 0; } @@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, return ret; } - mutex_lock(&tscs42xx->audio_params_lock); + guard(mutex)(&tscs42xx->audio_params_lock); tscs42xx->bclk_ratio = ratio; - mutex_unlock(&tscs42xx->audio_params_lock); - return 0; } From 70ab50fe639af0a7b7b16367f61f234583c09ec6 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:31 +0700 Subject: [PATCH 448/791] ASoC: codecs: tscs454: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tscs454.c | 107 +++++++++++++++---------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index aad394937ce6..b70c9d931e1e 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -4,6 +4,7 @@ // Author: Steven Eckhoff #include +#include #include #include #include @@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(coeff_ram_lock); - - memcpy(ucontrol->value.bytes.data, - &coeff_ram[ctl->addr * COEFF_SIZE], params->max); - - mutex_unlock(coeff_ram_lock); + scoped_guard(mutex, coeff_ram_lock) { + memcpy(ucontrol->value.bytes.data, + &coeff_ram[ctl->addr * COEFF_SIZE], params->max); + } return 0; } @@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(coeff_ram_lock); + guard(mutex)(coeff_ram_lock); *coeff_ram_synced = false; memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], ucontrol->value.bytes.data, params->max); - mutex_lock(&tscs454->pll1.lock); - mutex_lock(&tscs454->pll2.lock); + guard(mutex)(&tscs454->pll1.lock); + guard(mutex)(&tscs454->pll2.lock); val = snd_soc_component_read(component, R_PLLSTAT); if (val) { /* PLLs locked */ @@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, if (ret < 0) { dev_err(component->dev, "Failed to flush coeff ram cache (%d)\n", ret); - goto exit; + return ret; } *coeff_ram_synced = true; } - ret = 0; -exit: - mutex_unlock(&tscs454->pll2.lock); - mutex_unlock(&tscs454->pll1.lock); - mutex_unlock(coeff_ram_lock); - - return ret; + return 0; } static inline int coeff_ram_sync(struct snd_soc_component *component, @@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, { int ret; - mutex_lock(&tscs454->dac_ram.lock); - if (!tscs454->dac_ram.synced) { - ret = write_coeff_ram(component, tscs454->dac_ram.cache, - R_DACCRS, R_DACCRADD, R_DACCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->dac_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->dac_ram.lock) { + if (!tscs454->dac_ram.synced) { + ret = write_coeff_ram(component, tscs454->dac_ram.cache, + R_DACCRS, R_DACCRADD, R_DACCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->dac_ram.lock); - mutex_lock(&tscs454->spk_ram.lock); - if (!tscs454->spk_ram.synced) { - ret = write_coeff_ram(component, tscs454->spk_ram.cache, - R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->spk_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->spk_ram.lock) { + if (!tscs454->spk_ram.synced) { + ret = write_coeff_ram(component, tscs454->spk_ram.cache, + R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->spk_ram.lock); - mutex_lock(&tscs454->sub_ram.lock); - if (!tscs454->sub_ram.synced) { - ret = write_coeff_ram(component, tscs454->sub_ram.cache, - R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, - 0x00, COEFF_RAM_COEFF_COUNT); - if (ret < 0) { - mutex_unlock(&tscs454->sub_ram.lock); - return ret; + scoped_guard(mutex, &tscs454->sub_ram.lock) { + if (!tscs454->sub_ram.synced) { + ret = write_coeff_ram(component, tscs454->sub_ram.cache, + R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) + return ret; } } - mutex_unlock(&tscs454->sub_ram.lock); return 0; } @@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) static inline void reserve_pll(struct pll *pll) { - mutex_lock(&pll->lock); + guard(mutex)(&pll->lock); pll->users++; - mutex_unlock(&pll->lock); } static inline void free_pll(struct pll *pll) { - mutex_lock(&pll->lock); + guard(mutex)(&pll->lock); pll->users--; - mutex_unlock(&pll->lock); } static int pll_connected(struct snd_soc_dapm_widget *source, @@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, int users; if (strstr(source->name, "PLL 1")) { - mutex_lock(&tscs454->pll1.lock); - users = tscs454->pll1.users; - mutex_unlock(&tscs454->pll1.lock); + scoped_guard(mutex, &tscs454->pll1.lock) + users = tscs454->pll1.users; dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, users); } else { - mutex_lock(&tscs454->pll2.lock); - users = tscs454->pll2.users; - mutex_unlock(&tscs454->pll2.lock); + scoped_guard(mutex, &tscs454->pll2.lock) + users = tscs454->pll2.users; dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, users); } @@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, { struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); - mutex_lock(&tscs454->aifs_status_lock); + guard(mutex)(&tscs454->aifs_status_lock); dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); @@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, free_pll(tscs454->internal_rate.pll); } - mutex_unlock(&tscs454->aifs_status_lock); - return 0; } @@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, unsigned int val; int ret; - mutex_lock(&tscs454->aifs_status_lock); + guard(mutex)(&tscs454->aifs_status_lock); dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, aif->id, fs); @@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, ret = set_aif_fs(component, aif->id, fs); if (ret < 0) { dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); - goto exit; + return ret; } ret = set_aif_sample_format(component, params_format(params), aif->id); if (ret < 0) { dev_err(component->dev, "Failed to set aif sample format (%d)\n", ret); - goto exit; + return ret; } set_aif_status_active(&tscs454->aifs_status, aif->id, @@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", aif->id, tscs454->aifs_status.streams); - ret = 0; -exit: - mutex_unlock(&tscs454->aifs_status_lock); - - return ret; + return 0; } static int tscs454_hw_free(struct snd_pcm_substream *substream, From ff8af50301922a46ae9c90fb1f3ad1654c1c7101 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:32 +0700 Subject: [PATCH 449/791] ASoC: codecs: twl6040: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/twl6040.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index e10c51092a35..650836f9615f 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, struct twl6040_data *priv = snd_soc_component_get_drvdata(component); int status; - mutex_lock(&priv->mutex); + guard(mutex)(&priv->mutex); /* Sync status */ status = twl6040_read(component, TWL6040_REG_STATUS); @@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, snd_soc_jack_report(jack, report, report); else snd_soc_jack_report(jack, 0, report); - - mutex_unlock(&priv->mutex); } void twl6040_hs_jack_detect(struct snd_soc_component *component, From dabbe0eb4e40bbf0d10342dd80d303a4071caad7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:34 +0700 Subject: [PATCH 450/791] ASoC: codecs: wcd934x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-13-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 42 ++++++++++++++------------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index a9e6f2923099..caca5cc25100 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) { - mutex_lock(&wcd->sysclk_mutex); - - if (++wcd->sysclk_users != 1) { - mutex_unlock(&wcd->sysclk_mutex); - return 0; + scoped_guard(mutex, &wcd->sysclk_mutex) { + if (++wcd->sysclk_users != 1) + return 0; } - mutex_unlock(&wcd->sysclk_mutex); regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, WCD934X_ANA_BIAS_EN_MASK, @@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) { - mutex_lock(&wcd->sysclk_mutex); - if (--wcd->sysclk_users != 0) { - mutex_unlock(&wcd->sysclk_mutex); - return 0; + scoped_guard(mutex, &wcd->sysclk_mutex) { + if (--wcd->sysclk_users != 0) + return 0; } - mutex_unlock(&wcd->sysclk_mutex); regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, WCD934X_EXT_CLK_BUF_EN_MASK | @@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, __func__, micb_num); return -EINVAL; } - mutex_lock(&wcd934x->micb_lock); + guard(mutex)(&wcd934x->micb_lock); switch (req) { case MICB_PULLUP_ENABLE: @@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, break; } - mutex_unlock(&wcd934x->micb_lock); - return 0; } @@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd934x->micb_lock); + guard(mutex)(&wcd934x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD934X_MICB_VAL_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD934X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd934x->micb_lock); - return ret; + + return 0; } static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, From 5a66f232aa5a5f168c15ff26c0edafb0fb2b20ec Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:35 +0700 Subject: [PATCH 451/791] ASoC: codecs: wcd937x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-14-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index e0169e783ee9..0dd05604f5b8 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2,6 +2,7 @@ // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. #include +#include #include #include #include @@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, return -EINVAL; } - mutex_lock(&wcd937x->micb_lock); + guard(mutex)(&wcd937x->micb_lock); switch (req) { case MICB_PULLUP_ENABLE: wcd937x->pullup_ref[micb_index]++; @@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, } break; } - mutex_unlock(&wcd937x->micb_lock); return 0; } @@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd937x->micb_lock); + guard(mutex)(&wcd937x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD937X_MICB_VOUT_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD937X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd937x->micb_lock); - return ret; + + return 0; } static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, From 7f9f9045153c0d74ee98406e824b3b24878f69b7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:36 +0700 Subject: [PATCH 452/791] ASoC: codecs: wcd938x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-15-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index c69e18667a85..9a9ea37ecab3 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include #include #include #include @@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; switch (micb_num) { case MIC_BIAS_1: @@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd938x->micb_lock); + guard(mutex)(&wcd938x->micb_lock); /* * If requested micbias voltage is same as current micbias * voltage, then just return. Otherwise, adjust voltage as @@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD938X_MICB_VOUT_MASK); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = -EINVAL; - goto exit; - } + if (req_vout_ctl < 0) + return -EINVAL; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; if (micb_en == WCD938X_MICB_ENABLE) snd_soc_component_write_field(component, micb_reg, @@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, */ usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd938x->micb_lock); - return ret; + + return 0; } static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, From 5a230b65cebec2622f6a417671bb6df0b7e82128 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:37 +0700 Subject: [PATCH 453/791] ASoC: codecs: wcd939x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-16-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 010d12466722..26fe0b6313cd 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); unsigned int micb_reg, cur_vout_ctl, micb_en; int req_vout_ctl; - int ret = 0; switch (micb_num) { case MIC_BIAS_1: @@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, default: return -EINVAL; } - mutex_lock(&wcd939x->micb_lock); + guard(mutex)(&wcd939x->micb_lock); /* * If requested micbias voltage is same as current micbias @@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, WCD939X_MICB_VOUT_CTL); req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); - if (req_vout_ctl < 0) { - ret = req_vout_ctl; - goto exit; - } + if (req_vout_ctl < 0) + return req_vout_ctl; - if (cur_vout_ctl == req_vout_ctl) { - ret = 0; - goto exit; - } + if (cur_vout_ctl == req_vout_ctl) + return 0; dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), @@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, usleep_range(2000, 2100); } -exit: - mutex_unlock(&wcd939x->micb_lock); - return ret; + return 0; } static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, From c7e517ed25438f3cc572ca27fbcdb6d0dc0af04f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:38 +0700 Subject: [PATCH 454/791] ASoC: codecs: wm0010: Use guard() for mutex & spin locks Clean up the code using guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-17-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 2a8c61a72c17..58c0c601ee6c 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -9,6 +9,7 @@ * Scott Ling */ +#include #include #include #include @@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) static void wm0010_halt(struct snd_soc_component *component) { struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); - unsigned long flags; enum wm0010_state state; /* Fetch the wm0010 state */ - spin_lock_irqsave(&wm0010->irq_lock, flags); - state = wm0010->state; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + state = wm0010->state; switch (state) { case WM0010_POWER_OFF: @@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) break; } - spin_lock_irqsave(&wm0010->irq_lock, flags); - wm0010->state = WM0010_POWER_OFF; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + wm0010->state = WM0010_POWER_OFF; } struct wm0010_boot_xfer { @@ -190,11 +188,9 @@ struct wm0010_boot_xfer { static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) { enum wm0010_state state; - unsigned long flags; - spin_lock_irqsave(&wm0010->irq_lock, flags); - state = wm0010->state; - spin_unlock_irqrestore(&wm0010->irq_lock, flags); + scoped_guard(spinlock_irqsave, &wm0010->irq_lock) + state = wm0010->state; dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); @@ -734,9 +730,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { - mutex_lock(&wm0010->lock); - wm0010_halt(component); - mutex_unlock(&wm0010->lock); + scoped_guard(mutex, &wm0010->lock) + wm0010_halt(component); } break; case SND_SOC_BIAS_OFF: @@ -832,9 +827,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) case WM0010_OUT_OF_RESET: case WM0010_BOOTROM: case WM0010_STAGE2: - spin_lock(&wm0010->irq_lock); - complete(&wm0010->boot_completion); - spin_unlock(&wm0010->irq_lock); + scoped_guard(spinlock, &wm0010->irq_lock) + complete(&wm0010->boot_completion); return IRQ_HANDLED; default: return IRQ_NONE; From b0515499ffdd5d72884eb780dc2cc58dd2a18ba8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:39 +0700 Subject: [PATCH 455/791] ASoC: codecs: wm2000: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-18-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm2000.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 9b68ee69324b..897b0acac5f3 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); unsigned int anc_active = ucontrol->value.integer.value[0]; - int ret; if (anc_active > 1) return -EINVAL; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); wm2000->anc_active = anc_active; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); unsigned int val = ucontrol->value.integer.value[0]; - int ret; if (val > 1) return -EINVAL; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); wm2000->spk_ena = val; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - int ret; - mutex_lock(&wm2000->lock); + guard(mutex)(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - ret = wm2000_anc_set_mode(wm2000); - - mutex_unlock(&wm2000->lock); - - return ret; + return wm2000_anc_set_mode(wm2000); } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { From e396bb39a7702d69ced88c6e9f014fa1febdc009 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:40 +0700 Subject: [PATCH 456/791] ASoC: codecs: wm5102: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-19-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index b4d4137c05b4..74b775b95bfd 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -7,6 +7,7 @@ * Author: Mark Brown */ +#include #include #include #include @@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); put_unaligned_be16(arizona->dac_comp_coeff, ucontrol->value.bytes.data); - mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); - int ret = 0; - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); if (arizona->dac_comp_coeff != dac_comp_coeff) { arizona->dac_comp_coeff = dac_comp_coeff; - ret = 1; + return 1; } - mutex_unlock(&arizona->dac_comp_lock); - return ret; + return 0; } static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, @@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; - mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct arizona *arizona = dev_get_drvdata(component->dev->parent); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - int ret = 0; if (ucontrol->value.integer.value[0] > mc->max) return -EINVAL; - mutex_lock(&arizona->dac_comp_lock); + guard(mutex)(&arizona->dac_comp_lock); if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; - ret = 1; + return 1; } - mutex_unlock(&arizona->dac_comp_lock); - return ret; + return 0; } static const char * const wm5102_osr_text[] = { From a4aaef818a0875359bcb70bc45a22edee8916090 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:41 +0700 Subject: [PATCH 457/791] ASoC: codecs: wm8731: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-20-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index a2f0e2f5c407..ce87280d590d 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -10,6 +10,7 @@ * Based on wm8753.c by Liam Girdwood */ +#include #include #include #include @@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; - int ret = 0; if (deemph > 1) return -EINVAL; - mutex_lock(&wm8731->lock); + guard(mutex)(&wm8731->lock); if (wm8731->deemph != deemph) { wm8731->deemph = deemph; wm8731_set_deemph(component); - ret = 1; + return 1; } - mutex_unlock(&wm8731->lock); - return ret; + return 0; } static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); From 06b3eee8ac1ebc756858ab2a8df1488707a82a55 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:42 +0700 Subject: [PATCH 458/791] ASoC: codecs: wm8903: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-21-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8903.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 320d7737699d..156e1e24a388 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; - int ret = 0; if (deemph > 1) return -EINVAL; - mutex_lock(&wm8903->lock); + guard(mutex)(&wm8903->lock); if (wm8903->deemph != deemph) { wm8903->deemph = deemph; wm8903_set_deemph(component); - ret = 1; + return 1; } - mutex_unlock(&wm8903->lock); - return ret; + return 0; } /* ALSA can only do steps of .01dB */ From b6eda65e115f999eaffad4eb028e356cec45bab7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:43 +0700 Subject: [PATCH 459/791] ASoC: codecs: wm8958: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-22-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8958-dsp2.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 8ff0882732e7..f75a6dc9d2bb 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -7,6 +7,7 @@ * Author: Mark Brown */ +#include #include #include #include @@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->enh_eq = fw; - mutex_unlock(&wm8994->fw_lock); } } @@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->mbc_vss = fw; - mutex_unlock(&wm8994->fw_lock); } } @@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { - mutex_lock(&wm8994->fw_lock); + guard(mutex)(&wm8994->fw_lock); wm8994->mbc = fw; - mutex_unlock(&wm8994->fw_lock); } } From ee7bc9aa8d39d8790bf18f5b06d71903c9c7440f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:44 +0700 Subject: [PATCH 460/791] ASoC: codecs: wm8962: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-23-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 2db822fc1de7..8a9598161b35 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); int old = wm8962->dsp2_ena; - int ret = 0; int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & WM8962_DSP2_ENA; - mutex_lock(&wm8962->dsp2_ena_lock); + guard(mutex)(&wm8962->dsp2_ena_lock); if (ucontrol->value.integer.value[0]) wm8962->dsp2_ena |= 1 << shift; @@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, wm8962->dsp2_ena &= ~(1 << shift); if (wm8962->dsp2_ena == old) - goto out; - - ret = 1; + return 0; if (dsp2_running) { if (wm8962->dsp2_ena) @@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, wm8962_dsp2_stop(component); } -out: - mutex_unlock(&wm8962->dsp2_ena_lock); - - return ret; + return 1; } /* The VU bits for the headphones are in a different register to the mute From e2f1f4ad30db772f14b8e628878cac815c5b7ac8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:45 +0700 Subject: [PATCH 461/791] ASoC: codecs: wm8994: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-24-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1d64c7c42ed1..8bf58a6a8af7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -7,6 +7,7 @@ * Author: Mark Brown */ +#include #include #include #include @@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm8994->active_refcount++; @@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) /* If we're using jack detection go into audio mode */ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); - - mutex_unlock(&wm8994->accdet_lock); } static void active_dereference(struct snd_soc_component *component) @@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); u16 mode; - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm8994->active_refcount--; @@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) wm1811_jackdet_set_mode(component, mode); } - - mutex_unlock(&wm8994->accdet_lock); } static int clk_sys_event(struct snd_soc_dapm_widget *w, @@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) open_circuit_work.work); struct device *dev = wm8994->wm8994->dev; - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); wm1811_micd_stop(wm8994->hubs.component); @@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) snd_soc_jack_report(wm8994->micdet[0].jack, 0, wm8994->btn_mask | SND_JACK_HEADSET); - - mutex_unlock(&wm8994->accdet_lock); } static void wm8958_mic_id(void *data, u16 status) @@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) struct snd_soc_component *component = wm8994->hubs.component; struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - pm_runtime_get_sync(component->dev); + guard(pm_runtime_active)(component->dev); /* If required for an external cap force MICBIAS on */ if (control->pdata.jd_ext_cap) { @@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) snd_soc_dapm_sync(dapm); } - mutex_lock(&wm8994->accdet_lock); + guard(mutex)(&wm8994->accdet_lock); dev_dbg(component->dev, "Starting mic detection\n"); @@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, WM8958_MICD_ENA); } - - mutex_unlock(&wm8994->accdet_lock); - - pm_runtime_put(component->dev); } static irqreturn_t wm1811_jackdet_irq(int irq, void *data) @@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) mic_complete_work.work); struct snd_soc_component *component = wm8994->hubs.component; - pm_runtime_get_sync(component->dev); - - mutex_lock(&wm8994->accdet_lock); + guard(pm_runtime_active)(component->dev); + guard(mutex)(&wm8994->accdet_lock); wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); - - mutex_unlock(&wm8994->accdet_lock); - - pm_runtime_put(component->dev); } static irqreturn_t wm8958_mic_irq(int irq, void *data) From a65ce15323994240d0c1f66be107db0991a8acf7 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:46 +0700 Subject: [PATCH 462/791] ASoC: codecs: wm971x: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-25-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm9712.c | 5 ++--- sound/soc/codecs/wm9713.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 83cd42fa0c28..3105b2e0556f 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -6,6 +6,7 @@ * Author: Liam Girdwood */ +#include #include #include #include @@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, shift = mc->shift & 0xff; mask = 1 << shift; - mutex_lock(&wm9712->lock); + guard(mutex)(&wm9712->lock); old = wm9712->hp_mixer[mixer]; if (ucontrol->value.integer.value[0]) wm9712->hp_mixer[mixer] |= mask; @@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, &update); } - mutex_unlock(&wm9712->lock); - return change; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index b3bbecf074ee..3ba7ca3c1770 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -11,6 +11,7 @@ * o Support for DAPM */ +#include #include #include #include @@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, shift = mc->shift & 0xff; mask = (1 << shift); - mutex_lock(&wm9713->lock); + guard(mutex)(&wm9713->lock); old = wm9713->hp_mixer[mixer]; if (ucontrol->value.integer.value[0]) wm9713->hp_mixer[mixer] |= mask; @@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, &update); } - mutex_unlock(&wm9713->lock); - return change; } From 46f350091576e8869e6345abb15135084df5e7fe Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:47 +0700 Subject: [PATCH 463/791] ASoC: codecs: wm_adsp: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Richard Fitzgerald Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-26-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 77 ++++++++++++-------------------------- 1 file changed, 23 insertions(+), 54 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index baa75e7ff53b..90c24c4b318e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); - int ret = 1; if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) return 0; @@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) return -EINVAL; - mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); + guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) - ret = -EBUSY; + return -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; - mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); - - return ret; + return 1; } EXPORT_SYMBOL_GPL(wm_adsp_fw_put); @@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, if (val == 0) return 0; /* 0 means no event */ - mutex_lock(&cs_ctl->dsp->pwr_lock); + guard(mutex)(&cs_ctl->dsp->pwr_lock); if (cs_ctl->enabled) ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); else ret = -EPERM; - mutex_unlock(&cs_ctl->dsp->pwr_lock); - if (ret < 0) return ret; @@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; int ret = 0; - mutex_lock(&cs_ctl->dsp->pwr_lock); + guard(mutex)(&cs_ctl->dsp->pwr_lock); ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) ret = -EFAULT; - mutex_unlock(&cs_ctl->dsp->pwr_lock); - return ret; } @@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, struct cs_dsp_coeff_ctl *cs_ctl; int ret; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); - mutex_unlock(&dsp->cs_dsp.pwr_lock); if (ret < 0) return ret; @@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { - int ret; + guard(mutex)(&dsp->cs_dsp.pwr_lock); - mutex_lock(&dsp->cs_dsp.pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), 0, buf, len); - mutex_unlock(&dsp->cs_dsp.pwr_lock); - - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); @@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr, *tmp; struct snd_soc_pcm_runtime *rtd = stream->private_data; - int ret = 0; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); if (wm_adsp_fw[dsp->fw].num_caps == 0) { adsp_err(dsp, "%s: Firmware does not support compressed API\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -ENXIO; - goto out; + return -ENXIO; } if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { adsp_err(dsp, "%s: Firmware does not support stream direction\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -EINVAL; - goto out; + return -EINVAL; } list_for_each_entry(tmp, &dsp->compr_list, list) { if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { adsp_err(dsp, "%s: Only a single stream supported per dai\n", snd_soc_rtd_to_codec(rtd, 0)->name); - ret = -EBUSY; - goto out; + return -EBUSY; } } compr = kzalloc_obj(*compr); - if (!compr) { - ret = -ENOMEM; - goto out; - } + if (!compr) + return -ENOMEM; compr->dsp = dsp; compr->stream = stream; @@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) stream->runtime->private_data = compr; -out: - mutex_unlock(&dsp->cs_dsp.pwr_lock); - - return ret; + return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_open); @@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, struct wm_adsp_compr *compr = stream->runtime->private_data; struct wm_adsp *dsp = compr->dsp; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); wm_adsp_compr_detach(compr); list_del(&compr->list); @@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, kfree(compr->raw_buf); kfree(compr); - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_free); @@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, compr_dbg(compr, "Trigger: %d\n", cmd); - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, break; } - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); @@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, compr_dbg(compr, "Pointer request\n"); - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); buf = compr->buf; if (dsp->fatal_error || !buf || buf->error) { snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); - ret = -EIO; - goto out; + return -EIO; } if (buf->avail < wm_adsp_compr_frag_words(compr)) { ret = wm_adsp_buffer_update_avail(buf); if (ret < 0) { compr_err(compr, "Error reading avail: %d\n", ret); - goto out; + return ret; } /* @@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, if (buf->error) snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); - goto out; + return ret; } ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", ret); - goto out; + return ret; } } } @@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; tstamp->sampling_rate = compr->sample_rate; -out: - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); @@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, struct wm_adsp *dsp = compr->dsp; int ret; - mutex_lock(&dsp->cs_dsp.pwr_lock); + guard(mutex)(&dsp->cs_dsp.pwr_lock); if (stream->direction == SND_COMPRESS_CAPTURE) ret = wm_adsp_compr_read(compr, buf, count); else ret = -ENOTSUPP; - mutex_unlock(&dsp->cs_dsp.pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); From 3ca9593964f7e4bb8e21efc536e76722ff56e5bd Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 10:06:48 +0700 Subject: [PATCH 464/791] ASoC: codecs: wsa88xx: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260731030648.8706-27-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 11 +++++------ sound/soc/codecs/wsa884x.c | 10 ++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 24a5904d8e6c..442cf9f3eaac 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - mutex_lock(&wsa883x->sp_lock); - wsa883x->pa_on = true; - mutex_unlock(&wsa883x->sp_lock); + scoped_guard(mutex, &wsa883x->sp_lock) + wsa883x->pa_on = true; switch (wsa883x->dev_mode) { case RECEIVER: @@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, WSA883X_GLOBAL_PA_EN_MASK, 0); snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, WSA883X_PDM_EN_MASK, 0); - mutex_lock(&wsa883x->sp_lock); - wsa883x->pa_on = false; - mutex_unlock(&wsa883x->sp_lock); + scoped_guard(mutex, &wsa883x->sp_lock) + wsa883x->pa_on = false; break; } return 0; diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 567861dd42ad..a367c94bfb4d 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - mutex_lock(&wsa884x->sp_lock); - wsa884x->pa_on = true; - mutex_unlock(&wsa884x->sp_lock); + scoped_guard(mutex, &wsa884x->sp_lock) + wsa884x->pa_on = true; wsa884x_spkr_post_pmu(component, wsa884x); @@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, 0x0); - mutex_lock(&wsa884x->sp_lock); - wsa884x->pa_on = false; - mutex_unlock(&wsa884x->sp_lock); + scoped_guard(mutex, &wsa884x->sp_lock) + wsa884x->pa_on = false; break; } From dc15652b92a8772a6d6a07b1d18ea88cf0b826d3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:22:41 +0300 Subject: [PATCH 465/791] ASoC: SOF: Intel: hda-stream: clear hstream->running flag in hw_params During hw_params call we make sure that the host DMA is stopped but the hstream->running flag is not explicitly cleared at the same time. If the host DMA fails to stop during previous use then the flag is left set and on next start the host DMA will be left disabled since the trigger:STOP will skip the DMA enable. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260730122241.30541-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 0778002e2bd6..c95230487a1b 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -613,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return ret; } + /* Host DMA is not running */ + hstream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, From f6970d8535a95c137f05e9ca825072de3b217b88 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 30 Jul 2026 14:06:02 +0100 Subject: [PATCH 466/791] ASoC: SDCA: Add missing stub for sdca_fdl_free_state() There should be a stub for sdca_fdl_free_state() for the case FDL support isn't built into the kernel. Add the missing stub. Fixes: 0880082c27b6 ("ASoC: SDCA: Remove devm from primary IRQ cleanup") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607291304.FE3mOcJF-lkp@intel.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260730130602.3747053-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_fdl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index fbaf4b384c8a..979559e9ee63 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -81,6 +81,10 @@ static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) return 0; } +static inline void sdca_fdl_free_state(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_fdl_process(struct sdca_interrupt *interrupt) { return 0; From 8338deeb8fc28925257a9a3bfad1dc78c8d74cef Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 31 Jul 2026 16:18:26 +0100 Subject: [PATCH 467/791] ASoC: es9356: Remove unused headers es9356 doesn't use any SDCA function/regmap features, remove the redundant included headers. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260731151826.961912-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index a9863900bf70..80db0f2f4aef 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include #include #include From 7b51d4c4256cd5f8922f7ad45e79b5fff89c0995 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:28:56 +0300 Subject: [PATCH 468/791] ASoC: SOF: Intel: hda: Power down DSP if it is left enabled in pre_fw_run() It is expected that the DSP is in power down state when the firmware boot is attempted. If the DSP for any reason was left powered up then the DSP boot will fail since the ROM boot sequence might not be able to run. Make sure that the DSP is off before proceeding to boot it up. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730122857.5294-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index dc85903b8d46..007a8e4a2f3d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } /* pre fw run operations */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + int ret; + + /* Power down DSP if left enabled to ensure a clean boot state. */ + if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { + dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); + + ret = chip->power_down_dsp(sdev); + if (ret < 0) + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + } + /* disable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, false); } From 31fd7a0bbfa4b7e8aff11f278e817664c6a5f68b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:28:57 +0300 Subject: [PATCH 469/791] ASoC: SOF: Intel: mtl: Power down DSP if it is left enabled in pre_fw_run() It is expected that the DSP is in power down state when the firmware boot is attempted. If the DSP for any reason was left powered up then the DSP boot will fail since the ROM boot sequence might not be able to run. Make sure that the DSP is off before proceeding to boot it up. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730122857.5294-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/mtl.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 9503d00e6002..3d67d6777f1b 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) } EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); +static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) +{ + int val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); + if (val & MTL_HFDSSCS_CPA_MASK) + return true; + + return false; +} + /* pre fw run operations */ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { @@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) u32 dsppwrsts; const struct sof_intel_dsp_desc *chip; + /* Power down the DSP if it is left enabled to ensure clean boot state */ + if (mtl_dsp_is_enabled(sdev)) { + dev_dbg(sdev->dev, "powering down DSP first\n"); + + ret = mtl_power_down_dsp(sdev); + if (ret < 0) { + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + /* Continue anyway to attempt recovery */ + } + } + chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { dsppwrctl = PTL_HFPWRCTL2; From c1c90903a25d2f0d5d8fe689fc6371e13c8f796e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:27 +0300 Subject: [PATCH 470/791] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip() Move the hda_bus_ml_init() call from hda_init_caps() into hda_dsp_ctrl_init_chip(), right after the HDA controller reset has been de-asserted and unsolicited responses have been accepted. hda_dsp_ctrl_init_chip() already calls hda_bus_ml_reset_losidv() at the end of its sequence to clear the stream-to-link mapping. On first boot this call was a no-op because the multi-link list had not yet been populated: hda_bus_ml_init() only runs later in hda_init_caps(). Enumerating the links inside init_chip() makes the LOSIDV reset effective on first boot as well, without adding a second reset call from the probe path. hda_bus_ml_init() now returns early when the hlink_list is already populated, so the subsequent invocations from the D3 resume path (hda_resume() -> hda_dsp_ctrl_init_chip(false)) are no-ops. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-ctrl.c | 8 ++++++++ sound/soc/sof/intel/hda-mlink.c | 4 ++++ sound/soc/sof/intel/hda.c | 2 -- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 8332d4bda558..a9ead78d3fcb 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -223,6 +223,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) /* Accept unsolicited responses */ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); + /* Perform a one-time enumeration of the Multi-Link capability */ + ret = hda_bus_ml_init(bus); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", + __func__); + goto err; + } + if (detect_codec) hda_codec_detect_mask(sdev); diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 92314e3b568a..ca8551befdb5 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) if (!bus->mlcap) return 0; + /* Enumeration is a one time operation, skip if already done */ + if (!list_empty(&bus->hlink_list)) + return 0; + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 007a8e4a2f3d..8602ae3892a2 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -639,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; } - hda_bus_ml_init(bus); - /* Skip SoundWire if it is not supported */ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) goto skip_soundwire; From 9e6966f7040451603d4c93341d0205421046d902 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:28 +0300 Subject: [PATCH 471/791] ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+ Drop the hda_bus_ml_put_all() call at the end of hda_init_caps(). On multi-link (mlink) capable platforms the non-alternate links (HDaudio and iDisp) are powered on by hardware when CRST# is de-asserted (LCTL.SPA = 1) and their ref_count is pre-charged to 1 in hda_ml_alloc_h2link() to match this state. The put_all call immediately dropped that reference and toggled LCTL.SPA back to 0, relying on the first stream open to power the link up again. On ACE2+ platforms this redundant SPA 1->0->1 toggle at probe leaves the Processing Pipe Capability (PPLC) Linear Link Position counters in a state where they do not advance on the first stream after boot. The counters only start working after the first full runtime suspend/resume cycle, which includes a CRST# assert/deassert that fully resets the PPC AON block. Keep the non-alt links powered from CRST# de-assert through first use. System suspend still powers them down via hda_bus_ml_suspend(), and resume relies on CRST# de-assert to bring them back up, so no other path is affected. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 8602ae3892a2..4dbba9186b29 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -682,8 +682,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (!HDA_IDISP_CODEC(bus->codec_mask)) hda_codec_i915_display_power(sdev, false); - hda_bus_ml_put_all(bus); - return 0; } From 15488685319379212084c0513661c7dfbc239636 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:29 +0300 Subject: [PATCH 472/791] ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all() The helper became unused after probe no longer drops all non-alt links, so remove the dead API and implementation. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 2 -- sound/soc/sof/intel/hda-mlink.c | 13 ------------- 2 files changed, 15 deletions(-) diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index fed69998c93f..d9789b048c61 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -49,7 +49,6 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, int channel_mask, int stream_id, int dir); -void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); @@ -169,7 +168,6 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } -static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index ca8551befdb5..e0107cbd06e6 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -884,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); -void hda_bus_ml_put_all(struct hdac_bus *bus) -{ - struct hdac_ext_link *hlink; - - list_for_each_entry(hlink, &bus->hlink_list, list) { - struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); - - if (!h2link->alt) - snd_hdac_ext_bus_link_put(bus, hlink); - } -} -EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); - void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { struct hdac_ext_link *hlink; From 34d466aaa0d533f082b35629f8fd91cb8c260296 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:30 +0300 Subject: [PATCH 473/791] ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards On ACE2+ platforms the link DMA stream allocator must avoid two hardware errata in mlink-capable systems: - Concurrent (cross-direction) hazard: when SoundWire shares a physical link DMA stream index with HDaudio, iDisp or UAOL across the two directions, the LLP and timestamp values for the affected stream are wrong. SSP and DMIC are not affected because every DMA request from those links carries one sample block. - Sequential (playback only) hazard: once a HDaudio or iDisp link has used a playback stream index, that index cannot drive any non HDA/iDisp link in the same direction until the next controller reset (CRST#). Track the active link type per direction in two masks (one for SoundWire, one for HDA/iDisp/UAOL) and the persistent set of playback stream indices touched by HDA/iDisp in a third mask. The link DMA allocator skips streams that would violate either rule. Streams are released from the active masks when the stream is released; all masks are cleared in hda_dsp_ctrl_init_chip() because the CRST# performed there clears the hardware state as well. A new helper hda_bus_ml_link_get_type() returns the link type from the existing extended link descriptor so the SOF allocator can tell SoundWire, HDA/iDisp and UAOL apart without duplicating the parsing. The implementation is generic. On platforms older than ACE2 every link is reported as HDA, only the sequential mask is ever set and it has no effect because no other link types are present, so behavior is unchanged. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 21 +++++++++ sound/soc/sof/intel/hda-ctrl.c | 11 +++++ sound/soc/sof/intel/hda-dai-ops.c | 76 ++++++++++++++++++++++++++++--- sound/soc/sof/intel/hda-dai.c | 2 +- sound/soc/sof/intel/hda-mlink.c | 18 ++++++++ sound/soc/sof/intel/hda.h | 26 ++++++++++- 6 files changed, 146 insertions(+), 8 deletions(-) diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index d9789b048c61..ba35f03576b9 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -9,6 +9,22 @@ struct hdac_bus; struct hdac_ext_link; +/** + * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA + * allocator constraints (see struct sof_intel_hda_dev). + * + * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp + * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire + * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload + * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC + */ +enum hda_bus_ml_link_type { + HDA_BUS_ML_LINK_HDA, + HDA_BUS_ML_LINK_SDW, + HDA_BUS_ML_LINK_UAOL, + HDA_BUS_ML_LINK_OTHER, +}; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) int hda_bus_ml_init(struct hdac_bus *bus); @@ -53,6 +69,8 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); @@ -172,6 +190,9 @@ static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } +static inline enum hda_bus_ml_link_type +hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } + static inline struct hdac_ext_link * hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index a9ead78d3fcb..aeb34310eebd 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) { struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct hdac_stream *stream; int sd_offset, ret = 0; u32 gctl; @@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) if (bus->chip_init) return 0; + /* + * The controller reset clears the ACE2+ link DMA stream allocation + * constraints; reset the masks to reflect this. + */ + memset(sof_hda->link_dma_active_sdw_mask, 0, + sizeof(sof_hda->link_dma_active_sdw_mask)); + memset(sof_hda->link_dma_active_multi_mask, 0, + sizeof(sof_hda->link_dma_active_multi_mask)); + sof_hda->link_dma_out_hda_used_mask = 0; + hda_codec_set_codec_wakeup(sdev, true); hda_dsp_ctrl_misc_clock_gating(sdev, false); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index b2c559559962..f0be42048db3 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -20,7 +20,7 @@ /* These ops are only applicable for the HDA DAI's in their current form */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) /* - * This function checks if the host dma channel corresponding + * This function checks if the host DMA stream corresponding * to the link DMA stream_tag argument is assigned to one * of the FEs connected to the BE DAI. */ @@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, } static struct hdac_ext_stream * -hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) +hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, + enum hda_bus_ml_link_type link_type) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct sof_intel_hda_stream *hda_stream; const struct sof_intel_dsp_desc *chip; struct snd_sof_dev *sdev; struct hdac_ext_stream *res = NULL; struct hdac_stream *hstream = NULL; - int stream_dir = substream->stream; + bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; + bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; + bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; + bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; + u32 concur_block_mask = 0; + u32 seq_block_mask = 0; + unsigned int stream_idx; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); return NULL; } + /* + * On ACE2+ the link DMA stream allocator must avoid two HW errata, + * see the comment on struct sof_intel_hda_dev. + * + * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp + * and UAOL on the same physical stream index; SSP and DMIC are safe. + * - Sequential playback: a stream index previously used by an HDA/iDisp + * link cannot drive any non-HDA/iDisp link in the same direction + * until the next controller reset. + * + * The masks are protected by bus->reg_lock; sample them inside the + * lock together with the stream walk to keep the decision atomic + * with concurrent allocations and releases. + */ guard(spinlock_irq)(&bus->reg_lock); + + if (is_sdw) + concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; + else if (is_multi) + concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; + if (is_play && !is_hda) + seq_block_mask = sof_hda->link_dma_out_hda_used_mask; + list_for_each_entry(hstream, &bus->stream_list, list) { struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream sdev = hda_stream->sdev; chip = get_chip_info(sdev->pdata); + stream_idx = hstream->stream_tag - 1; + + /* skip streams blocked by the ACE2+ allocator constraints */ + if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) + continue; + /* check if link is available */ if (!hext_stream->link_locked) { /* @@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream /* * This must be a hostless stream. - * So reserve the host DMA channel. + * So reserve the host DMA stream. */ hda_stream->host_reserved = 1; break; @@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream res->link_locked = 1; res->link_substream = substream; + + stream_idx = res->hstream.stream_tag - 1; + if (is_sdw) + sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); + else if (is_multi) + sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); + + /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ + if (is_hda && is_play) + sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); } return res; @@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink) { struct hdac_ext_stream *hext_stream; + enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); if (!hext_stream) return NULL; @@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); + struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; + struct hdac_bus *bus = sof_to_bus(sdev); + int dir = substream->stream; + unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; + + /* + * Drop the stream index from the per-direction active concurrency masks. + * The two masks are mutually exclusive for a given stream/direction + * (and a stream of the SSP/DMIC kind appears in neither), so a blind + * clear of both is safe and lets us avoid having to remember the + * link type at allocation time. + */ + scoped_guard(spinlock_irq, &bus->reg_lock) { + sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); + sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); + } snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 15faedeec16d..bb44d4f8a4da 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) { if (ops->assign_hext_stream) - hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); + hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); } if (!hext_stream) diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index e0107cbd06e6..6f02fb5b70ce 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -894,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) +{ + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt) + return HDA_BUS_ML_LINK_HDA; + + switch (h2link->elid) { + case AZX_REG_ML_LEPTR_ID_SDW: + return HDA_BUS_ML_LINK_SDW; + case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: + return HDA_BUS_ML_LINK_UAOL; + default: + return HDA_BUS_ML_LINK_OTHER; + } +} +EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); + int hda_bus_ml_resume(struct hdac_bus *bus) { struct hdac_ext_link *hlink; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 799e49539b4a..1609589929a1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -523,6 +523,29 @@ struct sof_intel_hda_dev { /* the maximum number of streams (playback + capture) supported */ u32 stream_max; + /* + * ACE2+ link DMA stream allocation constraints (stream index = + * stream_tag - 1, shared between input and output directions). All + * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset + * (CRST#). + * + * - Concurrent (cross-direction) constraint: a SoundWire stream and + * a HDA/iDisp/UAOL stream cannot share a physical stream index + * across directions, the resulting LLP/timestamp values are wrong. + * link_dma_active_sdw_mask and link_dma_active_multi_mask + * (indexed by SNDRV_PCM_STREAM_*) track currently allocated + * streams per direction in each of the conflicting groups; SSP + * and DMIC do not participate. Bits are cleared on stream release. + * + * - Sequential (playback only) constraint: once a HDA/iDisp link + * has used a playback stream index, that index cannot drive a + * non-HDA/iDisp link in the same direction until the next CRST#. + * link_dma_out_hda_used_mask records this. + */ + u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_out_hda_used_mask; + /* PM related */ bool l1_disabled;/* is DMI link L1 disabled? */ @@ -1031,7 +1054,8 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream); struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream); + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink); void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream); void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, From cde33309169224df5a8a5200cb9dec8399b366e1 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 30 Jul 2026 15:56:00 +0300 Subject: [PATCH 474/791] ASoC: dapm: Add encoder and decoder widget types to kcontrol handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the issue where encoder or decoder widget types are assigned kcontrols in a topology but get ignored. The controls were parsed successfully but were not registered as ALSA kcontrols. In dapm_create_or_share_kcontrol() the snd_soc_dapm_encoder and the snd_soc_dapm_decoder are added to the switch statement to be handled similarly as e.g. the snd_soc_dapm_effect for assigning a proper long control name. In dapm_widget_show_component() the snd_soc_dapm_encoder and the snd_soc_dapm_decoder are added to switch statement to let them to be shown in the debugfs power state output. In snd_soc_dapm_new_widgets() the snd_soc_dapm_encoder and the snd_soc_dapm_decoder are added to same switch case handling as e.g. snd_soc_dapm_effect to be registered with dapm_new_pga(). The previous operation with default in the switch statement silently ignored them. Note: Despite the function name, the dapm_new_pga() is generic utility that calls dapm_create_or_share_kcontrol() for each kcontrol of the widget. Signed-off-by: Seppo Ingalsuo Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730125600.6491-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4ad126bd4f70..c98e917cc911 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: @@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: + case snd_soc_dapm_encoder: + case snd_soc_dapm_decoder: dapm_new_pga(w); break; case snd_soc_dapm_dai_link: From ea1224da5d61ea9a6c18d6b79562a1354dccf395 Mon Sep 17 00:00:00 2001 From: Yu-Hsuan Hsu Date: Thu, 30 Jul 2026 16:04:45 +0300 Subject: [PATCH 475/791] ASoC: SOF: Use high-priority workqueue for PCM period elapsed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_sof_pcm_period_elapsed function currently schedules work on the system-wide workqueue. This can lead to potential delays or jitter in audio processing if the system workqueue is busy with other tasks. To improve real-time performance and ensure timely processing of PCM periods, we can use the system_highpri_wq instead of the default work queue. In performance testing, this change significantly reduced the observed scheduling delays. For instance, under load(stressapptest -M 15000 -m 60), the maximum delay dropped from 9ms on the system workqueue to 5ms on the dedicated high-priority workqueue. Suggested-by: Kai Vehmanen Signed-off-by: Yu-Hsuan Hsu Reviewed-by: Péter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730130445.8277-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index b2071edeaea6..f748d072109a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) * To avoid sending IPC before the previous IPC is handled, we * schedule delayed work here to call the snd_pcm_period_elapsed(). */ - schedule_work(&spcm->stream[substream->stream].period_elapsed_work); + queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); From cdba62ff7413330451f0f65345166431a5d6e187 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 Jul 2026 19:47:04 +0530 Subject: [PATCH 476/791] ASoC: dt-bindings: qcom: add LPASS LPR vote clock ID Add a new clock ID, LPASS_HW_LPR_VOTE, to represent the LPASS low-power resource (LPR) vote through the PRM interface. The LPASS PRM supports a resource voting mechanism to control low-power states via PARAM_ID_RSC_CPU_LPR. Exposing this as a q6prm clock ID allows clients to request the LPR vote using the existing qcom,q6prm clock provider interface. This functionality is required on newer platforms (e.g. Hawi) where LPASS clients need to explicitly manage LPR resource voting via PRM. Acked-by: Krzysztof Kozlowski Signed-off-by: Prasad Kumpatla Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260724141708.2212057-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 7b553a73bc92..8e04106d48be 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -236,6 +236,7 @@ #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 +#define LPASS_HW_LPR_VOTE 104 #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 From 037826ea448d1ae76113288a7b80bf826a188fef Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 Jul 2026 19:47:05 +0530 Subject: [PATCH 477/791] ASoC: qcom: qdsp6: Increase Q6DSP_MAX_CLK_ID for LPASS LPR vote clock Q6DSP_MAX_CLK_ID defines the upper bound of supported clock identifiers in the qdsp6 LPASS clock driver. Increase the maximum clock ID value to accommodate the LPASS LPR vote clock identifier. Signed-off-by: Prasad Kumpatla Acked-by: Bartosz Golaszewski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724141708.2212057-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c index 03838582aead..ab7d20580638 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c @@ -12,7 +12,7 @@ #include #include "q6dsp-lpass-clocks.h" -#define Q6DSP_MAX_CLK_ID 104 +#define Q6DSP_MAX_CLK_ID 105 #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 From 42cc644e071c376410ba0c6147e94aa1ad415ad9 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 Jul 2026 19:47:06 +0530 Subject: [PATCH 478/791] ASoC: qcom: q6prm: add support for LPASS LPR resource voting Add support for issuing LPASS low-power resource (LPR) votes through the PRM interface. Some platforms (e.g. Hawi) require the LPASS to be kept active via LPR resource voting instead of the existing hardware core vote mechanism. Handle this by introducing support for PARAM_ID_RSC_CPU_LPR when the LPR vote clock ID is requested. For LPR requests, use the appropriate parameter ID and payload format to disable CPU subsystem sleep, ensuring that the LPASS register space remains accessible. Also add the corresponding clock mapping for LPASS_HW_LPR_VOTE. Reviewed-by: Srinivas Kandagatla Signed-off-by: Prasad Kumpatla Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260724141708.2212057-4-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6prm-clocks.c | 2 ++ sound/soc/qcom/qdsp6/q6prm.c | 16 +++++++++++++--- sound/soc/qcom/qdsp6/q6prm.h | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 4c574b48ab00..2b2b3872ed1c 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -63,6 +63,8 @@ static const struct q6dsp_clk_init q6prm_clks[] = { "LPASS_HW_MACRO"), Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, "LPASS_HW_DCODEC"), + Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, + "LPASS_HW_LPR_VOTE"), }; static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 04892fb4423f..1f3ce4cc0837 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -31,10 +31,16 @@ struct q6prm { #define PARAM_ID_RSC_HW_CORE 0x08001032 #define PARAM_ID_RSC_LPASS_CORE 0x0800102B #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C +#define PARAM_ID_RSC_CPU_LPR 0x08001A6E + +#define LPR_CPU_SS_SLEEP_DISABLE 0x1 struct prm_cmd_request_hw_core { struct apm_module_param_data param_data; - uint32_t hw_clk_id; + union { + u32 hw_clk_id; + u32 lpr_state; + }; } __packed; struct prm_cmd_request_rsc { @@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool struct prm_cmd_request_hw_core *req; gpr_device_t *gdev = prm->gdev; uint32_t opcode, rsp_opcode; + bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); if (enable) { opcode = PRM_CMD_REQUEST_HW_RSC; @@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool param_data->module_instance_id = GPR_PRM_MODULE_IID; param_data->error_code = 0; - param_data->param_id = PARAM_ID_RSC_HW_CORE; + param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; - req->hw_clk_id = hw_block_id; + if (lpr_req) + req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; + else + req->hw_clk_id = hw_block_id; return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); } diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index bc5b9fa13283..30dfcb8879ff 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -90,6 +90,7 @@ #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 #define Q6PRM_HW_CORE_ID_LPASS 1 #define Q6PRM_HW_CORE_ID_DCODEC 2 +#define Q6PRM_HW_LPR_VOTE 3 int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, unsigned int freq); From 9151afc31402d092c5d24a67ccea36489eaf0185 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 15 Jul 2026 17:22:19 +0530 Subject: [PATCH 479/791] ASoC: dt-bindings: qcom,wsa8855: add Qualcomm WSA8855 speaker amplifier Add bindings for the Qualcomm WSA8855 stereo smart speaker amplifier. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260715115220.3093799-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,wsa8855.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml new file mode 100644 index 000000000000..00a6a05e9443 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8855.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wsa8855.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WSA8855 stereo smart speaker amplifier + +maintainers: + - Srinivas Kandagatla + - Prasad Kumpatla + +description: + WSA885X is a Qualcomm Aqstic stereo smart speaker amplifier. It uses a PCM + audio interface, an I2C control interface, and a Class-H amplifier path for + high efficiency, low output noise, and low idle power consumption. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: qcom,wsa8855 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + powerdown-gpios: + description: GPIO controlling the SD_N powerdown pin. + maxItems: 1 + + reset-gpios: + description: GPIO controlling the SD_N powerdown pin. + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-1p8-supply: true + + vdd-io-supply: true + + qcom,battery-config: + description: + Battery topology connected to the speaker amplifier. 1S indicates one + battery cell in series, while 2S indicates two battery cells in series. + $ref: /schemas/types.yaml#/definitions/string + default: 1s + enum: + - 1s + - 2s + +required: + - compatible + - reg + - '#sound-dai-cells' + - interrupts + - vdd-1p8-supply + - vdd-io-supply + +oneOf: + - required: + - powerdown-gpios + - required: + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + speaker@c { + compatible = "qcom,wsa8855"; + reg = <0x0c>; + #sound-dai-cells = <0>; + powerdown-gpios = <&tlmm 11 GPIO_ACTIVE_LOW>; + interrupt-parent = <&tlmm>; + interrupts = <77 IRQ_TYPE_EDGE_FALLING>; + vdd-1p8-supply = <&vreg_l2g_1p8>; + vdd-io-supply = <&vreg_l1g_1p2>; + qcom,battery-config = "1s"; + }; + }; +... From 1d0368c9873cddf08966bdf9642a72c7af89a7d3 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 15 Jul 2026 17:22:20 +0530 Subject: [PATCH 480/791] ASoC: codecs: add Qualcomm WSA885X codec driver Add an ASoC codec driver for the Qualcomm WSA885X stereo smart speaker amplifier. The driver programs the register map, handles reset and interrupt support, exposes DAI operations for PCM/TDM playback, and provides mixer controls for usage mode, speaker volume and RX slot mask. Keep stream-time power-state sequencing in the DAI callbacks and use regmap for the control path. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260715115220.3093799-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 11 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/wsa885x.c | 1533 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1546 insertions(+) create mode 100644 sound/soc/codecs/wsa885x.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 76e90144ea91..06d5048fd3d0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -367,6 +367,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WSA881X imply SND_SOC_WSA883X imply SND_SOC_WSA884X + imply SND_SOC_WSA885X imply SND_SOC_ZL38060 help Normally ASoC codec drivers are only built if a machine driver which @@ -2770,6 +2771,16 @@ config SND_SOC_WSA884X This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D Smart Speaker Amplifier. +config SND_SOC_WSA885X + tristate "WSA885X Codec" + depends on I2C + select REGMAP_I2C + help + This enables support for Qualcomm WSA885X Stereo Smart Speaker + Amplifier. The codec driver programs the amplifier register + map and exposes the DAI and mixer controls used by Qualcomm + audio machine drivers. + config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aa0396e5b575..7a37f44724a6 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -426,6 +426,7 @@ snd-soc-wm-hubs-y := wm_hubs.o snd-soc-wsa881x-y := wsa881x.o snd-soc-wsa883x-y := wsa883x.o snd-soc-wsa884x-y := wsa884x.o +snd-soc-wsa885x-y := wsa885x.o snd-soc-zl38060-y := zl38060.o # Amp snd-soc-max9877-y := max9877.o @@ -876,6 +877,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o +obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o # Amp diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c new file mode 100644 index 000000000000..1faa8541a872 --- /dev/null +++ b/sound/soc/codecs/wsa885x.c @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* WSA885X codec driver */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control Registers - Audio Processing */ +#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 +#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 +#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 +#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a +#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a +#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 +#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 +#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 +#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 + +/* Analog Top Registers - Power and Clock Control */ +#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d +#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 +#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 +#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 + +/* Analog PLL Registers */ +#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 +#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 +#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 +#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 +#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 +#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a +#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b + +/* Analog Boost Control Registers */ +#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b +#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c +#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e +#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f +#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 +#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 +#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 + +/* Analog IV Sense ADC Registers */ +#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca +#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb +#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc +#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 + +/* Analog Speaker Power Stage Registers */ +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e +#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 +#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c +#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c +#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d +#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 +#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a +#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 +#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 +#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 +#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 +#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba +#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 +#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 +#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a +#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b +#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c +#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 +#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 +#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 +#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b +#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 +#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 +#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 +#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 +#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 +#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 +#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 + +/* Digital Control 1 Registers - I2S/TDM Interface */ +#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 +#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 +#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 +#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 +#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 +#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA +#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB +#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE + +/* CDC RX Path Registers - Audio Data Path */ +#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 +#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 +#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 +#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 +#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 +#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 +#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 +#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 + +/* CDC Compander Registers - Dynamic Range Control */ +#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 +#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 +#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 +#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 + +/* CDC Speaker Protection Registers - IV Sense */ +#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 +#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 +#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 +#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 + +/* CDC Class-H Registers - Headroom Control */ +#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD +#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC +#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 +#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 +#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE + +/* Driver Constants */ +#define WSA885X_CLK_RATE_FIXED 73728000 +#define WSA885X_NUM_REGS 0x03 + +/* Interrupt Registers */ +#define WSA885X_INTR_STATUS0 0x8584 +#define WSA885X_INTR_MASK0 0x8581 +#define WSA885X_INTR_CLEAR0 0x8587 + +/* Power and PA FSM Control Registers */ +#define WSA885X_PA0_FSM_CTL0 0x842A +#define WSA885X_PA1_FSM_CTL0 0x8434 + +/* Digital Control GPIO and Interrupt Registers */ +#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 +#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 +#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 + +#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) +#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) +#define WSA885X_I2S_CTL0_PCM_RATE(v) \ + FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) +#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 +#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 +#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 +#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 +#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 +#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 +#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 +#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) +#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) +#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ + FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) +#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) +#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ + FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) +#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) +#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) +#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) +#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) +#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) +#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) +#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) +#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) +#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) +#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) + +#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 +#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 +#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 +#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 + +/* RX Sample Rate Index Values - Audio Playback Path */ +#define WSA885X_RX_RATE_8000HZ 0x00 +#define WSA885X_RX_RATE_16000HZ 0x01 +#define WSA885X_RX_RATE_32000HZ 0x02 +#define WSA885X_RX_RATE_44100HZ 0x03 +#define WSA885X_RX_RATE_48000HZ 0x04 +#define WSA885X_RX_RATE_96000HZ 0x05 +#define WSA885X_RX_RATE_192000HZ 0x06 +#define WSA885X_RX_RATE_384000HZ 0x07 + +/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ +#define WSA885X_VI_RATE_8000HZ 0x00 +#define WSA885X_VI_RATE_16000HZ 0x01 +#define WSA885X_VI_RATE_44100HZ 0x02 +#define WSA885X_VI_RATE_48000HZ 0x03 +#define WSA885X_VI_RATE_96000HZ 0x04 +#define WSA885X_VI_RATE_22050HZ 0x05 +#define WSA885X_VI_RATE_24000HZ 0x06 +#define WSA885X_VI_RATE_192000HZ 0x07 +#define WSA885X_VI_RATE_384000HZ 0x08 + +/* Channel Configuration Masks */ +#define WSA885X_CHANNEL_STEREO 0x03 +#define WSA885X_CHANNEL_MONO_LEFT 0x01 +#define WSA885X_CHANNEL_MONO_RIGHT 0x02 + +#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define WSA885X_PLL_LOCK_BIT BIT(0) + +#define WSA885X_FU21_VOL_STEPS 124 +#define WSA885X_USAGE_MODE_MAX 8 +static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); + +static bool wsa885x_is_valid_rx_slot_mask(u32 mask) +{ + return mask == WSA885X_CHANNEL_MONO_LEFT || + mask == WSA885X_CHANNEL_MONO_RIGHT || + mask == WSA885X_CHANNEL_STEREO; +} + +static const char *const wsa885x_supply_name[] = { + "vdd-1p8", + "vdd-io", +}; + +enum { + WSA885X_BATT_1S = 1, + WSA885X_BATT_2S, +}; + +enum { + WSA885X_IRQ_INT_SAF2WAR = 0, + WSA885X_IRQ_INT_WAR2SAF, + WSA885X_IRQ_INT_DISABLE, + WSA885X_IRQ_INT_PA0_OCP, + WSA885X_IRQ_INT_PA1_OCP, + WSA885X_IRQ_INT_CLIP0, + WSA885X_IRQ_INT_CLIP1, + WSA885X_IRQ_INT_CLK_WD, + WSA885X_IRQ_INT_INTR_GPIO1_PIN, + WSA885X_IRQ_INT_INTR_GPIO2_PIN, + WSA885X_IRQ_INT_UVLO, + WSA885X_IRQ_INT_BOP, + WSA885X_IRQ_INT_PA0_FSM_ERR, + WSA885X_IRQ_INT_PA1_FSM_ERR, + WSA885X_IRQ_INT_MAIN_FSM_ERR, + WSA885X_IRQ_INT_PCM_DATA0_WD, + WSA885X_IRQ_INT_PCM_DATA1_WD, + WSA885X_IRQ_INT_PCM_DATA0_DC, + WSA885X_IRQ_INT_PCM_DATA1_DC, + WSA885X_IRQ_INT_PLL_UNLOCKED, + WSA885X_IRQ_INT_PROT_MODE_CHANGE, + WSA885X_IRQ_INT_PB_CLOCK_VALID, + WSA885X_IRQ_INT_SENSE_CLOCK_VALID, + WSA885X_IRQ_MAX, +}; + +struct wsa885x_priv { + struct i2c_client *client; + struct regmap *regmap; + struct device *dev; + struct snd_soc_component *component; + struct gpio_desc *sd_n; + struct reset_control *sd_reset; + u32 usage_mode; + u32 rx_slot_mask; + u32 batt_conf; + int stereo_vol_db; + struct mutex state_lock; /* protects mutable control state */ +}; + +struct wsa885x_reg_update { + unsigned int reg; + unsigned int mask; + unsigned int val; +}; + +static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 0x88ff, + .selector_reg = 0x0, + .selector_mask = 0xFF, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct reg_default wsa885x_codec_reg_defaults[] = { + {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, + {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, + {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, + {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, + {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, + {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, + {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, + {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, + {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, + {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, + {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, + {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, + {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, + {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, + {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, + {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, + {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, + {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, + {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, + {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, + {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, + {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, + {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, + {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, + {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, + {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, + {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, + {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, + {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, + {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, + {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, + {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, + {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, + {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, + {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, + {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, + {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, + {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, + {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, + {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, + {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, + {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, + {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, + {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, + {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, + {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, + {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, + {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, + {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, + {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, + {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, + {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, + {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, + {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, + {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, + {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, + {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, + {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, + {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, + {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, + {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, + {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, + {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, + {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, + {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, + {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, + {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, + {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, + {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, + {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, + {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, + {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, +}; + +static void wsa885x_multi_update_bits(struct regmap *regmap, + const struct wsa885x_reg_update *updates, + size_t num_updates) +{ + size_t i; + + for (i = 0; i < num_updates; i++) + regmap_update_bits(regmap, updates[i].reg, + updates[i].mask, updates[i].val); +} + +static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, + unsigned int reg, unsigned int mask) +{ + regmap_update_bits(wsa885x->regmap, reg, mask, 0); + regmap_update_bits(wsa885x->regmap, reg, mask, mask); +} + +static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, + unsigned int reg, unsigned int mask) +{ + regmap_update_bits(wsa885x->regmap, reg, mask, 0); + regmap_update_bits(wsa885x->regmap, reg, mask, mask); + regmap_update_bits(wsa885x->regmap, reg, mask, 0); +} + +static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) +{ + if (!slot_num_val) + return -EINVAL; + + switch (slots) { + case 2: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; + return 0; + case 4: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; + return 0; + case 8: + *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; + return 0; + default: + return -EINVAL; + } +} + +static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, + { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, + }; + unsigned int slot_num_val; + int ret; + + if (!regmap) + return -EINVAL; + + ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); + if (ret) + return ret; + + regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); + + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, + WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, + slot_num_val); + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, + WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, + WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); + regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH0_EN); + regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH1_EN, + WSA885X_I2S_TDM_CH_TX_CH1_EN); + + return 0; +} + +static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) +{ + unsigned int status = 0; + int cnt = 0, ret = 0; + + do { + usleep_range(1000, 1100); + ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); + if (ret) { + dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); + return ret; + } + + if (status & WSA885X_PLL_LOCK_BIT) + return 0; + } while (++cnt < 20); + + dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); + return -ETIMEDOUT; +} + +static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, + { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, + { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, + }; + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static const struct reg_sequence wsa885x_reg_init[] = { + { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, + { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, + { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, + { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, + { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, + { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, + { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, + { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, + { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, + { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, + { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, + { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, + { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, + { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, + { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, + { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, + { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, + { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, + { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, + { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, + { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, + { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, + { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, + { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, + { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, + { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, + { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, + { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, + { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, + { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, + { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, + { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, + { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, + { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, + { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, + { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, + { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, + { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, + { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, + { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, + { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, +}; + +static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, + { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, + { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, + }; + int ret; + + ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, + ARRAY_SIZE(wsa885x_reg_init)); + if (ret) + return ret; + + if (wsa885x->batt_conf == WSA885X_BATT_2S) { + ret = wsa885x_2s_conf(wsa885x); + if (ret) + return ret; + } + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) +{ + static const struct reg_sequence regs[] = { + { WSA885X_INTR_MASK0, 0x00 }, + { WSA885X_INTR_MASK0 + 1, 0x00 }, + { WSA885X_INTR_MASK0 + 2, 0xf8 }, + }; + + return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); +} + +static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) +{ + unsigned int act_ps = 0, clock_valid = 0; + int rc = 0, cnt = 0; + + if (ps < 0 || ps > 3) + return -EINVAL; + + do { + usleep_range(1000, 1500); + rc = regmap_read(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, + &act_ps); + if (rc) { + dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); + return rc; + } + if (act_ps == ps) + return 0; + } while (++cnt < 5); + + if (regmap_read(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, + &clock_valid)) + dev_err(wsa885x->dev, + "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", + ps, act_ps); + else + dev_err(wsa885x->dev, + "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", + ps, act_ps, clock_valid); + + return -ETIMEDOUT; +} + +static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, + int stereo_vol_db, bool commit) +{ + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, + (u8)(s8)stereo_vol_db); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, + (u8)(s8)stereo_vol_db); + regmap_write(wsa885x->regmap, + WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); + + if (commit) + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); +} + +static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa885x_priv *wsa885x; + u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + switch (params_rate(params)) { + case 8000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; + break; + case 16000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; + break; + case 32000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; + break; + case 44100: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; + break; + case 48000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; + break; + case 88200: + case 96000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; + break; + case 176400: + case 192000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; + break; + case 352800: + case 384000: + pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; + cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; + cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; + break; + default: + dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, + WSA885X_I2S_CTL0_PCM_RATE_MASK | + WSA885X_I2S_CTL0_ENABLE_MASK, + WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | + WSA885X_I2S_CTL0_ENABLE_MASK); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, + cs21_sample_rate_idx); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, + cs24_sample_rate_idx); + + mutex_lock(&wsa885x->state_lock); + wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); + mutex_unlock(&wsa885x->state_lock); + + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); + + return 0; +} + +static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_slot_mask, + unsigned int rx_slot_mask, int slots, + int slot_width) +{ + static const struct wsa885x_reg_update stereo_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, + { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, + WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, + WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, + }; + static const struct wsa885x_reg_update mono_left_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, + }; + static const struct wsa885x_reg_update mono_right_updates[] = { + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, + { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, + WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, + }; + struct wsa885x_priv *wsa885x; + unsigned int slot_num_val; + u32 mask; + int ret; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); + if (ret) { + dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", + __func__, slots); + return ret; + } + + if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { + dev_err(wsa885x->dev, + "%s: unsupported rx_slot_mask 0x%x\n", + __func__, rx_slot_mask); + return -EINVAL; + } + + mutex_lock(&wsa885x->state_lock); + if (rx_slot_mask) + wsa885x->rx_slot_mask = rx_slot_mask; + else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + mask = wsa885x->rx_slot_mask; + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, + WSA885X_I2S_RESET_CTL_RESET_MASK, + WSA885X_I2S_RESET_CTL_RESET_MASK); + + if (mask == WSA885X_CHANNEL_STEREO) { + wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, + ARRAY_SIZE(stereo_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH2_EN, + WSA885X_I2S_TDM_CH_TX_CH2_EN); + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, + WSA885X_I2S_TDM_CH_TX_CH3_EN, + WSA885X_I2S_TDM_CH_TX_CH3_EN); + } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { + wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, + ARRAY_SIZE(mono_left_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { + wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, + ARRAY_SIZE(mono_right_updates)); + ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); + if (ret) + goto exit_unlock; + } + + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, + WSA885X_I2S_CTL0_ENABLE_MASK, + WSA885X_I2S_CTL0_ENABLE_MASK); + regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, + WSA885X_I2S_RESET_CTL_RESET_MASK, 0); + + ret = 0; + +exit_unlock: + mutex_unlock(&wsa885x->state_lock); + + return ret; +} + +static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + static const struct reg_sequence pll_prep[] = { + { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, + { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, + { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, + { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, + { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, + }; + static const struct reg_sequence pll_cleanup[] = { + { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, + { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, + { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, + }; + struct wsa885x_priv *wsa885x; + u32 pll_div; + int ret = 0; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (!freq) + return -EINVAL; + if (WSA885X_CLK_RATE_FIXED % freq) + return -EINVAL; + pll_div = WSA885X_CLK_RATE_FIXED / freq; + if (pll_div > 0xff) + return -EINVAL; + + regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); + regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); + + ret = wsa885x_wait_for_pll_lock(wsa885x); + if (ret) { + dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); + regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, + ARRAY_SIZE(pll_cleanup)); + return ret; + } + + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); + regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); + + return 0; +} + +static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + static const struct reg_sequence mute_regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, + }; + static const struct reg_sequence mute_commit_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + }; + static const struct reg_sequence unmute_prep_head_regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + }; + static const struct reg_sequence unmute_prep_tail_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, + }; + static const struct reg_sequence unmute_volume_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, + }; + static const struct reg_sequence unmute_commit_regs[] = { + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, + }; + static const struct reg_sequence unmute_finish_regs[] = { + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, + { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, + { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, + }; + struct wsa885x_priv *wsa885x; + int ret = 0, ps0 = 0, ps3 = 3; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + mutex_lock(&wsa885x->state_lock); + + if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { + ret = -EINVAL; + goto exit_unlock; + } + + if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + + if (mute) { + regmap_multi_reg_write(wsa885x->regmap, mute_regs, + ARRAY_SIZE(mute_regs)); + ret = wsa885x_wait_for_pde_state(wsa885x, ps3); + if (ret) { + dev_err(wsa885x->dev, + "PS3 transition failed: %d\n", ret); + } else { + regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, + ARRAY_SIZE(mute_commit_regs)); + } + } else { + regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, + ARRAY_SIZE(unmute_prep_head_regs)); + regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, + wsa885x->usage_mode); + regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, + ARRAY_SIZE(unmute_prep_tail_regs)); + wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); + regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, + ARRAY_SIZE(unmute_volume_regs)); + regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, + ARRAY_SIZE(unmute_commit_regs)); + ret = wsa885x_wait_for_pde_state(wsa885x, ps0); + if (ret) + goto exit_unlock; + + if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH0_EN | + WSA885X_I2S_TDM_CH_RX_CH3_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); + } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH3_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); + } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, + WSA885X_I2S_TDM_CH_RX_CH0_EN); + regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); + } + + regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, + ARRAY_SIZE(unmute_finish_regs)); + } + +exit_unlock: + mutex_unlock(&wsa885x->state_lock); + + return ret; +} + +static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + static const struct reg_sequence regs[] = { + { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, + }; + struct wsa885x_priv *wsa885x; + + wsa885x = snd_soc_component_get_drvdata(dai->component); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + mutex_lock(&wsa885x->state_lock); + regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); + mutex_unlock(&wsa885x->state_lock); + + return 0; +} + +static const struct snd_soc_dai_ops wsa885x_dai_ops = { + .hw_params = wsa885x_codec_hw_params, + .set_tdm_slot = wsa885x_codec_set_tdm_slot, + .set_sysclk = wsa885x_codec_set_sysclk, + .mute_stream = wsa885x_codec_mute_stream, + .hw_free = wsa885x_codec_hw_free, +}; + +static struct snd_soc_dai_driver wsa885x_dai[] = { + { + .name = "wsa885x_dai_drv", + .playback = { + .stream_name = "WSA885X TDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WSA885X_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &wsa885x_dai_ops, + }, +}; + +static void wsa885x_reset_assert(void *data) +{ + struct wsa885x_priv *wsa885x = data; + + if (wsa885x->sd_reset) + reset_control_assert(wsa885x->sd_reset); + else + gpiod_direction_output(wsa885x->sd_n, 1); +} + +static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) +{ + if (wsa885x->sd_reset) + reset_control_deassert(wsa885x->sd_reset); + else + gpiod_direction_output(wsa885x->sd_n, 0); +} + +static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) +{ + wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa885x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), + "Failed to get reset\n"); + else if (wsa885x->sd_reset) + return 0; + + wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(wsa885x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), + "Shutdown Control GPIO not found\n"); + + return 0; +} + +static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA885X_ANA_TOP_PLL_STATUS_0: + case WSA885X_ANA_TOP_PLL_STATUS_1: + case WSA885X_DIG_CTRL0_SDCA_COMMIT: + case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: + case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: + case WSA885X_INTR_STATUS0: + case WSA885X_INTR_STATUS0 + 1: + case WSA885X_INTR_STATUS0 + 2: + case WSA885X_INTR_CLEAR0: + case WSA885X_INTR_CLEAR0 + 1: + case WSA885X_INTR_CLEAR0 + 2: + return true; + default: + return false; + } +} + +static bool wsa885x_readable_register(struct device *dev, unsigned int reg) +{ + if (reg == WSA885X_INTR_CLEAR0 || + reg == WSA885X_INTR_CLEAR0 + 1 || + reg == WSA885X_INTR_CLEAR0 + 2) + return false; + return reg <= 0x88ff; +} + +static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) +{ + if (reg > 0x88ff) + return false; + + switch (reg) { + case WSA885X_ANA_TOP_PLL_STATUS_0: + case WSA885X_ANA_TOP_PLL_STATUS_1: + case WSA885X_INTR_STATUS0: + case WSA885X_INTR_STATUS0 + 1: + case WSA885X_INTR_STATUS0 + 2: + case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: + case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: + return false; + default: + return true; + } +} + +static const struct regmap_config wsa885x_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x88FF, + .ranges = wsa885x_regmap_ranges, + .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), + .reg_defaults = wsa885x_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), + .volatile_reg = wsa885x_volatile_register, + .writeable_reg = wsa885x_writeable_register, + .readable_reg = wsa885x_readable_register, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int wsa885x_component_probe(struct snd_soc_component *component) +{ + struct wsa885x_priv *wsa885x = + snd_soc_component_get_drvdata(component); + int ret; + + wsa885x->component = component; + snd_soc_component_init_regmap(component, wsa885x->regmap); + + ret = wsa885x_hw_init(wsa885x); + if (ret) + return ret; + + return wsa885x_unmask_interrupts(wsa885x); +} + +static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + int val; + + mutex_lock(&wsa885x->state_lock); + val = wsa885x->stereo_vol_db + 84; + mutex_unlock(&wsa885x->state_lock); + if (val < 0 || val > WSA885X_FU21_VOL_STEPS) + return -ERANGE; + + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + long val; + int stereo_vol_db; + + val = ucontrol->value.integer.value[0]; + + if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { + dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); + return -EINVAL; + } + + stereo_vol_db = (int)val - 84; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->stereo_vol_db == stereo_vol_db) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); + wsa885x->stereo_vol_db = stereo_vol_db; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { + mutex_unlock(&wsa885x->state_lock); + return -ERANGE; + } + + ucontrol->value.integer.value[0] = wsa885x->usage_mode; + mutex_unlock(&wsa885x->state_lock); + + return 0; +} + +static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 val = ucontrol->value.integer.value[0]; + + if (val > WSA885X_USAGE_MODE_MAX) + return -EINVAL; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->usage_mode == val) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x->usage_mode = val; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 mask; + + mutex_lock(&wsa885x->state_lock); + mask = wsa885x->rx_slot_mask; + mutex_unlock(&wsa885x->state_lock); + if (!wsa885x_is_valid_rx_slot_mask(mask)) + return -ERANGE; + + ucontrol->value.integer.value[0] = mask; + + return 0; +} + +static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); + u32 mask = ucontrol->value.integer.value[0]; + + if (!wsa885x_is_valid_rx_slot_mask(mask)) + return -EINVAL; + + mutex_lock(&wsa885x->state_lock); + if (wsa885x->rx_slot_mask == mask) { + mutex_unlock(&wsa885x->state_lock); + return 0; + } + + wsa885x->rx_slot_mask = mask; + mutex_unlock(&wsa885x->state_lock); + + return 1; +} + +static const struct snd_kcontrol_new wsa885x_snd_controls[] = { + SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, + wsa885x_usage_modes_get, + wsa885x_usage_modes_put), + + SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, + 0, WSA885X_FU21_VOL_STEPS, 0, + wsa885x_stereo_gain_offset_get, + wsa885x_stereo_gain_offset_put, + wsa885x_fu21_digital_gain), + + SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, + wsa885x_rx_slot_mask_get, + wsa885x_rx_slot_mask_put), +}; + +static const struct snd_soc_component_driver wsa885x_component = { + .name = "wsa885x", + .probe = wsa885x_component_probe, + .controls = wsa885x_snd_controls, + .num_controls = ARRAY_SIZE(wsa885x_snd_controls), +}; + +static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) +{ + struct wsa885x_priv *wsa885x = data; + + if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) + return IRQ_NONE; + + switch (irq_idx) { + case WSA885X_IRQ_INT_SAF2WAR: + case WSA885X_IRQ_INT_WAR2SAF: + case WSA885X_IRQ_INT_DISABLE: + case WSA885X_IRQ_INT_INTR_GPIO1_PIN: + case WSA885X_IRQ_INT_INTR_GPIO2_PIN: + case WSA885X_IRQ_INT_PA0_OCP: + case WSA885X_IRQ_INT_PA1_OCP: + case WSA885X_IRQ_INT_CLIP0: + case WSA885X_IRQ_INT_CLIP1: + case WSA885X_IRQ_INT_CLK_WD: + case WSA885X_IRQ_INT_BOP: + case WSA885X_IRQ_INT_UVLO: + case WSA885X_IRQ_INT_PCM_DATA0_DC: + case WSA885X_IRQ_INT_PCM_DATA1_DC: + case WSA885X_IRQ_INT_PLL_UNLOCKED: + case WSA885X_IRQ_INT_PROT_MODE_CHANGE: + case WSA885X_IRQ_INT_PB_CLOCK_VALID: + case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: + break; + case WSA885X_IRQ_INT_PCM_DATA0_WD: + case WSA885X_IRQ_INT_PCM_DATA1_WD: + if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) + wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, + WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); + else + wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, + WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); + break; + case WSA885X_IRQ_INT_PA0_FSM_ERR: + case WSA885X_IRQ_INT_PA1_FSM_ERR: + case WSA885X_IRQ_INT_MAIN_FSM_ERR: + if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, + WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); + } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, + WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); + } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { + wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, + WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); + } + break; + default: + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) +{ + static const unsigned int status_reg[WSA885X_NUM_REGS] = { + WSA885X_INTR_STATUS0, + WSA885X_INTR_STATUS0 + 1, + WSA885X_INTR_STATUS0 + 2, + }; + static const unsigned int clear_reg[WSA885X_NUM_REGS] = { + WSA885X_INTR_CLEAR0, + WSA885X_INTR_CLEAR0 + 1, + WSA885X_INTR_CLEAR0 + 2, + }; + unsigned int status[WSA885X_NUM_REGS] = { 0 }; + struct wsa885x_priv *wsa885x = data; + irqreturn_t handled = IRQ_NONE; + irqreturn_t irq_ret; + int i, bit, ret, irq_num; + + for (i = 0; i < WSA885X_NUM_REGS; i++) { + ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); + if (ret) { + dev_err(wsa885x->dev, + "Failed to read status_reg[%d] (0x%x): %d\n", + i, status_reg[i], ret); + status[i] = 0; + continue; + } + } + + for (i = 0; i < WSA885X_NUM_REGS; i++) { + for (bit = 0; bit < 8; bit++) { + if (status[i] & BIT(bit)) { + irq_num = i * 8 + bit; + regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); + regmap_write(wsa885x->regmap, clear_reg[i], 0); + if (irq_num >= WSA885X_IRQ_MAX) { + dev_warn_ratelimited(wsa885x->dev, + "Unexpected IRQ bit %d (reg %d)\n", + bit, i); + handled = IRQ_HANDLED; + continue; + } + irq_ret = wsa885x_handle_irq(irq_num, wsa885x); + if (irq_ret == IRQ_HANDLED) + handled = IRQ_HANDLED; + } + } + } + return handled; +} + +static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) +{ + if (!wsa885x->client->irq) + return dev_err_probe(wsa885x->dev, -EINVAL, + "IRQ is not configured\n"); + + return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, + wsa885x_interrupt_handler, + IRQF_ONESHOT, + dev_name(wsa885x->dev), wsa885x); +} + +static int wsa885x_probe(struct i2c_client *client) +{ + struct wsa885x_priv *wsa885x; + const struct snd_soc_component_driver *component_driver = &wsa885x_component; + const char *battery_config; + unsigned int i; + int ret; + struct device *dev = &client->dev; + + wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); + if (!wsa885x) + return -ENOMEM; + + wsa885x->client = client; + wsa885x->dev = dev; + wsa885x->stereo_vol_db = -84; + wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; + mutex_init(&wsa885x->state_lock); + wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); + + if (IS_ERR(wsa885x->regmap)) + return PTR_ERR(wsa885x->regmap); + + ret = device_property_read_string(dev, "qcom,battery-config", + &battery_config); + if (ret) { + wsa885x->batt_conf = WSA885X_BATT_1S; + } else if (!strcmp(battery_config, "1s")) { + wsa885x->batt_conf = WSA885X_BATT_1S; + } else if (!strcmp(battery_config, "2s")) { + wsa885x->batt_conf = WSA885X_BATT_2S; + } else { + return dev_err_probe(dev, -EINVAL, + "Invalid battery config %s (expected 1s or 2s)\n", + battery_config); + } + + for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { + ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable regulator %s\n", + wsa885x_supply_name[i]); + } + + ret = wsa885x_get_reset(dev, wsa885x); + if (ret) + return ret; + + wsa885x_reset_deassert(wsa885x); + usleep_range(5000, 5500); + + ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); + if (ret) + return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); + + i2c_set_clientdata(client, wsa885x); + + ret = wsa885x_register_irq(wsa885x); + if (ret) + return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); + + ret = devm_snd_soc_register_component(dev, component_driver, + wsa885x_dai, + ARRAY_SIZE(wsa885x_dai)); + if (ret) + return dev_err_probe(dev, ret, "Codec component registration failed\n"); + + return 0; +} + +static const struct of_device_id wsa885x_dt_match[] = { + { + .compatible = "qcom,wsa8855", + }, + {} +}; +MODULE_DEVICE_TABLE(of, wsa885x_dt_match); + +static const struct i2c_device_id wsa885x_id[] = { + { + .name = "wsa885x", + .driver_data = 0, + }, + {} +}; +MODULE_DEVICE_TABLE(i2c, wsa885x_id); + +static struct i2c_driver wsa885x_driver = { + .driver = { + .name = "wsa885x", + .of_match_table = wsa885x_dt_match, + }, + .probe = wsa885x_probe, + .id_table = wsa885x_id, +}; + +module_i2c_driver(wsa885x_driver); + +MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); +MODULE_LICENSE("GPL"); From c143608b5602774d3c45a56d1149e21a213d70c6 Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Thu, 30 Jul 2026 13:43:39 -0400 Subject: [PATCH 481/791] ASoC: dt-bindings: qcom,sm8250: add compatible for sdm660 Add compatibles for sdm660 based soundcards. Signed-off-by: Nickolay Goppen Signed-off-by: Richard Acayan Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-2-mailingradian@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 486780ed553c..c1bd19299763 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -48,6 +48,7 @@ properties: - qcom,qrb5165-rb5-sndcard - qcom,sc7180-qdsp6-sndcard - qcom,sc8280xp-sndcard + - qcom,sdm660-sndcard - qcom,sdm845-sndcard - qcom,sm8250-sndcard - qcom,sm8450-sndcard From 96591d7030a68ecdbb1be793d4b067d2087e0290 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:40 -0400 Subject: [PATCH 482/791] ASoC: dt-bindings: qcom: q6dsp: add support for lpi mi2s ports 5-6 There are 7 internal MI2S ports per direction found on devices with the internal sound card for Snapdragon 660. This is similar to the LPI MI2S ports, and the LPI MI2S bindings can be reused for internal MI2S. Extend the bindings for LPI MI2S ports to accommodate the internal MI2S ports. Signed-off-by: Richard Acayan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-3-mailingradian@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml | 4 ++-- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 2b27d6c8f58f..3b03e2acd67e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -127,7 +127,7 @@ patternProperties: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and # QUINARY_MI2S_RX - QUINARY_MI2S_TX and - # LPI_MI2S_RX_0 - SENARY_MI2S_TX + # LPI_MI2S_RX_0 - LPI_MI2S_TX_6 items: oneOf: - minimum: 16 @@ -135,7 +135,7 @@ patternProperties: - minimum: 127 maximum: 128 - minimum: 137 - maximum: 148 + maximum: 152 then: required: - qcom,sd-lines diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 7b553a73bc92..31b9ef65f903 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -152,6 +152,10 @@ #define LPI_MI2S_TX_4 146 #define SENARY_MI2S_RX 147 #define SENARY_MI2S_TX 148 +#define LPI_MI2S_RX_5 149 +#define LPI_MI2S_TX_5 150 +#define LPI_MI2S_RX_6 151 +#define LPI_MI2S_TX_6 152 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 From e70f039588492937b72d9f2f9bef8d630436c59e Mon Sep 17 00:00:00 2001 From: Adam Skladowski Date: Thu, 30 Jul 2026 13:43:41 -0400 Subject: [PATCH 483/791] ASoC: dt-bindings: pm8916-wcd-analog-codec: Document pm8950/pm8953 Document pm8950 and pm8953 analog audio codecs. Signed-off-by: Adam Skladowski [richard: add back empty line] Signed-off-by: Richard Acayan Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-4-mailingradian@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,pm8916-wcd-analog-codec.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml index 94e7a1860977..15389645a3e8 100644 --- a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml @@ -14,7 +14,10 @@ description: properties: compatible: - const: qcom,pm8916-wcd-analog-codec + enum: + - qcom,pm8916-wcd-analog-codec + - qcom,pm8950-wcd-analog-codec + - qcom,pm8953-wcd-analog-codec reg: maxItems: 1 From 5f42d4525b43200298870999728cc90db06d4c01 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:42 -0400 Subject: [PATCH 484/791] ASoC: dt-bindings: pm8916-analog-codec: Add PM660L compatible The PM8953 (cajon 2.0) revision of the PM8916 analog codec is also found on PM660L, typically connected to the SDM660 internal sound card via the digital codec. Provide a space for specific compatibles and add the compatible for PM660L. Signed-off-by: Richard Acayan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-5-mailingradian@gmail.com Signed-off-by: Mark Brown --- .../sound/qcom,pm8916-wcd-analog-codec.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml index 15389645a3e8..be47dbdb2e92 100644 --- a/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,pm8916-wcd-analog-codec.yaml @@ -14,10 +14,16 @@ description: properties: compatible: - enum: - - qcom,pm8916-wcd-analog-codec - - qcom,pm8950-wcd-analog-codec - - qcom,pm8953-wcd-analog-codec + oneOf: + - items: + - enum: + - qcom,pm660l-wcd-analog-codec + - const: qcom,pm8953-wcd-analog-codec + + - enum: + - qcom,pm8916-wcd-analog-codec + - qcom,pm8950-wcd-analog-codec + - qcom,pm8953-wcd-analog-codec reg: maxItems: 1 From 6f48a57f4bb2d24e246f975a28ab0e1ad9fd146b Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:43 -0400 Subject: [PATCH 485/791] ASoC: dt-bindings: msm8916-digital-codec: Add SDM660 compatible The MSM8916 digital codec is also found on SDM660, typically connected to the SDM660 internal sound card. Provide a space for specific compatibles and add the compatible for SDM660. Signed-off-by: Richard Acayan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-6-mailingradian@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,msm8916-wcd-digital-codec.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml index a899c4e7c1c9..33bc23b6176a 100644 --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml @@ -14,7 +14,13 @@ description: properties: compatible: - const: qcom,msm8916-wcd-digital-codec + oneOf: + - items: + - enum: + - qcom,sdm660-wcd-digital-codec + - const: qcom,msm8916-wcd-digital-codec + + - const: qcom,msm8916-wcd-digital-codec reg: maxItems: 1 From 68400191e005f44c432c1b18eeca7f5527323a9f Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:44 -0400 Subject: [PATCH 486/791] ASoC: qdsp6: q6dsp-lpass-ports: add support for lpi mi2s ports 5-6 Add the extra LPI MI2S ports used for internal MI2S on SDM660. Link: https://android.googlesource.com/kernel/msm-extra/+/530cffa4cc977a348753831b163eb9d3302b954a/asoc/msm-dai-q6-v2.c#4597 Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-7-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.h | 2 +- sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index ee6662885593..48b114eb46a5 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -7,7 +7,7 @@ #include #include -#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) +#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index e5cd82f77b55..c3d8116ad503 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), + Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), + Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), + Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), + Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), @@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; break; case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: From bfe9098dc275a319c6b9f6e2417bb60fb8738a4b Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:45 -0400 Subject: [PATCH 487/791] ASoC: qdsp6: q6afe: add internal mi2s support The bindings for LPI MI2S ports, originally exclusive to q6apm, can be used for internal MI2S ports on q6afe. Add the port mappings for internal MI2S, found on the Snapdragon 660 internal sound card. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-8-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 40237267fda0..1d68a80e8e0c 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -132,6 +132,20 @@ #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 +#define AFE_PORT_ID_INT0_MI2S_RX 0x102e +#define AFE_PORT_ID_INT0_MI2S_TX 0x102f +#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 +#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 +#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 +#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 +#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 +#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 +#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 +#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 +#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 +#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 +#define AFE_PORT_ID_INT6_MI2S_RX 0x103a +#define AFE_PORT_ID_INT6_MI2S_TX 0x103b /* Start of the range of port IDs for TDM devices. */ #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 @@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, RX_CODEC_DMA_RX_7, 1, 1}, [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, + [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, + LPI_MI2S_RX_0, 1, 1}, + [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, + LPI_MI2S_TX_0, 0, 1}, + [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, + LPI_MI2S_RX_1, 1, 1}, + [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, + LPI_MI2S_TX_1, 0, 1}, + [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, + LPI_MI2S_RX_2, 1, 1}, + [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, + LPI_MI2S_TX_2, 0, 1}, + [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, + LPI_MI2S_RX_3, 1, 1}, + [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, + LPI_MI2S_TX_3, 0, 1}, + [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, + LPI_MI2S_RX_4, 1, 1}, + [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, + LPI_MI2S_TX_4, 0, 1}, + [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, + LPI_MI2S_RX_5, 1, 1}, + [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, + LPI_MI2S_TX_5, 0, 1}, + [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, + LPI_MI2S_RX_6, 1, 1}, + [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, + LPI_MI2S_TX_6, 0, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_QUINARY_MI2S_TX: case AFE_PORT_ID_SENARY_MI2S_RX: case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_TX: cfg_type = AFE_PARAM_ID_I2S_CONFIG; break; case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: From 0b494ae6a582b07c090839b8c62b931d6f4e2848 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:46 -0400 Subject: [PATCH 488/791] ASoC: qdsp6: q6afe-dai: add internal mi2s support The bindings for LPI MI2S ports, originally exclusive to q6apm, can be used for internal MI2S ports on q6afe. Add the internal MI2S ports found on the SDM660 internal sound card using the LPI MI2S bindings. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-9-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-dai.c | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index a0d21034a626..920345609d2c 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].i2s_cfg); if (rc < 0) { @@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { /* USB playback AFE port receives data for playback, hence use the RX port */ {"USB Playback", NULL, "USB_RX"}, + + {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, + {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, + {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, + {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, + {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, + {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, + {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, + {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, + {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, + {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, + {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, + {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, + {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, + {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, }; static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) @@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, + 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { @@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: + case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: priv = &data->priv[id]; ret = of_property_read_variable_u32_array(node, "qcom,sd-lines", From 14d3cca9c2ed1ae0d647f109cfdf5d63f3f5be09 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:47 -0400 Subject: [PATCH 489/791] ASoC: qdsp6: q6routing: add lpi mi2s support Add the ASM-AFE routing for LPI MI2S ports which represent internal MI2S ports on SDM660. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-10-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6routing.c | 78 +++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 7386226046fa..d076c99f197f 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -127,7 +127,14 @@ { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ - { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} + { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ + { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ + { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ + { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ + { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ + { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ + { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ + { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } #define Q6ROUTING_TX_MIXERS(id) \ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ @@ -320,6 +327,27 @@ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), @@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; +static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; + +static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; static const struct snd_kcontrol_new mmul1_mixer_controls[] = { Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; @@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, usb_rx_mixer_controls, ARRAY_SIZE(usb_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_0_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_1_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_2_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_3_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_4_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_5_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + lpi_mi2s_rx_6_mixer_controls, + ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, @@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), From bd8216bfe24ff96211f436ffffa6a9c4adbc9d36 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:48 -0400 Subject: [PATCH 490/791] ASoC: qdsp6: common: support headphone jacks connected to lpi mi2s On SDM660, LPI MI2S ports can be connected to a WCD codec which may support headphones. Register the headphone jack on codecs connected to the playback port, LPI_MI2S_RX_0. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-11-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index e4ff247ea47f..f8782e5cfaae 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -215,6 +215,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, } switch (cpu_dai->id) { + case LPI_MI2S_RX_0: case TX_CODEC_DMA_TX_0: case TX_CODEC_DMA_TX_1: case TX_CODEC_DMA_TX_2: From 5e913c10bd0d60ef93657f5c84014c998dbd4100 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:49 -0400 Subject: [PATCH 491/791] ASoC: qcom: sm8250: add support for LPI_MI2S_RX_0 and LPI_MI2S_TX_3 The LPI_MI2S_RX_0 and LPI_MI2S_TX_3 ports on SDM660 can be connected to the digital and analog WCD codecs. They can be supported with the same logic for other ports, but just need to be explicitly stated. Add support for these ports. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-12-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index f193d0ba63d0..1952c599e004 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) snd_soc_dai_set_fmt(cpu_dai, fmt); snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; + case LPI_MI2S_RX_0: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; + case LPI_MI2S_TX_3: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; default: break; } From 84f950973d9115adceee596528f4215b89323748 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:50 -0400 Subject: [PATCH 492/791] ASoC: qcom: sm8250: add SDM660 compatible Add the compatible for SDM660 and SDM670 devices, which can use the support for WCD codecs connected to internal MI2S. Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730174353.108023-13-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 1952c599e004..76dc3a3f4a31 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -210,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, + { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, {} }; From a3e1181e12c779a5f537302421bf9f3202cc1abe Mon Sep 17 00:00:00 2001 From: Adam Skladowski Date: Thu, 30 Jul 2026 13:43:51 -0400 Subject: [PATCH 493/791] ASoC: msm8916-wcd-analog: add pm8950 codec Add regs overrides for PM8950 codec and implement matching reg overrides via compatible. Signed-off-by: Adam Skladowski Reviewed-by: Dmitry Baryshkov Signed-off-by: Richard Acayan Link: https://patch.msgid.link/20260730174353.108023-14-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-analog.c | 52 ++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 9ca381812975..13df60409857 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -232,6 +232,8 @@ #define RX_EAR_CTL_PA_SEL_MASK BIT(7) #define RX_EAR_CTL_PA_SEL BIT(7) +#define CDC_A_RX_EAR_STATUS (0xf1A1) + #define CDC_A_SPKR_DAC_CTL (0xf1B0) #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 @@ -250,6 +252,7 @@ SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) +#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) #define CDC_A_SPKR_OCP_CTL (0xf1B4) #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) @@ -264,6 +267,7 @@ #define CDC_A_SPKR_DRV_DBG (0xf1B7) #define CDC_A_CURRENT_LIMIT (0xf1C0) +#define CDC_A_BYPASS_MODE (0xf1C2) #define CDC_A_BOOST_EN_CTL (0xf1C3) #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) #define CDC_A_SEC_ACCESS (0xf1D0) @@ -286,6 +290,11 @@ static const char * const supply_names[] = { #define MBHC_MAX_BUTTONS (5) +struct wcd_reg_seq { + const struct reg_default *seq; + int seq_size; +}; + struct pm8916_wcd_analog_priv { u16 pmic_rev; u16 codec_version; @@ -715,9 +724,41 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { {CDC_A_MASTER_BIAS_CTL, 0x30}, }; +static const struct wcd_reg_seq pm8916_data = { + .seq = wcd_reg_defaults_2_0, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), +}; + +static const struct reg_default wcd_reg_defaults_pm8950[] = { + {CDC_A_RX_COM_OCP_CTL, 0xd1}, + {CDC_A_RX_COM_OCP_COUNT, 0xff}, + {CDC_D_SEC_ACCESS, 0xa5}, + {CDC_D_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, + {CDC_A_NCP_FBCTRL, 0xa8}, + {CDC_A_NCP_VCTRL, 0xa4}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_SEC_ACCESS, 0xa5}, + {CDC_A_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_CURRENT_LIMIT, 0x82}, + {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xe1}, + {CDC_A_RX_HPH_BIAS_PA, 0xfa}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, + {CDC_A_MICB_1_INT_RBIAS, 0x00}, +}; + +static const struct wcd_reg_seq pm8950_data = { + .seq = wcd_reg_defaults_pm8950, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), +}; + static int pm8916_wcd_analog_probe(struct snd_soc_component *component) { struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + const struct wcd_reg_seq *wcd_reg_init_data; int err, reg; err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); @@ -738,9 +779,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); - for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) - snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, - wcd_reg_defaults_2_0[reg].def); + wcd_reg_init_data = of_device_get_match_data(component->dev); + + for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) + snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, + wcd_reg_init_data->seq[reg].def); priv->component = component; @@ -1259,7 +1302,8 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) } static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { - { .compatible = "qcom,pm8916-wcd-analog-codec", }, + { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, + { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, { } }; From 4583c124538dfc591bdb08e1e0353d02d3a145bc Mon Sep 17 00:00:00 2001 From: Vladimir Lypak Date: Thu, 30 Jul 2026 13:43:52 -0400 Subject: [PATCH 494/791] ASoC: msm8916-wcd-analog: add pm8953 codec Add regs overrides for PM8953 codec. Signed-off-by: Vladimir Lypak [Adam: rename codec] Signed-off-by: Adam Skladowski Reviewed-by: Dmitry Baryshkov Signed-off-by: Richard Acayan Link: https://patch.msgid.link/20260730174353.108023-15-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-analog.c | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 13df60409857..b9325290c28d 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -755,6 +755,34 @@ static const struct wcd_reg_seq pm8950_data = { .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), }; +static const struct reg_default wcd_reg_defaults_pm8953[] = { + {CDC_A_RX_COM_OCP_CTL, 0xd1}, + {CDC_A_RX_COM_OCP_COUNT, 0xff}, + {CDC_D_SEC_ACCESS, 0xa5}, + {CDC_D_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, + {CDC_A_NCP_FBCTRL, 0xa8}, + {CDC_A_NCP_VCTRL, 0xa4}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_SEC_ACCESS, 0xa5}, + {CDC_A_PERPH_RESET_CTL3, 0x0f}, + {CDC_A_CURRENT_LIMIT, 0xa2}, + {CDC_A_BYPASS_MODE, 0x18}, + {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xe1}, + {CDC_A_RX_HPH_BIAS_PA, 0xfa}, + {CDC_A_RX_EAR_STATUS, 0x10}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, + {CDC_A_MICB_1_INT_RBIAS, 0x00}, +}; + +static const struct wcd_reg_seq pm8953_data = { + .seq = wcd_reg_defaults_pm8953, + .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), +}; + static int pm8916_wcd_analog_probe(struct snd_soc_component *component) { struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); @@ -1304,6 +1332,7 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, + { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, { } }; From ff01bff07665d17286a102f70087911675f72953 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:53 -0400 Subject: [PATCH 495/791] ASoC: msm8916-wcd-analog: add quirk for cajon 2.0 The codec version CAJON_2_0 on the Snapdragon 670 requires touching the HPH test registers. Add the quirk so this driver can also support SDM670. Signed-off-by: Richard Acayan Link: https://patch.msgid.link/20260730174353.108023-16-mailingradian@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-analog.c | 63 ++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index b9325290c28d..87f8a47cc293 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -217,9 +217,11 @@ #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) #define CDC_A_RX_HPH_BIAS_CNP (0xf196) #define CDC_A_RX_HPH_CNP_EN (0xf197) +#define CDC_A_RX_HPH_L_TEST (0xf19A) #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) +#define CDC_A_RX_HPH_R_TEST (0xf19C) #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) @@ -705,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, return 0; } +static int pm8916_wcd_analog_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + + /* This quirk is not required for revisions prior to CAJON_2_0 */ + if (priv->codec_version < 4) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, + 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + /* wait 20 ms after the digital codec has powered down */ + msleep(20); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, + 0x04, 0x00); + break; + } + return 0; +} + +static int pm8916_wcd_analog_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + + /* This quirk is not required for revisions prior to CAJON_2_0 */ + if (priv->codec_version < 4) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, + 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + msleep(20); + snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, + 0x04, 0x00); + break; + } + return 0; +} + static const struct reg_default wcd_reg_defaults_2_0[] = { {CDC_A_RX_COM_OCP_CTL, 0xD1}, {CDC_A_RX_COM_OCP_COUNT, 0xFF}, @@ -954,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, + pm8916_wcd_analog_enable_hphl_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, + pm8916_wcd_analog_enable_hphr_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, 0), From 07fd957b53fc97dafbf6a3f7f13e2f030bce8114 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 31 Jul 2026 17:26:23 +0100 Subject: [PATCH 496/791] ASoC: qcom: sc8280xp: tolerate -ENOTSUPP from codec set_sysclk Not all codecs implement the set_sysclk operation. When the board enables codec_sysclk_set, snd_soc_dai_set_sysclk() on the codec DAI can return -ENOTSUPP, which currently aborts hw_params and breaks playback/capture on such boards even though the missing clock setup is harmless. Ignore -ENOTSUPP for the codec set_sysclk call. Fixes: 766f3f79c312 ("ASoC: qcom: sc8280xp: enhance machine driver for board-specific config") Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260731162626.1588561-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index a9304784d41e..ce2b0633688c 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -202,7 +202,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, SND_SOC_CLOCK_IN); - if (ret) + if (ret && ret != -ENOTSUPP) return ret; } break; From a2fdf929fbb491f3bf4d3cadb5a7cc7c90182c0f Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 31 Jul 2026 17:26:24 +0100 Subject: [PATCH 497/791] ASoC: qcom: qdsp6: q6prm: add the missing MCLK clock IDs Add the missing MCLK ids for the q6prm DSP interface. Reviewed-by: Srinivas Kandagatla Signed-off-by: Neil Armstrong Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260731162626.1588561-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6prm-clocks.c | 5 +++++ sound/soc/qcom/qdsp6/q6prm.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 4c574b48ab00..51b131fa9531 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index bc5b9fa13283..1800b997722f 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -55,6 +55,17 @@ /* Clock ID for QUINARY MI2S OSR CLK */ #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 +/* Clock ID for MCLK1 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 +/* Clock ID for MCLK2 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 +/* Clock ID for MCLK3 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 +/* Clock ID for MCLK4 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 +/* Clock ID for MCLK5 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 From a28da0f80d056ec938157a88dc3cecef120c017a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 31 Jul 2026 17:26:25 +0100 Subject: [PATCH 498/791] ASoC: qcom: sc8280xp: rename snd_soc_common to qcom_snd_soc_common The driver-local structure was named 'snd_soc_common', which occupies the generic snd_soc_ ASoC namespace even though it is specific to the Qualcomm sc8280xp machine driver. Rename the type to qcom_snd_soc_common so the identifier is properly scoped to this driver, and rename the pointer field in sc8280xp_snd_data from 'snd_soc_common_priv' to 'priv' so the shorter name doesn't repeat the type name at every use. No functional change. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260731162626.1588561-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index ce2b0633688c..d938eeda64bf 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -37,7 +37,7 @@ static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; -struct snd_soc_common { +struct qcom_snd_soc_common { const char *driver_name; const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; @@ -57,7 +57,7 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; - const struct snd_soc_common *snd_soc_common_priv; + const struct qcom_snd_soc_common *priv; bool jack_setup; }; @@ -121,7 +121,7 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - if (data->snd_soc_common_priv->wcd_jack) + if (data->priv->wcd_jack) return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); return 0; @@ -175,14 +175,14 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, if (ret && ret != -ENOTSUPP) return ret; - if (data->snd_soc_common_priv->codec_dai_fmt) { + if (data->priv->codec_dai_fmt) { ret = snd_soc_dai_set_fmt(codec_dai, - data->snd_soc_common_priv->codec_dai_fmt); + data->priv->codec_dai_fmt); if (ret && ret != -ENOTSUPP) return ret; } - if (data->snd_soc_common_priv->mi2s_mclk_enable) { + if (data->priv->mi2s_mclk_enable) { ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_MCLK, mclk_freq, SND_SOC_CLOCK_OUT); @@ -190,7 +190,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return ret; } - if (data->snd_soc_common_priv->mi2s_bclk_enable) { + if (data->priv->mi2s_bclk_enable) { ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, SND_SOC_CLOCK_OUT); @@ -198,7 +198,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return ret; } - if (data->snd_soc_common_priv->codec_sysclk_set) { + if (data->priv->codec_sysclk_set) { ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, SND_SOC_CLOCK_IN); @@ -269,38 +269,38 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - data->snd_soc_common_priv = of_device_get_match_data(dev); - if (!data->snd_soc_common_priv) + data->priv = of_device_get_match_data(dev); + if (!data->priv) return -ENODEV; card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); - card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; - card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; - card->dapm_routes = data->snd_soc_common_priv->dapm_routes; - card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; - card->controls = data->snd_soc_common_priv->controls; - card->num_controls = data->snd_soc_common_priv->num_controls; + card->dapm_widgets = data->priv->dapm_widgets; + card->num_dapm_widgets = data->priv->num_dapm_widgets; + card->dapm_routes = data->priv->dapm_routes; + card->num_dapm_routes = data->priv->num_dapm_routes; + card->controls = data->priv->controls; + card->num_controls = data->priv->num_controls; ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = data->snd_soc_common_priv->driver_name; + card->driver_name = data->priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } -static const struct snd_soc_common eliza_priv_data = { +static const struct qcom_snd_soc_common eliza_priv_data = { .driver_name = "eliza", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common hawi_priv_data = { +static const struct qcom_snd_soc_common hawi_priv_data = { .driver_name = "hawi", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), @@ -309,74 +309,74 @@ static const struct snd_soc_common hawi_priv_data = { .wcd_jack = true, }; -static const struct snd_soc_common kaanapali_priv_data = { +static const struct qcom_snd_soc_common kaanapali_priv_data = { .driver_name = "kaanapali", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common qcs9100_priv_data = { +static const struct qcom_snd_soc_common qcs9100_priv_data = { .driver_name = "sa8775p", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common qcs615_priv_data = { +static const struct qcom_snd_soc_common qcs615_priv_data = { .driver_name = "qcs615", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common qcm6490_priv_data = { +static const struct qcom_snd_soc_common qcm6490_priv_data = { .driver_name = "qcm6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common qcs6490_priv_data = { +static const struct qcom_snd_soc_common qcs6490_priv_data = { .driver_name = "qcs6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common qcs8275_priv_data = { +static const struct qcom_snd_soc_common qcs8275_priv_data = { .driver_name = "qcs8300", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common sc8280xp_priv_data = { +static const struct qcom_snd_soc_common sc8280xp_priv_data = { .driver_name = "sc8280xp", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common sm8450_priv_data = { +static const struct qcom_snd_soc_common sm8450_priv_data = { .driver_name = "sm8450", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common sm8550_priv_data = { +static const struct qcom_snd_soc_common sm8550_priv_data = { .driver_name = "sm8550", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common sm8650_priv_data = { +static const struct qcom_snd_soc_common sm8650_priv_data = { .driver_name = "sm8650", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .wcd_jack = true, }; -static const struct snd_soc_common sm8750_priv_data = { +static const struct qcom_snd_soc_common sm8750_priv_data = { .driver_name = "sm8750", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), From 4ba2678ad03ea21e946395d4b1476e9fb0d218fb Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 31 Jul 2026 17:26:26 +0100 Subject: [PATCH 499/791] ASoC: qcom: sc8280xp: add monaco/monza controls for qcs8275 Update dai-ids and add DAPM widgets, sysclk and controls required for the VENTUNO-Q platform which uses MAX98090 codec. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260731162626.1588561-5-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index d938eeda64bf..912137566f94 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -37,6 +37,26 @@ static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; +static const struct snd_kcontrol_new max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Headset Mic12"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic56"), + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Receiver"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic12", NULL), + SND_SOC_DAPM_MIC("Headset Mic56", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Receiver", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + struct qcom_snd_soc_common { const char *driver_name; const struct snd_soc_dapm_widget *dapm_widgets; @@ -171,6 +191,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); if (ret && ret != -ENOTSUPP) return ret; @@ -344,8 +365,12 @@ static const struct qcom_snd_soc_common qcs6490_priv_data = { static const struct qcom_snd_soc_common qcs8275_priv_data = { .driver_name = "qcs8300", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .dapm_widgets = max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), + .controls = max98090_controls, + .num_controls = ARRAY_SIZE(max98090_controls), + .codec_sysclk_set = true, + .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, }; static const struct qcom_snd_soc_common sc8280xp_priv_data = { From 1bd470f27f283cfcfd5c94705a2fabbe5f1e4e9f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:00:09 +0000 Subject: [PATCH 500/791] ASoC: apple: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0f5jgx2.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/apple/mca.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index afb3754a8883..be702b2942a5 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -739,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 mca_fe_selectable_formats[] = +{ + /* pattern 1 */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF, + + /* pattern 2 */ + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF +}; + static const struct snd_soc_dai_ops mca_fe_ops = { .startup = mca_fe_startup, .set_fmt = mca_fe_set_fmt, @@ -748,6 +761,8 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .trigger = mca_fe_trigger, .prepare = mca_fe_prepare, .hw_free = mca_fe_hw_free, + .auto_selectable_formats = mca_fe_selectable_formats, + .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), }; /* From f7726b460010c59f5af5a82897cbcf3192d2454d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:00:23 +0000 Subject: [PATCH 501/791] ASoC: atmel: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87wlupjgwp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/atmel/atmel-i2s.c | 4 ++++ sound/soc/atmel/atmel_ssc_dai.c | 7 +++++++ sound/soc/atmel/mchp-i2s-mcc.c | 9 +++++++++ sound/soc/atmel/mchp-pdmc.c | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 762199faf872..d9ad262ec44d 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; + static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { .probe = atmel_i2s_dai_probe, .prepare = atmel_i2s_prepare, .trigger = atmel_i2s_trigger, .hw_params = atmel_i2s_hw_params, .set_fmt = atmel_i2s_set_dai_fmt, + .auto_selectable_formats = &atmel_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver atmel_i2s_dai = { diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 89098f41679c..f4f886bf678d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S32_LE) +static const u64 atmel_ssc_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A; + static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, .shutdown = atmel_ssc_shutdown, @@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .hw_params = atmel_ssc_hw_params, .set_fmt = atmel_ssc_set_dai_fmt, .set_clkdiv = atmel_ssc_set_dai_clkdiv, + .auto_selectable_formats = &atmel_ssc_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver atmel_ssc_dai = { diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index 17d138bb9064..a832dee73ac7 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 mchp_i2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_GATED | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { .probe = mchp_i2s_mcc_dai_probe, .set_sysclk = mchp_i2s_mcc_set_sysclk, @@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { .hw_free = mchp_i2s_mcc_hw_free, .set_fmt = mchp_i2s_mcc_set_dai_fmt, .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, + .auto_selectable_formats = &mchp_i2s_selectable_formats, + .num_auto_selectable_formats = 1, }; #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index ec7233ce1f78..a6a9b5e6cd95 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, return ret; } +static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; + static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .probe = mchp_pdmc_dai_probe, .set_fmt = mchp_pdmc_set_fmt, @@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .hw_params = mchp_pdmc_hw_params, .trigger = mchp_pdmc_trigger, .pcm_new = &mchp_pdmc_pcm_new, + .auto_selectable_formats = &mchp_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver mchp_pdmc_dai = { From d2b8012401ea59b665493f6cbaf1553bdba34882 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:00:30 +0000 Subject: [PATCH 502/791] ASoC: au1x: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87v7a9jgwh.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/au1x/i2sc.c | 11 +++++++++++ sound/soc/au1x/psc-i2s.c | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c index 57735004f416..0e9c7eef9ee8 100644 --- a/sound/soc/au1x/i2sc.c +++ b/sound/soc/au1x/i2sc.c @@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, return 0; } +static const u64 au1xi2s_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops au1xi2s_dai_ops = { .startup = au1xi2s_startup, .trigger = au1xi2s_trigger, .hw_params = au1xi2s_hw_params, .set_fmt = au1xi2s_set_fmt, + .auto_selectable_formats = &au1xi2s_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver au1xi2s_dai_driver = { diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bf59105fcb7a..72aa5177147f 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, return 0; } +static const u64 au1xpsc_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { .startup = au1xpsc_i2s_startup, .trigger = au1xpsc_i2s_trigger, .hw_params = au1xpsc_i2s_hw_params, .set_fmt = au1xpsc_i2s_set_fmt, + .auto_selectable_formats = &au1xpsc_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { From 9626075249c5ec181a166472c0a5c3620e2ad72d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:00:37 +0000 Subject: [PATCH 503/791] ASoC: bcm: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87tsptjgwa.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/bcm/bcm2835-i2s.c | 13 +++++++++++++ sound/soc/bcm/cygnus-ssp.c | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 87d2f06c2f53..208977828254 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const u64 bcm2835_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .probe = bcm2835_i2s_dai_probe, .startup = bcm2835_i2s_startup, @@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, + .auto_selectable_formats = &bcm2835_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver bcm2835_i2s_dai = { diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 47706ae0a31f..753021789a52 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) #define cygnus_ssp_resume NULL #endif +static const u64 cygnus_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B; + static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .startup = cygnus_ssp_startup, .shutdown = cygnus_ssp_shutdown, @@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .set_fmt = cygnus_ssp_set_fmt, .set_sysclk = cygnus_ssp_set_sysclk, .set_tdm_slot = cygnus_set_dai_tdm_slot, + .auto_selectable_formats = &cygnus_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { From 01856b7ac984390302c3450b067aca97da7142e3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Jul 2026 01:00:42 +0000 Subject: [PATCH 504/791] ASoC: cirrus: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87se5djgw5.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/cirrus/ep93xx-i2s.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 5dba741594fa..8195ed7c6dd6 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) #define ep93xx_i2s_resume NULL #endif +static const u64 ep93xx_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { .probe = ep93xx_i2s_dai_probe, .startup = ep93xx_i2s_startup, @@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { .hw_params = ep93xx_i2s_hw_params, .set_sysclk = ep93xx_i2s_set_sysclk, .set_fmt = ep93xx_i2s_set_dai_fmt, + .auto_selectable_formats = &ep93xx_selectable_formats, + .num_auto_selectable_formats = 1, }; #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) From ae788eeb17dbcdbbe6fcdaf69f2650a71d001837 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 30 Jul 2026 16:54:04 +0700 Subject: [PATCH 505/791] ASoC: spear: spdif_in: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260730095407.33894-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index b31b120a85de..bbe016ed030e 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) host->io_base = io_base; host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) { - dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); + if (host->irq < 0) return host->irq; - } host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) @@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, "spdif-in", host); - if (ret) { - dev_warn(&pdev->dev, "request_irq failed\n"); + if (ret) return ret; - } ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, &spdif_in_dai, 1); From c6c14a9915d512252d79aa6d3217e76b4143cc8b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 30 Jul 2026 16:54:05 +0700 Subject: [PATCH 506/791] ASoC: spear: spdif_in: Switch to snd_soc_dai_dma_data_set_capture() Replace the legacy direct access to dai->capture_dma_data with snd_soc_dai_dma_data_set_capture(). The capture_dma_data field no longer exists in struct snd_soc_dai, making the previous implementation incompatible with current ASoC APIs. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260730095407.33894-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index bbe016ed030e..d571055bfc6b 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); host->dma_params_rx.filter_data = &host->dma_params; - dai->capture_dma_data = &host->dma_params_rx; + snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); return 0; } From 9f2ba3a104499d97aa7ffc52c81129b16d968bf8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 30 Jul 2026 16:54:06 +0700 Subject: [PATCH 507/791] ASoC: spear: spdif_in: Move DAI probe callback to snd_soc_dai_ops The .probe callback is no longer part of struct snd_soc_dai_driver and is now provided through struct snd_soc_dai_ops. Move spdif_in_dai_probe() accordingly so the driver follows the current ASoC API and builds correctly on modern kernels. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260730095407.33894-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index d571055bfc6b..dd1e176193af 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops spdif_in_dai_ops = { .shutdown = spdif_in_shutdown, + .probe = spdif_in_dai_probe, .trigger = spdif_in_trigger, .hw_params = spdif_in_hw_params, }; static struct snd_soc_dai_driver spdif_in_dai = { - .probe = spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, From ad95fc2e9b3bad40d6c3c6897ecc5f853b57ef87 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 30 Jul 2026 16:54:07 +0700 Subject: [PATCH 508/791] ASoC: spear: spdif_out: Move DAI probe callback to snd_soc_dai_ops The .probe callback is no longer part of struct snd_soc_dai_driver and is now provided through struct snd_soc_dai_ops. Move spdif_soc_dai_probe() accordingly so the driver follows the current ASoC API and builds correctly on modern kernels. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260730095407.33894-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spear/spdif_out.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index c06f09c646a8..01dc2cef7448 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops spdif_out_dai_ops = { + .probe = spdif_soc_dai_probe, .mute_stream = spdif_mute, .startup = spdif_out_startup, .shutdown = spdif_out_shutdown, @@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { SNDRV_PCM_RATE_192000), .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .probe = spdif_soc_dai_probe, .ops = &spdif_out_dai_ops, }; From 5ba790f097f6208fe1a1f840868dab0f7ae04a04 Mon Sep 17 00:00:00 2001 From: Yu-Hsuan Hsu Date: Fri, 31 Jul 2026 07:39:35 +0000 Subject: [PATCH 509/791] ALSA: aloop: Fix spinlock deadlock in loopback_hrtimer_stop() In loopback_hrtimer_stop(), calling hrtimer_cancel() while holding cable->lock triggers an AB-BA spinlock deadlock if the hrtimer softirq is executing concurrently on another CPU: 1) CPU A runs loopback_trigger(STOP), acquires spin_lock(&cable->lock), and calls hrtimer_cancel(). Since hrtimer_cancel() is synchronous, it spins waiting for the executing callback to complete before returning. 2) CPU B executes loopback_hrtimer_function(), which immediately tries to acquire spin_lock(&cable->lock). This mutual dependency leads to a CPU hard lockup and NMI watchdog panic when multiple streams start and stop concurrently with small period sizes. Replace hrtimer_cancel() in loopback_hrtimer_stop() with the non-blocking hrtimer_try_to_cancel(), matching the behavior of jiffies timers (timer_delete vs timer_delete_sync). If try_to_cancel returns -1 because the handler is running, CPU A releases cable->lock cleanly. When the running handler subsequently acquires cable->lock, it observes that the stream is no longer in running state (cleared by trigger STOP) and terminates without re-arming the timer. Synchronous hrtimer_cancel() remains preserved in loopback_hrtimer_stop_sync() where cable->lock is not held. Fixes: bf08a5f698dc ("ALSA: aloop: Add 'hrtimer' option to timer_source") Signed-off-by: Yu-Hsuan Hsu Link: https://patch.msgid.link/20260731074255.1513402-1-yuhsuan@chromium.org Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 7ebe8e5c9303..d520d83c2577 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -303,7 +303,7 @@ static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm) /* call in cable->lock */ static inline int loopback_hrtimer_stop(struct loopback_pcm *dpcm) { - hrtimer_cancel(&dpcm->hrtimer); + hrtimer_try_to_cancel(&dpcm->hrtimer); return 0; } From a9fa2a016e805504223abf8c9b373ac9f7bfb9b3 Mon Sep 17 00:00:00 2001 From: Mauricio Orozco Date: Wed, 29 Jul 2026 22:34:55 -0500 Subject: [PATCH 510/791] ALSA: hda/realtek: Add quirk for ASUS VivoBook M515DA/X515DAP The ASUS VivoBook M515DA/X515DAP (subsystem ID 1043:1e3e) requires the ALC256_FIXUP_ASUS_MIC_NO_PRESENCE fixup to enable the internal microphone. Without this quirk, the internal microphone captures only silence under Linux, while it works correctly under Windows. The fix has been verified on real hardware. Tested on an ASUS VivoBook M515DA/X515DAP running Linux Mint 22.3 with Ubuntu HWE kernel 7.0.0-28. Signed-off-by: Mauricio Orozco Link: https://patch.msgid.link/20260730033506.8958-1-mauoro3@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index f7877127e0e4..ce92463e18f9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7689,6 +7689,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1e3e, "ASUS VivoBook M515DA/X515DAP", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), From 186d4adbb40138e7cb7cffc87a81e95630ced123 Mon Sep 17 00:00:00 2001 From: Sean Rhodes Date: Fri, 31 Jul 2026 22:13:08 +0100 Subject: [PATCH 511/791] ALSA: hda/realtek: Limit Star Labs internal mic boost The 30 dB internal mic boost is too high for laptops, especially with fans. Limit Star Labs internal mic boost to 10 dB. Signed-off-by: Sean Rhodes Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/be87292613b24150d6321adac102b4b25d00e9e6.1785532385.git.sean@starlabs.systems --- sound/hda/codecs/realtek/alc269.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index ce92463e18f9..3344a948a060 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4239,6 +4239,7 @@ enum { ALC245_FIXUP_CLEVO_NOISY_MIC, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, + ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST, ALC233_FIXUP_STARLABS_STARFIGHTER, ALC294_FIXUP_BASS_SPEAKER_15, ALC283_FIXUP_DELL_HP_RESUME, @@ -6812,6 +6813,10 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + }, [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, @@ -8245,6 +8250,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK_VENDOR(0x2145, "Star Labs", ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST), {} }; From cd401c70df472d3eddd0b6b055726a03c212181a Mon Sep 17 00:00:00 2001 From: Sean Rhodes Date: Fri, 31 Jul 2026 22:13:09 +0100 Subject: [PATCH 512/791] ALSA: hda/realtek: Add StarFighter HDA SSID Support the new StarFighter HDA SSID while keeping the existing SSID chained to the same quirk until the new match reaches backports. Signed-off-by: Sean Rhodes Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/06865eaedf3de8dff199e9aa7e86cd135572f20f.1785532385.git.sean@starlabs.systems --- sound/hda/codecs/realtek/alc269.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 3344a948a060..95385007234d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6820,6 +6820,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, + .chained = true, + .chain_id = ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST, }, [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, @@ -8167,6 +8169,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x2145, 0x0001, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), From db6c95bb2c314b8147e8491553da9622010899b9 Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Sat, 1 Aug 2026 10:28:31 +0800 Subject: [PATCH 513/791] ALSA: hda/tas2781: Add new quirk for HP new project (Messi) Add new vendor_id and subsystem_id in quirk for HP new project (Messi). Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20260801022831.1241-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 95385007234d..27c19cdeb6eb 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7584,6 +7584,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f7e, "HP Messi", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), From 8177480d9976dd0a19a4ebc10921de30e834d33a Mon Sep 17 00:00:00 2001 From: Aaron Fan Date: Sun, 2 Aug 2026 01:57:27 -0400 Subject: [PATCH 514/791] ALSA: hda/realtek: Add quirk for LG gram 16 (16Z90TR) The LG gram 16 (16Z90TR, SSID 1854:0554) drives its internal speakers through Samsung-style smart amplifiers on an ALC298. Nothing initialises them, so the internal speakers are silent after a cold boot, while headphones, HDMI and the microphones work. A warm reset leaves the amps initialised, which masks the problem: rebooting gives working speakers, a cold boot does not, with a bit-identical kernel log in both cases. Dumping the codec's processing coefficients in the two states shows the difference confined to COEF 0x22/0x23/0x25/0x26. COEF 0x22, the amp select register written by alc298_samsung_v2_init_amps(), reads 0x39 when the speakers work and 0x00 after a cold boot. 0x39 is the second entry of alc298_samsung_v2_amp_desc_tbl[], so two amps are in use. Verified with hda_model=alc298-samsung-amp-v2-2-amps, which selects the same fixup: the internal speakers work from a cold boot and COEF 0x22 reads 0x39. Signed-off-by: Aaron Fan Link: https://patch.msgid.link/20260802055818.7389-1-aaronfan404@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 27c19cdeb6eb..72cb193ddf61 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8123,6 +8123,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14), + SND_PCI_QUIRK(0x1854, 0x0554, "LG gram 16 (16Z90TR)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), From 6bd8a57c044fac5ed23e40a5bf24664fc5498bd6 Mon Sep 17 00:00:00 2001 From: Jeremie Pardou Date: Sun, 2 Aug 2026 21:48:32 +0200 Subject: [PATCH 515/791] ALSA: hda/realtek: Enable jack detection on Minisforum AI X1 Pro The firmware of the Minisforum AI X1 Pro leaves the headphone jack detector reset bit asserted on its ALC245 codec. As a result, pin sense on NID 0x21 always reports the jack as absent. Clear only the Reset HP JD bit during codec initialization. Preserve the remaining coefficient bits. This makes pin sense and the generic HDA auto-mute logic work normally. Apply the fixup at INIT to also reapply the setting after codec reinitialization and resume. Tested on a Minisforum AI X1 Pro with codec 0x10ec0245 and subsystem 0x1f4cb020 using Ubuntu 26.04 kernel 7.0.0-28-generic. Signed-off-by: Jeremie Pardou Link: https://patch.msgid.link/20260802194832.49393-1-jrmi@jeremiez.net Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 72cb193ddf61..e9b0f3aa9776 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1398,6 +1398,15 @@ static void alc245_fixup_hp_gpio_led(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0, 0x04); } +static void alc245_fixup_minisforum_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + if (action == HDA_FIXUP_ACT_INIT) + /* Clear Reset HP JD while preserving the other coefficient bits. */ + alc_update_coef_idx(codec, 0x4a, BIT(15), 0); +} + /* turn on/off mic-mute LED per capture hook via VREF change */ static int vref_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -4237,6 +4246,7 @@ enum { ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, ALC256_FIXUP_CHROME_BOOK, ALC245_FIXUP_CLEVO_NOISY_MIC, + ALC245_FIXUP_MINISFORUM_JACK_DETECT, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST, @@ -6796,6 +6806,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, }, + [ALC245_FIXUP_MINISFORUM_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_minisforum_jack_detect, + }, [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8167,6 +8181,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1e50, 0x7038, "Positivo DN140", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1ee7, 0x2081, "HONOR MRB-XXX M1020", ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO), + SND_PCI_QUIRK(0x1f4c, 0xb020, "Minisforum AI X1 Pro", + ALC245_FIXUP_MINISFORUM_JACK_DETECT), SND_PCI_QUIRK(0x1f4c, 0xe001, "Minisforum V3 (SE)", ALC245_FIXUP_BASS_HP_DAC), SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), From abea6e6abdbe3a31e8286bee5568b0c6d2d80bc1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 1 Aug 2026 21:54:26 +0200 Subject: [PATCH 516/791] ASoC: dt-bindings: Correct white-space style Correct a few white-space issues, like double space after '=' character, which will be flagged by dt-check-style. No functional changes. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260801195425.234120-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/neofidelity,ntp8918.yaml | 2 +- .../devicetree/bindings/sound/renesas,r9a09g047-sound.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml b/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml index 6946177e391a..99b739595ebf 100644 --- a/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml +++ b/Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml @@ -66,7 +66,7 @@ examples: #sound-dai-cells = <0>; reg = <0x2a>; clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>; - clock-names = "wck", "scl", "bck"; + clock-names = "wck", "scl", "bck"; reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; }; }; diff --git a/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml index d7fa16554698..213c1c40b6f0 100644 --- a/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml @@ -793,7 +793,7 @@ examples: bitclock-master = <&rsnd_endpoint0>; frame-master = <&rsnd_endpoint0>; playback = <&ssi3>, <&src1>, <&dvc1>; - capture = <&ssi4>, <&src0>, <&dvc0>; + capture = <&ssi4>, <&src0>, <&dvc0>; }; }; }; From 22f947d6d795dbe2c393ed3b6460fa34a432fb90 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:50 +0700 Subject: [PATCH 517/791] ALSA: hda/realtek: Remove ALC294_FIXUP_CS35L41_I2C_2 ALC294_FIXUP_CS35L41_I2C_2 is exactly the same as ALC287_FIXUP_CS35L41_I2C_2, so remove the former and move existing devices that previously used ALC294_FIXUP_CS35L41_I2C_2 to ALC287_FIXUP_CS35L41_I2C_2. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-2-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e9b0f3aa9776..f44996869059 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4232,7 +4232,6 @@ enum { ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, - ALC294_FIXUP_CS35L41_I2C_2, ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, ALC256_FIXUP_HEADPHONE_AMP_VOL, ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, @@ -6730,10 +6729,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC289_FIXUP_DUAL_SPK }, - [ALC294_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc256_fixup_acer_sfg16_micmute_led, @@ -7671,7 +7666,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), @@ -7682,7 +7677,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19f4, "ASUS UM3405GA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC294_FIXUP_ASUS_SPI_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), From b2d447288fa78aa3d055e45e5f948807b1ab917f Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:51 +0700 Subject: [PATCH 518/791] ALSA: hda/realtek: Add ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST quirk Add ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST, identical to ALC269_FIXUP_LIMIT_INT_MIC_BOOST. This prepares for removing the chain from ALC269_FIXUP_LIMIT_INT_MIC_BOOST. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-3-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index f44996869059..2efa984a2193 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3975,6 +3975,7 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, + ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ASUS_ZENBOOK, ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -4774,6 +4775,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI, }, + [ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, [ALC269VB_FIXUP_ASUS_ZENBOOK] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, @@ -7930,7 +7937,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), @@ -7946,7 +7953,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2288, "Thinkpad X390", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), @@ -8102,10 +8109,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), - SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), @@ -8118,7 +8125,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), From 8a889cce1c6d736ff1d1c160ae2d1538795b1771 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:52 +0700 Subject: [PATCH 519/791] ALSA: hda/realtek: Unchain ALC269_FIXUP_THINKPAD_ACPI from ALC269_FIXUP_LIMIT_INT_MIC_BOOST After creating ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST, ALC269_FIXUP_LIMIT_INT_MIC_BOOST no longer needs to be chained to ALC269_FIXUP_THINKPAD_ACPI and can be a generic quirk usable by all devices. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-4-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2efa984a2193..c457ee9f61a2 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4771,9 +4771,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + .v.func = alc269_fixup_limit_int_mic_boost }, [ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST] = { .type = HDA_FIXUP_FUNC, From 99289f9e0a9e421981608d2279ccfa229a7b9ced Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:53 +0700 Subject: [PATCH 520/791] ALSA: hda/realtek: Remove ALC233_FIXUP_INTEL_NUC8_BOOST Now that ALC269_FIXUP_LIMIT_INT_MIC_BOOST is no longer chained to ALC269_FIXUP_THINKPAD_ACPI, ALC233_FIXUP_INTEL_NUC8_BOOST and ALC269_FIXUP_LIMIT_INT_MIC_BOOST are both identical. Remove the former and replace it with the latter to avoid redundancy. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-5-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c457ee9f61a2..addea99b3bce 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4157,7 +4157,6 @@ enum { ALC269_FIXUP_LEMOTE_A190X, ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC233_FIXUP_INTEL_NUC8_DMIC, - ALC233_FIXUP_INTEL_NUC8_BOOST, ALC256_FIXUP_INTEL_NUC10, ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, @@ -5149,11 +5148,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, .chained = true, - .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST, - }, - [ALC233_FIXUP_INTEL_NUC8_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, }, [ALC255_FIXUP_DELL_SPK_NOISE] = { .type = HDA_FIXUP_FUNC, From 7eb09a19ec14e94a4ab14220e17261ac49d6bc5e Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:54 +0700 Subject: [PATCH 521/791] ALSA: hda/realtek: Remove ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST Now that ALC269_FIXUP_LIMIT_INT_MIC_BOOST is no longer chained to ALC269_FIXUP_THINKPAD_ACPI, ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST and ALC269_FIXUP_LIMIT_INT_MIC_BOOST are both identical. Remove the former and replace it with the latter to avoid redundancy. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-6-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index addea99b3bce..473422e236e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4248,7 +4248,6 @@ enum { ALC245_FIXUP_MINISFORUM_JACK_DETECT, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, - ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST, ALC233_FIXUP_STARLABS_STARFIGHTER, ALC294_FIXUP_BASS_SPEAKER_15, ALC283_FIXUP_DELL_HP_RESUME, @@ -6822,15 +6821,11 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - }, [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, .chained = true, - .chain_id = ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, }, [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, @@ -8266,7 +8261,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), - SND_PCI_QUIRK_VENDOR(0x2145, "Star Labs", ALC269_FIXUP_STARLABS_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK_VENDOR(0x2145, "Star Labs", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), {} }; From a34038f3b6c2310aa5cc06fe11d517a869afb239 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:55 +0700 Subject: [PATCH 522/791] ALSA: hda/realtek: Add ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1 In preparation for unchaining ALC269_FIXUP_THINKPAD_ACPI from ALC285_FIXUP_SPEAKER2_TO_DAC1, add ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1 as a duplicate of ALC285_FIXUP_SPEAKER2_TO_DAC1. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-7-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 473422e236e9..991136bc43ec 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4044,6 +4044,7 @@ enum { ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC285_FIXUP_SPEAKER2_TO_DAC1, + ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_HEADSET_MIC, ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, @@ -5211,6 +5212,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, + [ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, @@ -8016,7 +8023,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), From 8cd47d65f7b24f9c1482eaba13d89c7f22c91ab6 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:56 +0700 Subject: [PATCH 523/791] ALSA: hda/realtek: Unchain ALC269_FIXUP_THINKPAD_ACPI from ALC285_FIXUP_SPEAKER2_TO_DAC1 Now that ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1 exists, ALC285_FIXUP_SPEAKER2_TO_DAC1 can be unchained from ALC269_FIXUP_THINKPAD_ACPI. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-8-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 991136bc43ec..1e6f3f262e03 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -5209,8 +5209,6 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, [ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, From 621919e960bb92bb9e2445ccc29ebd5a4c07dc37 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:57 +0700 Subject: [PATCH 524/791] ALSA: hda/realtek: Remove ALC294_FIXUP_ASUS_ALLY_SPEAKER ALC294_FIXUP_ASUS_ALLY_SPEAKER is exactly the same as ALC285_FIXUP_SPEAKER2_TO_DAC1. Remove the former to avoid redundancy. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-9-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 1e6f3f262e03..81d535acfd05 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4114,7 +4114,6 @@ enum { ALC294_FIXUP_ASUS_ALLY, ALC294_FIXUP_ASUS_ALLY_PINS, ALC294_FIXUP_ASUS_ALLY_VERBS, - ALC294_FIXUP_ASUS_ALLY_SPEAKER, ALC294_FIXUP_ASUS_HPE, ALC294_FIXUP_ASUS_COEF_1B, ALC294_FIXUP_ASUS_GX502_HP, @@ -5732,11 +5731,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER - }, - [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, + .chain_id = ALC285_FIXUP_SPEAKER2_TO_DAC1 }, [ALC285_FIXUP_THINKPAD_X1_GEN7] = { .type = HDA_FIXUP_FUNC, From 1c03dd434e0a38a1d92fe02d38fa2c019bf807e0 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 3 Aug 2026 16:10:58 +0700 Subject: [PATCH 525/791] ALSA: hda/realtek: Remove ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 and ALC285_FIXUP_SPEAKER2_TO_DAC1 are exactly the same. Remove the former to avoid redundancy. Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260803091102.107570-10-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 81d535acfd05..481335776ba8 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4255,7 +4255,6 @@ enum { ALC274_FIXUP_HP_AIO_BIND_DACS, ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, - ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK, ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, @@ -6853,11 +6852,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, + .chain_id = ALC285_FIXUP_SPEAKER2_TO_DAC1 }, [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, From a6604d9fb21699385b8b5a720c06fa5df04998f5 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 31 Jul 2026 17:15:38 +0700 Subject: [PATCH 526/791] ASoC: spacemit: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Reviewed-by: Troy Mitchell Link: https://patch.msgid.link/20260731101539.36290-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spacemit/k1_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 2751485ac0b0..2d5ea1fd5d49 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -478,7 +478,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->base)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); + return PTR_ERR(i2s->base); i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(i2s->reset)) @@ -495,7 +495,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) &spacemit_i2s_component, dai, 1); if (ret) - return dev_err_probe(i2s->dev, ret, "failed to register component"); + return ret; return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); } From cf07f148068cdfdc29580da445f3cc3ce429f764 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 3 Aug 2026 15:56:12 +0200 Subject: [PATCH 527/791] ASoC: sdw_utils: Use auto-cleanup for put_device() A temporary refcount management of a struct device can be done gracefully with __clean(put_device) for avoiding potential leaks. No functional change but just a code cleanup. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260803135648.917760-1-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 36 ++++++++++------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d4074309fa81..8a07ba2a29e5 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1818,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, int adr_index) { struct sdw_slave *slave; - struct device *sdw_dev; const char *sdw_codec_name; - int ret = 0; int i; if (!aux_codec_name) @@ -1830,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, if (!sdw_codec_name) return -ENOMEM; - sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + struct device *sdw_dev __free(put_device) = + bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); if (!sdw_dev) { dev_err(dev, "codec %s not found\n", sdw_codec_name); return -EINVAL; @@ -1840,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, if (!slave->sdca_data.interface_revision) { dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); - ret = 1; - goto put_dev; + return 1; } for (i = 0; i < slave->sdca_data.num_functions; i++) { const char *fname = slave->sdca_data.function[i].name; - if (fname && strstr(aux_codec_name, fname)) { - ret = 1; - goto put_dev; - } + if (fname && strstr(aux_codec_name, fname)) + return 1; } dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); -put_dev: - put_device(sdw_dev); - return ret; + return 0; } int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, @@ -1957,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; struct sdw_slave *slave; - struct device *sdw_dev; const char *sdw_codec_name; - int ret, i; + int i; adr_end = &adr_dev->endpoints[end_index]; dai_info = &codec_info->dais[adr_end->num]; @@ -1968,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, if (!sdw_codec_name) return -ENOMEM; - sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + struct device *sdw_dev __free(put_device) = + bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); if (!sdw_dev) { dev_err(dev, "codec %s not found\n", sdw_codec_name); return -EINVAL; @@ -1979,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, /* Make sure BIOS provides SDCA properties */ if (!slave->sdca_data.interface_revision) { dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); - ret = 1; - goto put_device; + return 1; } for (i = 0; i < slave->sdca_data.num_functions; i++) { @@ -1989,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, if (dai_type == dai_info->dai_type) { dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", dai_type, slave->sdca_data.function[i].name); - ret = 1; - goto put_device; + return 1; } } @@ -1998,11 +1990,7 @@ static int is_sdca_endpoint_present(struct device *dev, "SDCA device function for DAI type %d not supported, skip endpoint\n", dai_info->dai_type); - ret = 0; - -put_device: - put_device(sdw_dev); - return ret; + return 0; } int asoc_sdw_parse_sdw_endpoints(struct device *dev, From dc9edf5878286beb140c401f4e0c3549d529049a Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 3 Aug 2026 15:07:52 -0700 Subject: [PATCH 528/791] ALSA: hda/core: Log stream DMA errors on interrupt The stream descriptor status register reports FIFO and descriptor errors, but these are currently cleared silently along with the rest of the interrupt status. Log them, rate-limited, so DMA problems are visible instead of only manifesting as audible glitches. Observed on some AMD GPU HDMI audio controllers under specific low power circumstances. Signed-off-by: Arun Raghavan Cc: Arun Raghavan Link: https://patch.msgid.link/20260803-master-v1-1-9bcedb736978@valvesoftware.com Signed-off-by: Takashi Iwai --- sound/hda/core/controller.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index 32048ef32d96..78855ac357c6 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -688,6 +688,11 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; + if (sd_status & (SD_INT_FIFO_ERR | SD_INT_DESC_ERR)) { + dev_warn_ratelimited(bus->dev, + "stream %u dma error: 0x%02x\n", + azx_dev->index, sd_status); + } if ((!azx_dev->substream && !azx_dev->cstream) || !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) continue; From 5713fea91f183a3d21966856b41f5fbc358fc1f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 3 Aug 2026 16:00:51 +0200 Subject: [PATCH 529/791] ALSA: hda: aw88399: Use auto-cleanup for put_device() A temporary refcount management of a struct device can be done gracefully with __clean(put_device) for avoiding potential leaks. No functional change but just a code cleanup. Cc: Marco Giunta Reviewed-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260803140100.919071-1-tiwai@suse.de --- sound/hda/codecs/side-codecs/aw88399_hda.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.c b/sound/hda/codecs/side-codecs/aw88399_hda.c index 42de68faddbc..5175d341ecbe 100644 --- a/sound/hda/codecs/side-codecs/aw88399_hda.c +++ b/sound/hda/codecs/side-codecs/aw88399_hda.c @@ -194,7 +194,6 @@ static const struct aw88399_prop_model aw88399_prop_model_table[] = { static int aw88399_hda_acpi_probe(struct aw88399_hda *aw88399) { struct acpi_device *adev; - struct device *physdev; const char *sub; const struct aw88399_prop_model *model; @@ -208,13 +207,13 @@ static int aw88399_hda_acpi_probe(struct aw88399_hda *aw88399) return -ENODEV; } - physdev = get_device(acpi_get_first_physical_node(adev)); + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); if (!physdev) return -ENODEV; sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - put_device(physdev); if (IS_ERR_OR_NULL(sub)) return 0; From 7c458597a2e9f19e51eec83cd60a4e2e4e3c8d55 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 3 Aug 2026 16:00:52 +0200 Subject: [PATCH 530/791] ALSA: hda: tas2781: Use auto-cleanup for put_device() A temporary refcount management of a struct device can be done gracefully with __clean(put_device) for avoiding potential leaks. No functional change but just a code cleanup. Cc: Shenghao Ding Cc: Kevin Lu Cc: Baojun Xu Cc: Sen Wang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260803140100.919071-2-tiwai@suse.de --- sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 6 ++---- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 624db967f17b..6c502c34e015 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -88,7 +88,6 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct gpio_desc *speaker_id; struct acpi_device *adev; - struct device *physdev; LIST_HEAD(resources); const char *sub; uint32_t subid; @@ -101,7 +100,8 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) return -ENODEV; } - physdev = get_device(acpi_get_first_physical_node(adev)); + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); if (ret < 0) { dev_err(p->dev, "Failed to get ACPI resource.\n"); @@ -151,14 +151,12 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) end_2563: acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); - put_device(physdev); acpi_dev_put(adev); return 0; err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); acpi_dev_put(adev); return ret; diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 271c56a79c32..d3b2a746e1a7 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -328,7 +328,6 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, { struct tasdevice_priv *p = tas_hda->priv; struct acpi_device *adev; - struct device *physdev; u32 values[HDA_MAX_COMPONENTS]; const char *property; size_t nval; @@ -341,7 +340,9 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, } strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); + + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); if (!physdev) return -ENODEV; @@ -381,13 +382,11 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, goto err; } } - put_device(physdev); return 0; + err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); - return ret; } From c437a83cc69645edab8e0bfc450a3b706abe759e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 3 Aug 2026 16:00:53 +0200 Subject: [PATCH 531/791] ALSA: hda: cs35l41: Use auto-cleanup for put_device() A temporary refcount management of a struct device can be done gracefully with __clean(put_device) for avoiding potential leaks. No functional change but just a code cleanup. Cc: patches@opensource.cirrus.com Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260803140100.919071-3-tiwai@suse.de --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index c8eba0638ada..62296be45c55 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1915,7 +1915,6 @@ int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) { struct acpi_device *adev; - struct device *physdev; struct spi_device *spi; const char *sub; int ret; @@ -1927,7 +1926,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)); + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); if (!physdev) { acpi_dev_put(adev); return -ENODEV; @@ -1945,13 +1945,9 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i } ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id); - if (ret) { - put_device(physdev); + if (ret) return ret; - } out: - put_device(physdev); - cs35l41->bypass_fw = false; if (cs35l41->control_bus == SPI) { spi = to_spi_device(cs35l41->dev); From 6cd3d2c82651a9e77aa5b1b9c12aa918c0d6c0a9 Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Tue, 4 Aug 2026 20:36:37 +0800 Subject: [PATCH 532/791] ALSA: hda/realtek: ALC269 fixup for Yoga/Legion Mic noise Lenovo Yoga Pro 7 15ASH11 and Legion 7 15ASH11 use the same audio subsystem implementation and support only analog microphone. Limit Amp-In Vals to 0x00 and 0x01 for the internal microphone to reduce recording noise. Values 0x02 and 0x03 introduce significant noise on them. Fixes: 17065203e1bc ("ALSA: hda/realtek:ALC269 fixup for Yoga Pro 7 15ASH11 mic mute LED") Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260804123637.21001-1-xy-jackie@139.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 481335776ba8..bd1958bcf698 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3570,6 +3570,16 @@ static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, } } +static void alc_fixup_yoga_pro7_audio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ + alc285_fixup_thinkpad_x1_gen7(codec, fix, action); + + /* Limit the internal mic boost to 0 or 1 to avoid noise */ + alc269_fixup_limit_int_mic_boost(codec, fix, action); +} + static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -6273,8 +6283,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC287_FIXUP_LENOVO_YOGA_PRO7] = { .type = HDA_FIXUP_FUNC, - /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ - .v.func = alc285_fixup_thinkpad_x1_gen7, + .v.func = alc_fixup_yoga_pro7_audio, .chained = true, .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, }, From b843df095061b152b3ce7450ec293cc65a23bb9f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:17:27 +0300 Subject: [PATCH 533/791] ASoC: SOF: ipc4-topology: Store the params change between input/output of a module Based on the input and output formats we can evaluate what param might be changed by the module instance. If there is a difference between the input rate/channels/format and the output rate/channels/format it means that the module can change one or multiple of the params. Store this information during init for later use. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730121729.18673-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 41 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc4-topology.h | 3 +++ 2 files changed, 44 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 95ad5266b0c6..007c3dd945e0 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) return NULL; } +static void +sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) +{ + struct sof_ipc4_audio_format *fmt; + u32 in_rate, in_channels, in_valid_bits; + u32 out_rate, out_channels, out_valid_bits; + u32 changed_params = 0; + int i, j; + + for (i = 0; i < available_fmt->num_input_formats; i++) { + fmt = &available_fmt->input_pin_fmts[i].audio_fmt; + in_rate = fmt->sampling_frequency; + in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + for (j = 0; j < available_fmt->num_output_formats; j++) { + fmt = &available_fmt->output_pin_fmts[j].audio_fmt; + out_rate = fmt->sampling_frequency; + out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (in_rate != out_rate) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); + if (in_channels != out_channels) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); + if (in_valid_bits != out_valid_bits) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + } + } + + available_fmt->changed_params = changed_params; +} + /** * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples * @scomp: pointer to pointer to SOC component @@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, available_fmt->num_output_formats); } + sof_ipc4_evaluate_params_change(available_fmt); + return 0; err_out: @@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + /* * This callback is used by host copier and module-to-module copier, * and only host copier needs to set gtw_cfg. @@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + ret = sof_update_ipc_object(scomp, &node_type, SOF_COPIER_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(node_type), 1); diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index a289c1d8f3ff..ce086188d6e1 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { * @input_pin_fmts: Available input pin formats * @num_input_formats: Number of input pin formats * @num_output_formats: Number of output pin formats + * @changed_params: Mask of changed params by the module instance between it's + * input and output formts (rate, channels, depth) */ struct sof_ipc4_available_audio_format { struct sof_ipc4_pin_format *output_pin_fmts; struct sof_ipc4_pin_format *input_pin_fmts; u32 num_input_formats; u32 num_output_formats; + u32 changed_params; }; /** From c599f0f52139f44d40a097f4c9fcc8b4a076d896 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:17:28 +0300 Subject: [PATCH 534/791] ASoC: SOF: ipc4-topology: Correct the process module's output lookup The process module can change different parameters in the audio path and this change has to be properly evaluated and applied. In case of playback we are converting from multiple input formats to a single format (or just passing through without change), the output format lookup must be based on the input format. In case of capture, we are converting from a single input format to a format which is to be passed to the FE, we need to use the input parameters and the FE parameters to be able to find the correct format: for those parameters that are modified by the module instance we need to use the FE parameter while for the rest we use the input parameters. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730121729.18673-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 66 +++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 007c3dd945e0..247aabc8cab4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2861,39 +2861,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, if (available_fmt->num_output_formats) { struct sof_ipc4_audio_format *in_fmt; struct sof_ipc4_pin_format *pin_fmt; - u32 out_ref_rate, out_ref_channels; - int out_ref_valid_bits, out_ref_type; + u32 ref_rate, ref_channels; + int ref_valid_bits, ref_type; if (available_fmt->num_input_formats) { + /* + * The process module can change parameters and their operation + * depends on the direction: + * Playback: typically they have single output format. This is + * to 'force' the conversion from input to output. + * Use the input format as reference since the single + * format is going to be picked. + * Capture: typically they have multiple output formats to + * convert from dai (input) to FE (output) parameters. + * Use the input format as base and replace the param + * which is changed by the module with the FE parameter + * Reason: we can have module which changes the + * parameters in path, we cannot use the full + * FE param set for the module output lookup. + */ in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = + ref_rate = in_fmt->sampling_frequency; + ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = + ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); - out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); + ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); } else { /* for modules without input formats, use FE params as reference */ - out_ref_rate = params_rate(fe_params); - out_ref_channels = params_channels(fe_params); + ref_rate = params_rate(fe_params); + ref_channels = params_channels(fe_params); ret = sof_ipc4_get_sample_type(sdev, fe_params); if (ret < 0) return ret; - out_ref_type = (u32)ret; + ref_type = (u32)ret; - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); - if (out_ref_valid_bits < 0) - return out_ref_valid_bits; + ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; } + if (dir == SNDRV_PCM_STREAM_CAPTURE) { + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) + ref_rate = params_rate(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) + ref_channels = params_channels(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { + ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; + + ref_type = sof_ipc4_get_sample_type(sdev, fe_params); + if (ref_type < 0) + return ref_type; + } + } output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &process->base_config, available_fmt, - out_ref_rate, - out_ref_channels, - out_ref_valid_bits, - out_ref_type); + ref_rate, + ref_channels, + ref_valid_bits, + ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2907,9 +2937,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, /* modify the pipeline params with the output format */ ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format, - BIT(SNDRV_PCM_HW_PARAM_FORMAT) | - BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | - BIT(SNDRV_PCM_HW_PARAM_RATE)); + available_fmt->changed_params); if (ret) return ret; } From 94648dc65e66dbdb318a96a132f652a53d62cea1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:17:29 +0300 Subject: [PATCH 535/791] ASoC: SOF: ipc4-topology: Update the pipeline_params of prepared modules If the module in path has been already prepared on a branch type of topology, where the branching happens downstream: A1--> A2 ---> B1 --> B2 ... B-branch |-> C1 --> C2 ... C-branch In this case if B-branch is started then A1/A2 is prepared, but when C-branch starts we still need to refine the parameters up to C1 to arrive with a correct params to configure C1. This branching can happen with copiers process modules. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730121729.18673-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 94 +++++++++++++++++++++++++++++++++-- sound/soc/sof/sof-audio.c | 5 +- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 247aabc8cab4..1abe603f9b37 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2104,10 +2104,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget } static int -sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, - struct snd_pcm_hw_params *fe_params, - struct snd_sof_platform_stream_params *platform_params, - struct snd_pcm_hw_params *pipeline_params, int dir) +sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *pipeline_params) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc4_copier_data *copier_data; + struct sof_ipc4_copier *ipc4_copier; + + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_buffer: + ipc4_copier = swidget->private; + copier_data = &ipc4_copier->data; + break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + struct snd_sof_dai *dai; + + if (pipeline->use_chain_dma) + return 0; + + dai = swidget->private; + + ipc4_copier = (struct sof_ipc4_copier *)dai->private; + copier_data = &ipc4_copier->data; + + break; + } + default: + dev_err(sdev->dev, "unsupported type %d for copier %s", + swidget->id, swidget->widget->name); + return -EINVAL; + } + + /* modify the input params for the next widget */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, + &copier_data->out_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); +} + +static int +_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; @@ -2579,6 +2626,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return 0; } +static int +sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) +{ + if (swidget->prepared) + return sof_ipc4_copier_module_update_params(swidget, + pipeline_params); + + return _sof_ipc4_prepare_copier_module(swidget, fe_params, + platform_params, pipeline_params, + dir); +} + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, @@ -2592,6 +2654,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->data.base_config, pipeline_params, @@ -2637,6 +2703,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; + /* Already prepared, nothing to do */ + if (swidget->prepared) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config, pipeline_params, @@ -2683,6 +2753,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int output_fmt_index, input_fmt_index; + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->data.base_config, pipeline_params, @@ -2849,6 +2923,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, int ret; if (available_fmt->num_input_formats) { + if (swidget->prepared) { + if (!available_fmt->num_output_formats) + return 0; + + /* modify the pipeline params with the output format */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, + &process->output_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); + } + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config, pipeline_params, diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index acf56607bc9c..a58617d2b824 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -502,9 +502,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget !sof_widget_in_same_direction(swidget, dir)) return 0; - /* skip widgets already prepared or aggregated DAI widgets*/ - if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || - is_aggregated_dai(swidget)) + /* skip widgets aggregated DAI widgets */ + if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) goto sink_prepare; /* prepare the source widget */ From 67605cbf8dfe79f10fc189f6e794d2843e249918 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:01 +0530 Subject: [PATCH 536/791] ASoC: qcom: qdsp6: add topology-driven Audio IF support Add topology parsing and media-format programming for Audio IF source and sink modules. Add the Audio IF module IDs, the required topology tokens, and a dedicated topology loader that stores the parsed interface configuration in the AudioReach module state. Also add the Audio IF media-format path that sends the interface configuration, hardware endpoint media format, and frame-duration parameters for Audio IF modules. This keeps the serial-interface configuration topology-driven while still allowing the machine driver to provide runtime slot and media format settings. The same Audio IF path can then be reused for TDM, PCM, and I2S style backends. The new UAPI tokens (AR_TKN_U16_MODULE_SYNC_SRC=262 through AR_TKN_U8_MODULE_INV_EXT_BIT_CLK=276) are added, together with the value defines used by the sync source, sync mode, data delay, interface mode, bit clock type, and polarity tokens. MODULE_ID_AUDIO_IF_SINK (0x0700117C) and MODULE_ID_AUDIO_IF_SOURCE (0x0700117D) are introduced in this patch. This Module is validated on Hawi and Shikra platforms. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- include/uapi/sound/snd_ar_tokens.h | 93 ++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.c | 93 ++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 90 +++++++++++++++++++++++++ sound/soc/qcom/qdsp6/topology.c | 103 +++++++++++++++++++++++++++++ 4 files changed, 379 insertions(+) diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121..1700e3f5cb64 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -168,6 +168,60 @@ enum ar_event_types { * LOG_WAIT = 0, * LOG_IMMEDIATELY = 1 * + * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source + * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, + * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 + * + * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control + * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, + * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 + * + * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask + * + * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame + * + * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) + * + * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode + * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, + * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, + * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 + * + * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity + * AR_AUDIO_IF_SYNC_NORMAL = 0, + * AR_AUDIO_IF_SYNC_INVERTED = 1 + * + * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync + * AR_AUDIO_IF_DATA_DELAY_NONE = 0, + * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, + * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 + * + * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode + * AR_AUDIO_IF_INTF_MODE_TDM = 0, + * AR_AUDIO_IF_INTF_MODE_PCM = 1, + * AR_AUDIO_IF_INTF_MODE_I2S = 2 + * + * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index + * AR_AUDIO_IF_QAIF = 0, + * AR_AUDIO_IF_QAIF_VA = 1 + * + * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane + * + * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz + * + * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type + * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, + * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, + * AR_AUDIO_IF_BIT_CLK_SKIP = 2 + * + * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * + * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * * %AR_TKN_DAI_INDEX: dai index * */ @@ -240,6 +294,45 @@ enum ar_event_types { #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 #define AR_TKN_U32_MODULE_LOG_MODE 261 +#define AR_TKN_U16_MODULE_SYNC_SRC 262 +#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 +#define AR_TKN_U32_MODULE_SLOT_MASK 264 +#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 +#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 +#define AR_TKN_U16_MODULE_SYNC_MODE 267 +#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 +#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 +#define AR_TKN_U16_MODULE_INTF_MODE 270 +#define AR_TKN_U16_MODULE_QAIF_TYPE 271 +#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 +#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 +#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 +#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 +#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 + +#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 +#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 +#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 +#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 +#define AR_AUDIO_IF_INTF_MODE_TDM 0 +#define AR_AUDIO_IF_INTF_MODE_PCM 1 +#define AR_AUDIO_IF_INTF_MODE_I2S 2 +#define AR_AUDIO_IF_QAIF 0 +#define AR_AUDIO_IF_QAIF_VA 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 +#define AR_AUDIO_IF_SYNC_NORMAL 0 +#define AR_AUDIO_IF_SYNC_INVERTED 1 +#define AR_AUDIO_IF_DATA_DELAY_NONE 0 +#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 +#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 +#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 +#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 +#define AR_AUDIO_IF_BIT_CLK_SKIP 2 +#define AR_AUDIO_IF_CLK_NORMAL 0 +#define AR_AUDIO_IF_CLK_INVERTED 1 + #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 struct audioreach_module_priv_data { __le32 size; /* size in bytes of the array, including all elements */ diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 0cc840aca69d..cce0ad31ff0c 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) +struct apm_audio_if_module_intf_cfg { + struct apm_module_param_data param_data; + struct param_id_audio_if_intf_cfg cfg; +} __packed; + +#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) + struct apm_module_hw_ep_mf_cfg { struct apm_module_param_data param_data; struct param_id_hw_ep_mf mf; @@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) +struct apm_module_hw_ep_frame_duration_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_frame_duration frame_duration; +} __packed; + +#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) + struct apm_module_hw_ep_power_mode_cfg { struct apm_module_param_data param_data; struct param_id_hw_ep_power_mode_cfg power_mode; @@ -1052,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, return q6apm_send_cmd_sync(graph->apm, pkt, 0); } +static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) +{ + struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; + struct apm_module_param_data *param_data; + struct apm_audio_if_module_intf_cfg *intf_cfg; + struct apm_module_hw_ep_mf_cfg *hw_cfg; + int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; + int ep_sz = APM_HW_EP_CFG_PSIZE; + int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; + int size = ic_sz + ep_sz + fd_sz; + u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; + u16 nslots_per_frame = cfg->nslots_per_frame ? + (u16)cfg->nslots_per_frame : module->nslots_per_frame; + u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; + void *p; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + intf_cfg = p; + + param_data = &intf_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; + param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; + intf_cfg->cfg.qaif_type = module->qaif_type; + intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; + intf_cfg->cfg.intf_mode = module->intf_mode; + intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; + intf_cfg->cfg.active_slot_mask = slot_mask; + intf_cfg->cfg.nslots_per_frame = nslots_per_frame; + intf_cfg->cfg.slot_width = slot_width; + intf_cfg->cfg.active_lane_mask = module->active_lane_mask; + intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; + intf_cfg->cfg.frame_sync_src = module->sync_src; + intf_cfg->cfg.frame_sync_mode = module->sync_mode; + intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; + intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; + intf_cfg->cfg.bit_clk_type = module->bit_clk_type; + intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; + intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; + + p += ic_sz; + hw_cfg = p; + param_data = &hw_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_MF_CFG; + param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; + + hw_cfg->mf.sample_rate = cfg->sample_rate; + hw_cfg->mf.bit_width = cfg->bit_width; + hw_cfg->mf.num_channels = cfg->num_channels; + hw_cfg->mf.data_format = module->data_format; + + p += ep_sz; + fd_cfg = p; + param_data = &fd_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; + param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; + fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; + fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; + fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; + fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); +} + static int audioreach_logging_set_media_format(struct q6apm_graph *graph, const struct audioreach_module *module) { @@ -1438,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, if (!rc) rc = audioreach_module_enable(graph, module, true); break; + case MODULE_ID_AUDIO_IF_SOURCE: + case MODULE_ID_AUDIO_IF_SINK: + rc = audioreach_audio_if_set_media_format(graph, module, cfg); + break; default: rc = 0; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 62a2fd79bbcb..35541b2d8c99 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -36,6 +36,8 @@ struct q6apm_graph; #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 #define MODULE_ID_OPUS_DEC 0x07001174 +#define MODULE_ID_AUDIO_IF_SINK 0x0700117C +#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D #define APM_CMD_GET_SPF_STATE 0x01001021 #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 @@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { #define PORT_ID_I2S_OUPUT 1 #define I2S_STACK_SIZE 2048 +#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 + +/* + * struct param_id_audio_if_intf_cfg - Audio interface configuration + * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) + * @intf_idx: Interface instance index + * @intf_mode: Interface operating mode (TDM/PCM/I2S) + * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters + * @active_slot_mask: Bitmask indicating active slots + * @nslots_per_frame: Number of slots per audio frame + * @slot_width: Width of each slot in bits + * @active_lane_mask: Bitmask of active data lanes + * @frame_sync_rate: Frame sync rate in Hz + * @frame_sync_src: Frame sync source selection + * @frame_sync_mode: Frame sync mode configuration + * @invert_frame_sync_pulse: Invert frame sync polarity when set + * @frame_sync_data_delay: Data delay from frame sync in bit clocks + * @bit_clk_type: Bit clock type (internal / external) + * @inv_int_bit_clk: Invert internal bit clock when set + * @inv_ext_bit_clk: Invert external bit clock when set + * + * This structure defines configuration parameters for the Qualcomm + * Audio Interface (QAIF) block. It is used to program interface + * characteristics such as slot configuration, clocking and frame + * synchronization behaviour. + */ +struct param_id_audio_if_intf_cfg { + uint16_t qaif_type; + uint16_t intf_idx; + uint16_t intf_mode; + uint16_t ctrl_data_out_enable; + uint32_t active_slot_mask; + uint16_t nslots_per_frame; + uint16_t slot_width; + uint32_t active_lane_mask; + uint32_t frame_sync_rate; + uint16_t frame_sync_src; + uint16_t frame_sync_mode; + uint16_t invert_frame_sync_pulse; + uint16_t frame_sync_data_delay; + uint16_t bit_clk_type; + uint8_t inv_int_bit_clk; + uint8_t inv_ext_bit_clk; +} __packed; + +#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F +#define AUDIO_IF_FRAME_DURATION_US 1000 +#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 +#define AUDIO_IF_FRAME_DURATION_MIN_US 1 +#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 + +/** + * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration + * @frame_duration_in_us: Frame duration in microseconds. + * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. + * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. + * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. + * + * This structure configures the frame duration for the Audio IF hardware + * endpoint and, when enabled, the allowed normalization range. + */ +struct param_id_hw_ep_frame_duration { + uint32_t frame_duration_in_us; + uint32_t allow_frame_duration_normalization; + uint32_t min_normalized_frame_dur_us; + uint32_t max_normalized_frame_dur_us; +} __packed; + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 struct param_id_display_port_intf_cfg { @@ -877,6 +947,23 @@ struct audioreach_module { uint32_t data_format; uint32_t hw_interface_type; + /* Audio IF module (TDM/PCM/I2S) */ + u32 slot_mask; + u32 active_lane_mask; + u32 frame_sync_rate; + u16 qaif_type; + u16 sync_src; + u16 ctrl_data_out_enable; + u16 nslots_per_frame; + u16 slot_width; + u16 intf_mode; + u16 sync_mode; + u16 ctrl_invert_sync_pulse; + u16 ctrl_sync_data_delay; + u16 bit_clk_type; + u8 inv_int_bit_clk; + u8 inv_ext_bit_clk; + /* PCM module specific */ uint32_t interleave_type; @@ -907,6 +994,9 @@ struct audioreach_module_config { u32 channel_allocation; u32 sd_line_mask; int fmt; + u32 slot_mask; + u16 nslots_per_frame; + u16 slot_width; struct snd_codec codec; u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; }; diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 1f69fba6de26..54661bcb006c 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, return 0; } +static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, + const struct snd_soc_tplg_vendor_array *mod_array) +{ + const struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + u32 val; + + mod_elem = mod_array->value; + + while (tkn_count < le32_to_cpu(mod_array->num_elems)) { + val = le32_to_cpu(mod_elem->value); + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_HW_IF_IDX: + mod->hw_interface_idx = val; + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = val; + break; + case AR_TKN_U16_MODULE_SYNC_SRC: + if (val > U16_MAX) + return -EINVAL; + mod->sync_src = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_data_out_enable = (u16)val; + break; + case AR_TKN_U32_MODULE_SLOT_MASK: + mod->slot_mask = val; + break; + case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: + if (val > U16_MAX) + return -EINVAL; + mod->nslots_per_frame = (u16)val; + break; + case AR_TKN_U16_MODULE_SLOT_WIDTH: + if (val > U16_MAX) + return -EINVAL; + mod->slot_width = (u16)val; + break; + case AR_TKN_U16_MODULE_INTF_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->intf_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_SYNC_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->sync_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_invert_sync_pulse = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_sync_data_delay = (u16)val; + break; + case AR_TKN_U16_MODULE_QAIF_TYPE: + if (val > U16_MAX) + return -EINVAL; + mod->qaif_type = (u16)val; + break; + case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: + mod->active_lane_mask = val; + break; + case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: + mod->frame_sync_rate = val; + break; + case AR_TKN_U16_MODULE_BIT_CLK_TYPE: + if (val > U16_MAX) + return -EINVAL; + mod->bit_clk_type = (u16)val; + break; + case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: + if (val > U8_MAX) + return -EINVAL; + mod->inv_int_bit_clk = (u8)val; + break; + case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: + if (val > U8_MAX) + return -EINVAL; + mod->inv_ext_bit_clk = (u8)val; + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + static int audioreach_widget_dp_module_load(struct audioreach_module *mod, const struct snd_soc_tplg_vendor_array *mod_array) { @@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, case MODULE_ID_I2S_SOURCE: audioreach_widget_i2s_module_load(mod, mod_array); break; + case MODULE_ID_AUDIO_IF_SINK: + case MODULE_ID_AUDIO_IF_SOURCE: + ret = audioreach_widget_audio_if_module_load(mod, mod_array); + if (ret) + return ret; + break; case MODULE_ID_DISPLAY_PORT_SINK: audioreach_widget_dp_module_load(mod, mod_array); break; From 4d084017589312a0da2bec3474ef2035c5f4a407 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:02 +0530 Subject: [PATCH 537/791] ASoC: qcom: q6apm-lpass-dais: add TDM DAI operations Add TDM DAI operations to q6apm-lpass-dais so AudioReach TDM backends can be configured through the normal ASoC hw_params and DAI setup flow. The TDM set_tdm_slot() callback validates the supported slot width and slot count, stores the active slot mask in the AudioReach module configuration, and leaves existing DMA, I2S and HDMI paths unchanged. Reuse the existing LPASS child-clock handling for TDM nodes as well as MI2S nodes, since TDM backends also request optional backend clocks through the machine driver set_sysclk() path. Reviewed-by: Srinivas Kandagatla Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index e68e8b000e07..e204fd59e512 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -358,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + unsigned int cap_mask, slot_mask; + + if (slot_width != 16 && slot_width != 32) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); + return -EINVAL; + } + + switch (slots) { + case 2: + case 4: + case 8: + case 16: + cap_mask = GENMASK(slots - 1, 0); + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); + return -EINVAL; + } + + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; + if (slot_mask & ~cap_mask) { + dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", + __func__, slot_mask, slots); + return -EINVAL; + } + + cfg->nslots_per_frame = slots; + cfg->slot_width = slot_width; + cfg->slot_mask = slot_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); + return -EINVAL; + } + + return 0; +} + static const struct snd_soc_dai_ops q6dma_ops = { .prepare = q6apm_lpass_dai_prepare, .startup = q6apm_lpass_dai_startup, @@ -387,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .trigger = q6apm_lpass_dai_trigger, }; +static const struct snd_soc_dai_ops q6tdm_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, +}; + static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .name = "q6apm-be-dai-component", .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, @@ -415,6 +473,7 @@ static int of_q6apm_parse_dai_data(struct device *dev, case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: priv = &data->priv[id]; priv->mclk = of_clk_get_by_name(node, "mclk"); if (IS_ERR(priv->mclk)) { @@ -479,6 +538,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; cfg.q6hdmi_ops = &q6hdmi_ops; + cfg.q6tdm_ops = &q6tdm_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); From ff09adc612103c10cf1922ea2670a29fcf629536 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:03 +0530 Subject: [PATCH 538/791] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks Add the LPASS Audio IF clock IDs used by newer backend interfaces. Platforms using Audio IF module backends request the interface bit clocks through q6prm. Add the Audio IF IBIT and EBIT IDs to the binding header so these clocks can be referenced from device trees. Acked-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-4-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- .../sound/qcom,q6dsp-lpass-ports.h | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index ca84952c3884..2e879a3c09ae 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -237,6 +237,35 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 +#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 +#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 +#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 +#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 +#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 +#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 +#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 +#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 +#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 +#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 +#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 +#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 +#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 +#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 +#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 +#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 +#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 +#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 +#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 +#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 +#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 +#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 +#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 +#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 +#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 +#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 +#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 +#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 + #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 From d6a4a2e190fa0e26e5d9095326f984645b3990fa Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:04 +0530 Subject: [PATCH 539/791] ASoC: qcom: q6prm: add Audio IF clock IDs Add the q6prm clock table entries and internal DSP clock IDs for LPASS Audio IF backend clocks. The public binding IDs map to q6prm DSP clock IDs starting at 0x500 for Audio IF0 IBIT/EBIT. Add the internal definitions and register all Audio IF IBIT and EBIT clocks so machine drivers can request them through the APM clock controller. Reviewed-by: Srinivas Kandagatla Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-5-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6prm-clocks.c | 28 ++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6prm.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 02dad9ee9804..f613e2aee75e 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -64,6 +64,34 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, "LPASS_HW_MACRO"), Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index 938b1bfce287..cca77cd92bc1 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -97,6 +97,35 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 #define Q6PRM_HW_CORE_ID_LPASS 1 From 269b23d5135a050a1706c9e91e95251bf9bf963b Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:05 +0530 Subject: [PATCH 540/791] dt-bindings: sound: qcom,sm8250: allow TDM slot properties Allow standard dai-tdm-slot-* properties in the CPU and codec child nodes of a DAI link. The QCOM machine driver parses these child nodes to configure TDM slots on the active CPU and codec DAIs. The properties are already defined by the common tdm-slot binding, but qcom,sm8250.yaml currently rejects them because the CPU and codec child nodes set additionalProperties: false. Permit dai-tdm-slot-num, dai-tdm-slot-width and dai-tdm-slot-[rt]x-mask there so boards using TDM backends can describe the slot layout without schema warnings. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-6-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,sm8250.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index c1bd19299763..3e7216d61a5e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -95,6 +95,19 @@ patternProperties: sound-dai: maxItems: 1 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + platform: description: Holds subnode which indicates platform dai. type: object @@ -114,6 +127,19 @@ patternProperties: minItems: 1 maxItems: 8 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + required: - link-name - cpu From 1e844ecbc9cbd7c88543c5a84c0d1e434f46fd8b Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:06 +0530 Subject: [PATCH 541/791] ASoC: qcom: common: add DAI-node TDM slot helpers Add common helpers to parse standard dai-tdm-slot-* properties from the CPU and codec child nodes of a backend DAI link and apply the result to the active DAIs. QCOM machine drivers already use qcom_snd_parse_of() to build links from DT, but they lacked a shared helper to translate endpoint TDM properties into snd_soc_dai_set_tdm_slot() calls. Boards therefore had to carry ad hoc parsing or rely on non-standard DT properties. The helpers parse endpoint masks, validate the shared slot count and slot width, and program CPU and codec DAIs with the resulting slot configuration. A cfg-based apply helper is provided for callers that already parsed the DT data and want to avoid a second DT traversal. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-7-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 155 ++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/common.h | 14 ++++ 2 files changed, 169 insertions(+) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index f8782e5cfaae..d231024206db 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; +static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct of_phandle_args args; + int ret; + + if (!card->dev || !card->dev->of_node) + return NULL; + + for_each_available_child_of_node_scoped(card->dev->of_node, np) { + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(np, "cpu"); + + if (!cpu_np) + continue; + + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, + &args); + if (ret) + continue; + + if (args.np == rtd->dai_link->cpus[0].of_node && + args.args_count == 1 && args.args[0] == cpu_dai->id) { + of_node_put(args.np); + return of_node_get(np); + } + + of_node_put(args.np); + } + + return NULL; +} + +static int qcom_snd_parse_tdm_slot(struct device_node *np, + struct qcom_snd_tdm_slot_cfg *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + + return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, + &cfg->slots, &cfg->slot_width); +} + +static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + unsigned int slots; + unsigned int slot_width; + + if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) + return -EINVAL; + + if (cpu_cfg->slot_width && codec_cfg->slot_width && + cpu_cfg->slot_width != codec_cfg->slot_width) + return -EINVAL; + + slots = cpu_cfg->slots ?: codec_cfg->slots; + if (!slots) + return 0; + + slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; + if (!slot_width) + return -EINVAL; + + cpu_cfg->slots = slots; + codec_cfg->slots = slots; + cpu_cfg->slot_width = slot_width; + codec_cfg->slot_width = slot_width; + + return 0; +} + +static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); + int ret; + + if (!link_np) + return -EINVAL; + + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(link_np, "cpu"); + struct device_node *codec_np __free(device_node) = + of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) + return -EINVAL; + + ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); + if (ret) + return ret; + + return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); +} + +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + int ret; + + ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); + if (ret) + return ret; + + return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); +} +EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); + +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + int i; + int ret; + + if (!cpu_cfg->slots) + return 0; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, + cpu_cfg->slots, cpu_cfg->slot_width); + if (ret) + return ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + codec_cfg->tx_mask, + codec_cfg->rx_mask, + codec_cfg->slots, + codec_cfg->slot_width); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); + +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + int ret; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); +} +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); + int qcom_snd_parse_of(struct snd_soc_card *card) { struct device *dev = card->dev; diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index 48b114eb46a5..c1deac109f24 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -9,7 +9,21 @@ #define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) +struct qcom_snd_tdm_slot_cfg { + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int slots; + unsigned int slot_width; +}; + int qcom_snd_parse_of(struct snd_soc_card *card); +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, From a689ee3b2fcd342333eceb94c65ba808a66f96fd Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 4 Aug 2026 12:33:07 +0530 Subject: [PATCH 542/791] ASoC: qcom: sc8280xp: add TDM hw_params support Add TDM backend handling to the sc8280xp machine driver. Use the common QCOM DAI-node TDM helper to parse the standard DAI TDM slot properties from backend CPU and codec endpoints. Reuse the parsed configuration when programming DAIs so hw_params does not need a second DT traversal. Derive the LPASS backend bit clock from the runtime TDM parameters and request it through the backend child-clock path using LPAIF_MI2S_BCLK. Program codec sysclk in hw_params so codec PLL setup happens before the stream is triggered. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260804070307.117119-8-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index c3ce3e05b260..597c0d887d2f 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -106,6 +106,63 @@ static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) snd_pcm_format_width(params_format(params))); } +static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + unsigned int bclk_freq; + int ret; + int i; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + if (!cpu_cfg.slots) + return 0; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret) + return ret; + + ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret; + + bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); + if (!bclk_freq) + return -EINVAL; + + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", + __func__, ret); + return ret; + } + } + + if (data->priv->codec_sysclk_set) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", + __func__, codec_dai->name, ret); + return ret; + } + } + } + + return 0; +} + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -229,6 +286,8 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return ret; } break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + return sc8280xp_tdm_hw_params(substream, params); default: break; } From b50ecf5873df1ce6ff34f3e3421ebb33750e61f6 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 4 Aug 2026 18:48:28 +0800 Subject: [PATCH 543/791] ASoC: rt766: add RT766/RT767 SDCA driver This patch adds the initial SDCA multi-function codec driver for the RT766 and RT767. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260804104828.557228-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt766-sdca-sdw.c | 339 +++++++ sound/soc/codecs/rt766-sdca-sdw.h | 61 ++ sound/soc/codecs/rt766-sdca.c | 1362 +++++++++++++++++++++++++++++ sound/soc/codecs/rt766-sdca.h | 138 +++ 6 files changed, 1912 insertions(+) create mode 100644 sound/soc/codecs/rt766-sdca-sdw.c create mode 100644 sound/soc/codecs/rt766-sdca-sdw.h create mode 100644 sound/soc/codecs/rt766-sdca.c create mode 100644 sound/soc/codecs/rt766-sdca.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a678e68b0bd2..43162c7d23cf 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT721_SDCA_SDW imply SND_SOC_RT722_SDCA_SDW + imply SND_SOC_RT766_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318 @@ -1984,6 +1985,15 @@ config SND_SOC_RT715_SDCA_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT766_SDCA_SDW + tristate "Realtek RT766 SDCA Codec - SDW" + depends on SOUNDWIRE + depends on SND_SOC_SDCA + select SND_SOC_SDCA_HID + select SND_SOC_SDCA_IRQ + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT9120 tristate "Richtek RT9120 Stereo Class-D Amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2e006f5a8abd..3d122ace75ad 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -285,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o +snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o snd-soc-rt9120-y := rt9120.o snd-soc-rt9123-y := rt9123.o snd-soc-rt9123p-y := rt9123p.o @@ -727,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o +obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c new file mode 100644 index 000000000000..5d60c3bcc6cd --- /dev/null +++ b/sound/soc/codecs/rt766-sdca-sdw.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver +// +// Copyright(c) 2026 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include "rt766-sdca.h" +#include "rt766-sdca-sdw.h" + +#define RT766_PROBE_TIMEOUT 5000 + +static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: + case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: + case 0xc344 ... 0xc345: + case 0xc900: + case 0xc920: + case 0xd540 ... 0xd542: + case 0xf01e: + case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: + case 0x310100: + case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: + case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: + case RT766_MUTE_REG(UAJ, USER_FU41, 1): + case RT766_MUTE_REG(UAJ, USER_FU41, 2): + case RT766_VOLUME_REG(UAJ, USER_FU41, 1): + case RT766_VOLUME_REG(UAJ, USER_FU41, 2): + case RT766_MUTE_REG(UAJ, USER_FU36, 1): + case RT766_MUTE_REG(UAJ, USER_FU36, 2): + case RT766_VOLUME_REG(UAJ, USER_FU36, 1): + case RT766_VOLUME_REG(UAJ, USER_FU36, 2): + case RT766_PDE_REQ_REG(UAJ, PDE47): + case RT766_PDE_REQ_REG(UAJ, PDE34): + case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ + case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ + case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ + case RT766_PDE_REQ_REG(MIC, PDE11): + case RT766_MUTE_REG(MIC, USER_FU113, 1): + case RT766_MUTE_REG(MIC, USER_FU113, 2): + case RT766_MUTE_REG(MIC, USER_FU113, 3): + case RT766_MUTE_REG(MIC, USER_FU113, 4): + case RT766_VOLUME_REG(MIC, USER_FU113, 1): + case RT766_VOLUME_REG(MIC, USER_FU113, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 3): + case RT766_VOLUME_REG(MIC, USER_FU113, 4): + case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ + case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ + case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ + /* 0x40c80080 - 0x40c80098 */ + case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): + case RT766_MUTE_REG(AMP, USER_FU21, 1): + case RT766_MUTE_REG(AMP, USER_FU21, 2): + case RT766_VOLUME_REG(AMP, USER_FU21, 1): + case RT766_VOLUME_REG(AMP, USER_FU21, 2): + case RT766_PDE_REQ_REG(AMP, PDE23): + case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ + case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): + case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): + case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ + case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: + case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: + case 0xc344 ... 0xc345: + case 0xc900: + case 0xc920: + case 0xd540 ... 0xd542: + case 0xf01e: + case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: + case 0x310100: + case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: + case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: + case RT766_FUNC_STATUS_REG(UAJ): + case RT766_PDE_ACTUAL_REG(UAJ, PDE47): + case RT766_PDE_ACTUAL_REG(UAJ, PDE34): + case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): + case RT766_FUNC_STATUS_REG(MIC): + case RT766_PDE_ACTUAL_REG(MIC, PDE11): + case RT766_FUNC_STATUS_REG(HID): + case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): + case RT766_FUNC_STATUS_REG(AMP): + case RT766_PDE_ACTUAL_REG(AMP, PDE23): + case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT766_VOLUME_REG(UAJ, USER_FU41, 1): + case RT766_VOLUME_REG(UAJ, USER_FU41, 2): + case RT766_VOLUME_REG(UAJ, USER_FU36, 1): + case RT766_VOLUME_REG(UAJ, USER_FU36, 2): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): + case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 1): + case RT766_VOLUME_REG(MIC, USER_FU113, 2): + case RT766_VOLUME_REG(MIC, USER_FU113, 3): + case RT766_VOLUME_REG(MIC, USER_FU113, 4): + case RT766_VOLUME_REG(AMP, USER_FU21, 1): + case RT766_VOLUME_REG(AMP, USER_FU21, 2): + return 2; + default: + return 1; + } +} + +static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { + .mbq_size = rt766_sdca_mbq_size, +}; + +static const struct regmap_config rt766_sdca_regmap = { + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt766_sdca_readable_register, + .volatile_reg = rt766_sdca_volatile_register, + .reg_defaults = rt766_sdca_defaults, + .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), + .max_register = SDW_SDCA_MAX_REGISTER, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt766_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt766->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt766->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt766_sdca_jack_init set. + */ + sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, + SDW_SCP_SDCA_INTMASK_SDCA_16); + sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, + SDW_SCP_SDCA_INTMASK_SDCA_24); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt766_sdca_io_init(&slave->dev, slave); +} + +static int rt766_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int ret; + + ret = sdw_slave_read_prop(slave); + if (ret < 0) + return ret; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + /* + * SDCA interrupts are routed through SoundWire domain IRQ. + */ + prop->use_domain_irq = true; + + return 0; +} + +static const struct sdw_slave_ops rt766_sdca_slave_ops = { + .read_prop = rt766_sdca_read_prop, + .update_status = rt766_sdca_update_status, +}; + +static int rt766_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, + &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt766_sdca_init(&slave->dev, regmap, slave); +} + +static void rt766_sdca_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); +} + +static const struct sdw_device_id rt766_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); + +static int rt766_sdca_dev_suspend(struct device *dev) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + + if (!rt766->hw_init) + return 0; + + regcache_cache_only(rt766->regmap, true); + return 0; +} + +static int rt766_sdca_dev_system_suspend(struct device *dev) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt766->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt766->disable_irq_lock); + rt766->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, + SDW_SCP_SDCA_INTMASK_SDCA_16, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, + SDW_SCP_SDCA_INTMASK_SDCA_24, 0); + mutex_unlock(&rt766->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt766_sdca_dev_suspend(dev); +} + +static int rt766_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + int ret; + + if (!rt766->first_hw_init) + return 0; + + if (!slave->unattach_request) { + mutex_lock(&rt766->disable_irq_lock); + if (rt766->disable_irq == true) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); + rt766->disable_irq = false; + } + mutex_unlock(&rt766->disable_irq_lock); + goto regmap_sync; + } + + ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); + if (ret) { + sdw_show_ping_status(slave->bus, true); + return ret; + } + +regmap_sync: + regcache_cache_only(rt766->regmap, false); + ret = regcache_sync(rt766->regmap); + if (ret) { + regcache_cache_only(rt766->regmap, true); + regcache_mark_dirty(rt766->regmap); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rt766_sdca_pm = { + SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) + RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt766_sdca_sdw_driver = { + .driver = { + .name = "rt766-sdca", + .pm = pm_ptr(&rt766_sdca_pm), + }, + .probe = rt766_sdca_sdw_probe, + .remove = rt766_sdca_sdw_remove, + .ops = &rt766_sdca_slave_ops, + .id_table = rt766_sdca_id, +}; +module_sdw_driver(rt766_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h new file mode 100644 index 000000000000..3e923a1ea2f8 --- /dev/null +++ b/sound/soc/codecs/rt766-sdca-sdw.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2026 Realtek Semiconductor Corp. + */ + +#ifndef __RT766_SDW_H__ +#define __RT766_SDW_H__ + +#include +#include + +static const struct reg_default rt766_sdca_defaults[] = { + /* 0x40400289 - 0x4040028a */ + { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, + { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, + /* 0x40400291 - 0x40400292 */ + { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, + { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, + /* 0x40400789 - 0x4040078a */ + { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, + { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, + /* 0x40400791 - 0x40400792 */ + { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, + { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, + { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ + { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ + { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ + { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ + /* 0x40600259 - 0x4060025a */ + { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, + { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, + { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ + + { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ + /* 0x40801809 - 0x4080180c */ + { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, + { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, + /* 0x40801811 - 0x40801814 */ + { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, + { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, + { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ + + /* 0x41000189 - 0x4100018a */ + { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, + { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, + /* 0x41000191 - 0x41000192 */ + { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, + { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, + { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ + { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ + { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ + +}; + +#endif /* __RT766_SDW_H__ */ diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c new file mode 100644 index 000000000000..49ee9cef5c54 --- /dev/null +++ b/sound/soc/codecs/rt766-sdca.c @@ -0,0 +1,1362 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver +// +// Copyright(c) 2026 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt766-sdca.h" +#include "rt-sdw-common.h" + +static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) +{ + struct rt766_sdca_priv *rt766 = interrupt->priv; + struct sdca_entity *ent_hid = interrupt->entity; + unsigned char *buf = NULL; + unsigned int offset, owner, length; + unsigned int det_mode, idx, val; + int ret; + + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), + &det_mode); + if (ret < 0) + goto io_error; + + /* get current UMP message owner */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), + &owner); + if (ret < 0) + goto io_error; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + if (det_mode) { + /* read UMP message length */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), + &length); + if (ret < 0) + goto _end_btn_det_; + + /* read UMP message offset */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), + &offset); + if (ret < 0) + goto _end_btn_det_; + + buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); + if (!buf) { + dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); + goto _end_btn_det_; + } + + for (idx = 0; idx < length; idx++) { + ret = regmap_read(rt766->regmap, + RT766_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (ent_hid) + hid_input_report(ent_hid->hide.hid, HID_INPUT_REPORT, + buf, length, 1); + } + +_end_btn_det_: + if (buf) + devm_kfree(&rt766->slave->dev, buf); + + /* Host is owner, so set back to device */ + if (owner == 0) { + regmap_write(rt766->regmap, + RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); + } + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct rt766_sdca_priv *rt766 = interrupt->priv; + + if (!rt766->hs_jack) + return IRQ_HANDLED; + + if (!rt766->component->card || !rt766->component->card->instantiated) + return IRQ_HANDLED; + + mutex_lock(&rt766->disable_irq_lock); + if (!rt766->disable_irq) + rt766_sdca_btn_detect(interrupt); + mutex_unlock(&rt766->disable_irq_lock); + return IRQ_HANDLED; +} + +static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), + &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt766->jack_type = 0; + break; + case 0x03: + rt766->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt766->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), + det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt766->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct rt766_sdca_priv *rt766 = interrupt->priv; + + if (!rt766->hs_jack) + return IRQ_HANDLED; + + if (!rt766->component->card || !rt766->component->card->instantiated) + return IRQ_HANDLED; + + mutex_lock(&rt766->disable_irq_lock); + if (!rt766->disable_irq) + rt766_sdca_headset_detect(rt766); + mutex_unlock(&rt766->disable_irq_lock); + + dev_dbg(&rt766->slave->dev, + "in %s, jack_type=%d\n", __func__, rt766->jack_type); + + snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); + return IRQ_HANDLED; +} + +static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, + struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info, + bool enabled) +{ + struct device *dev = &rt766->slave->dev; + struct sdca_interrupt *interrupt; + struct sdca_control *control; + struct sdca_entity *entity; + irq_handler_t handler; + int i, j, irq, ret; + + for (i = 0; i < function->num_entities; i++) { + entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + control = &entity->controls[j]; + irq = control->interrupt_position; + + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + handler = rt766_sdca_irq_jd_handler; + break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + handler = rt766_sdca_irq_btn_handler; + break; + default: + continue; + } + + interrupt = &info->irqs[irq]; + + if (enabled) { + ret = sdca_irq_data_populate(dev, rt766->regmap, component, + function, entity, control, + interrupt); + if (ret) + return ret; + + interrupt->priv = rt766; + ret = sdca_irq_request(dev, info, irq, interrupt->name, + handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + interrupt->name, ret); + sdca_irq_cleanup_late(dev, function, info); + return ret; + } + dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); + } else { + sdca_irq_cleanup_late(dev, function, info); + dev_dbg(dev, "Freeing IRQ %d\n", irq); + } + } + } + + return 0; +} + +static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt766->uaj_func_data) { + dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); + return -EINVAL; + } + + rt766->hs_jack = hs_jack; + + if (!rt766->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + /* disable interrupts if hs_jack is not set */ + if (!rt766->hs_jack) { + if (rt766->uaj_func_data) + rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, + rt766->component, rt766->irq_info, false); + + if (rt766->hid_func_data) + rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, + rt766->component, rt766->irq_info, false); + } + + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) +{ + unsigned int fu01_reg, fu02_reg; + unsigned int ch_01, ch_02; + unsigned int ch_mute; + unsigned int fu_reg; + int err, i; + + switch (fu_num) { + case RT766_SDCA_ENT_USER_FU41: + ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU36: + ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU21: + ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; + break; + case RT766_SDCA_ENT_USER_FU113: + for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { + ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; + fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; + err = regmap_write(rt766->regmap, fu_reg, ch_mute); + if (err < 0) + return err; + } + return 0; + } + + fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); + fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); + err = regmap_write(rt766->regmap, fu01_reg, ch_01); + if (err < 0) + return err; + err = regmap_write(rt766->regmap, fu02_reg, ch_02); + if (err < 0) + return err; + + return 0; +} + +static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; + return 0; +} + +static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err; + + if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + if (err < 0) + return err; + + return 1; +} + +static int rt766_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu113_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu113_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + break; + } + return 0; +} + +static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + const unsigned int interval_offset = 0xc0; + unsigned int regvalue, ctl, i; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt766->regmap, p->reg_base + i, ®value); + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + const unsigned int interval_offset = 0xc0; + unsigned int gain_val[4]; + unsigned int i, changed = 0; + unsigned int regvalue[4]; + int err; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + unsigned int i; + + for (i = 0; i < 4; i++) + ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; + return 0; +} + +static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + int err, changed = 0, i; + + for (i = 0; i < 4; i++) { + if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); + if (err < 0) + return err; + return changed; +} + +static int rt766_sdca_fu41_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu41_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu41_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); + break; + } + return 0; +} + +static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdca_function_data *func_data; + unsigned char ps0 = 0x0, ps3 = 0x3; + const struct sdca_entity *entity; + unsigned int pde_req_reg; + int from_ps, to_ps; + int ret; + + pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); + + switch (func_num) { + case RT766_FUNC_NUM_UAJ: + func_data = rt766->uaj_func_data; + break; + case RT766_FUNC_NUM_AMP: + func_data = rt766->sa_func_data; + break; + case RT766_FUNC_NUM_MIC: + func_data = rt766->sm_func_data; + break; + default: + dev_err(component->dev, "%s: unsupported func_num %d\n", + __func__, func_num); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt766->regmap, pde_req_reg, ps0); + from_ps = ps3; + to_ps = ps0; + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt766->regmap, pde_req_reg, ps3); + from_ps = ps0; + to_ps = ps3; + break; + } + + entity = sdca_find_entity_by_label(func_data, pde_ent); + if (!entity) { + dev_err(component->dev, "%s: failed to find entity %s\n", + __func__, pde_ent); + return -EINVAL; + } + + ret = sdca_asoc_pde_poll_actual_ps(component->dev, rt766->regmap, + func_num, + pde_num, + from_ps, to_ps, + entity->pde.max_delay, + entity->pde.num_max_delay); + if (ret) + dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", + __func__, from_ps, to_ps, ret); + + return ret; +} + +static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); +} + +static int rt766_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu36_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu36_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); + break; + } + return 0; +} + +static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); +} + +static int rt766_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt766->fu21_dapm_mute = false; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + break; + case SND_SOC_DAPM_PRE_PMD: + rt766->fu21_dapm_mute = true; + rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); + break; + } + return 0; +} + +static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); +} + +static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return rt766_sdca_pde_event(w, kcontrol, event, + RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); +} + +static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt_sdca_dmic_kctrl_priv *p = + (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static const char * const rt766_rx_data_ch_select[] = { + "L,R", + "R,L", + "L,L", + "R,R", + "L,L+R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, + RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, + rt766_rx_data_ch_select); + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); + +#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ +#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ +#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ +#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ +#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ +#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ +#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ +#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ +#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ + +static const struct snd_kcontrol_new rt766_sdca_controls[] = { + SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), + SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", + RT766_VOLUME_REG(UAJ, USER_FU41, 1), + RT766_VOLUME_REG(UAJ, USER_FU41, 2), + RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), + SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), + SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", + RT766_VOLUME_REG(UAJ, USER_FU36, 1), + RT766_VOLUME_REG(UAJ, USER_FU36, 2), + RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), + SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", + RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), + RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), + RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), + + SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), + SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", + RT766_VOLUME_REG(AMP, USER_FU21, 1), + RT766_VOLUME_REG(AMP, USER_FU21, 2), + RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), + SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), + + RT_SDCA_FU_CTRL("FU113 Capture Switch", + RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, + rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), + RT_SDCA_EXT_TLV("FU113 Capture Volume", + RT766_VOLUME_REG(MIC, USER_FU113, 1), + rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), +}; + +static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { + /* UAJ */ + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde34_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu41_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* AMP */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* DMIC */ + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt766_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt766_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { + { "FU 41", NULL, "DP3RX" }, + { "DP12TX", NULL, "FU 36" }, + { "FU 36", NULL, "PDE 34" }, + { "FU 36", NULL, "MIC2" }, + { "HP", NULL, "PDE 47" }, + { "HP", NULL, "FU 41" }, + + { "FU 21", NULL, "DP1RX" }, + { "FU 21", NULL, "PDE 23" }, + { "SPOL", NULL, "FU 21" }, + { "SPOR", NULL, "FU 21" }, + + {"DP8TX", NULL, "FU 113"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "DMIC1"}, + {"FU 113", NULL, "DMIC2"}, +}; + +static int rt766_sdca_probe(struct snd_soc_component *component) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct device *dev = &rt766->slave->dev; + int ret; + + rt766->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (rt766->uaj_func_data) { + dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); + + rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); + if (IS_ERR(rt766->irq_info)) + return PTR_ERR(rt766->irq_info); + + ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, + component, rt766->irq_info, true); + if (ret < 0) { + dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); + return ret; + } + + if (rt766->hid_func_data) { + ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, + component, rt766->irq_info, true); + if (ret < 0) { + dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +static void rt766_sdca_remove(struct snd_soc_component *component) +{ + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + + sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); + sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { + .probe = rt766_sdca_probe, + .remove = rt766_sdca_remove, + .controls = rt766_sdca_controls, + .num_controls = ARRAY_SIZE(rt766_sdca_controls), + .dapm_widgets = rt766_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), + .dapm_routes = rt766_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), + .set_jack = rt766_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + unsigned int sampling_rate; + int retval, port; + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt766->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT766_AIF1) + port = 3; + else if (dai->id == RT766_AIF2) + port = 1; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT766_AIF1) + port = 12; + else if (dai->id == RT766_AIF3) + port = 8; + else + return -EINVAL; + } + + port_config.num = port; + retval = sdw_stream_add_slave(rt766->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT766_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT766_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT766_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT766_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + switch (dai->id) { + case RT766_AIF1: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + regmap_write(rt766->regmap, + RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + case RT766_AIF2: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + case RT766_AIF3: + regmap_write(rt766->regmap, + RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), + sampling_rate); + break; + default: + dev_err(component->dev, "%s: Wrong DAI id\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt766->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt766->slave, sdw_stream); + return 0; +} + +#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) +#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rt766_sdca_ops = { + .hw_params = rt766_sdca_pcm_hw_params, + .hw_free = rt766_sdca_pcm_hw_free, + .set_stream = rt766_sdca_set_sdw_stream, + .shutdown = rt766_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt766_sdca_dai[] = { + { + .name = "rt766-sdca-aif1", + .id = RT766_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT766_STEREO_RATES, + .formats = RT766_DAC_FORMATS, + }, + .capture = { + .stream_name = "DP12 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT766_STEREO_RATES, + .formats = RT766_ADC_FORMATS, + }, + .ops = &rt766_sdca_ops, + .symmetric_rate = 1, + }, + { + .name = "rt766-sdca-aif2", + .id = RT766_AIF2, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = RT766_STEREO_RATES, + .formats = RT766_DAC_FORMATS, + }, + .ops = &rt766_sdca_ops, + }, + { + .name = "rt766-sdca-aif3", + .id = RT766_AIF3, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT766_STEREO_RATES, + .formats = RT766_ADC_FORMATS, + }, + .ops = &rt766_sdca_ops, + } +}; + +static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, + const char *label) +{ + struct snd_soc_pcm_stream stream; + struct sdca_entity *entity; + int i, ret; + + for (i = 0; i < function->num_entities; i++) { + entity = &function->entities[i]; + + if (strcmp(entity->label, label)) + continue; + + /* Can't check earlier as only terminals have an iot member. */ + if (!entity->iot.is_dataport) + continue; + + ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); + if (ret < 0) { + dev_dbg(dev, "%s: failed to parse rates for entity %s\n", + __func__, entity->label); + return 0; + } + + dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); + } + + return stream.rates; +} + +int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) +{ + struct sdca_function_data *func_data_ptr; + struct snd_soc_dai_driver *dai_drv; + struct rt766_sdca_priv *rt766; + unsigned int rates; + int ret; + int i; + + rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); + if (!rt766) + return -ENOMEM; + + dev_set_drvdata(dev, rt766); + rt766->slave = slave; + rt766->regmap = regmap; + + regcache_cache_only(rt766->regmap, true); + + ret = devm_mutex_init(dev, &rt766->disable_irq_lock); + if (ret < 0) { + dev_err(dev, "Failed to initialize mutex\n"); + return ret; + } + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt766->hw_init = false; + rt766->first_hw_init = false; + rt766->fu41_dapm_mute = true; + rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; + rt766->fu36_dapm_mute = true; + rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; + rt766->fu21_dapm_mute = true; + rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; + rt766->fu113_dapm_mute = true; + rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = + rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; + + dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); + if (!dai_drv) { + dev_err(dev, "Failed to allocate memory for DAI driver\n"); + ret = -ENOMEM; + goto _sdw_init_err_; + } + memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); + + /* get SDCA function data */ + dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); + for (i = 0; i < slave->sdca_data.num_functions; i++) { + func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); + if (!func_data_ptr) { + dev_err(dev, "Failed to allocate memory for function data\n"); + ret = -ENOMEM; + goto _free_dai_drv_; + } + + func_data_ptr->desc = &slave->sdca_data.function[i]; + ret = sdca_parse_function(dev, slave, func_data_ptr); + if (ret) { + devm_kfree(dev, func_data_ptr); + goto _free_dai_drv_; + } + dev_dbg(dev, "Function type=%d, num_entities=%d", + slave->sdca_data.function[i].type, func_data_ptr->num_entities); + + switch (slave->sdca_data.function[i].type) { + case SDCA_FUNCTION_TYPE_UAJ: + rt766->uaj_func_data = func_data_ptr; + /* + * Some machines may only support a subset of the sample rates supported by the codec. + * Therefore, we need to parse the supported sample rates from the DisCo table and + * configure them in the DAI. If the DisCo table does not provide sample rate information, + * we will fall back to the default supported rates defined in the codec driver. + */ + rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); + if (rates) + dai_drv[RT766_DAI_UAJ].playback.rates = rates; + + rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); + if (rates) + dai_drv[RT766_DAI_UAJ].capture.rates = rates; + break; + case SDCA_FUNCTION_TYPE_SMART_AMP: + rt766->sa_func_data = func_data_ptr; + rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); + if (rates) + dai_drv[RT766_DAI_AMP].playback.rates = rates; + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + rt766->sm_func_data = func_data_ptr; + rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); + if (rates) + dai_drv[RT766_DAI_MIC].capture.rates = rates; + break; + case SDCA_FUNCTION_TYPE_HID: + rt766->hid_func_data = func_data_ptr; + break; + default: + dev_dbg(dev, "Unexpected SDCA function type found: %d", + slave->sdca_data.function[i].type); + } + } + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); + if (ret < 0) + goto _free_dai_drv_; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + dev_dbg(dev, "%s\n", __func__); + return 0; + +_free_dai_drv_: + if (dai_drv) + devm_kfree(dev, dai_drv); + +_sdw_init_err_: + return ret; +} + +static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) +{ + struct device *dev = &rt766->slave->dev; + unsigned int func_status_reg; + unsigned int func_status; + int ret; + + switch (func_data->desc->type) { + case SDCA_FUNCTION_TYPE_UAJ: + func_status_reg = RT766_FUNC_STATUS_REG(UAJ); + break; + case SDCA_FUNCTION_TYPE_SMART_AMP: + func_status_reg = RT766_FUNC_STATUS_REG(AMP); + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + func_status_reg = RT766_FUNC_STATUS_REG(MIC); + break; + case SDCA_FUNCTION_TYPE_HID: + func_status_reg = RT766_FUNC_STATUS_REG(HID); + break; + default: + dev_dbg(dev, "Unexpected SDCA function type found: %d", + func_data->desc->type); + return -EINVAL; + } + + regmap_read(rt766->regmap, func_status_reg, &func_status); + dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); + + if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { + ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); + if (ret) { + dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); + goto _func_init_err_; + } + + regmap_write(rt766->regmap, func_status_reg, + SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); + } + + return 0; + +_func_init_err_: + dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); + return ret; +} + +int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); + unsigned int val; + + rt766->disable_irq = false; + + if (rt766->hw_init) + return 0; + + regcache_cache_only(rt766->regmap, false); + if (rt766->first_hw_init) { + regcache_cache_bypass(rt766->regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); + dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); + + /* check function status and initialize if needed */ + if (rt766->uaj_func_data) + rt766_func_initialize(rt766, rt766->uaj_func_data); + if (rt766->sa_func_data) + rt766_func_initialize(rt766, rt766->sa_func_data); + if (rt766->sm_func_data) + rt766_func_initialize(rt766, rt766->sm_func_data); + if (rt766->hid_func_data) + rt766_func_initialize(rt766, rt766->hid_func_data); + + if (rt766->first_hw_init) { + regcache_cache_bypass(rt766->regmap, false); + regcache_mark_dirty(rt766->regmap); + } else { + rt766->first_hw_init = true; + } + + /* Mark Slave initialization complete */ + rt766->hw_init = true; + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h new file mode 100644 index 000000000000..5acdb83a42fb --- /dev/null +++ b/sound/soc/codecs/rt766-sdca.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2026 Realtek Semiconductor Corp. + */ + +#ifndef __RT766_H__ +#define __RT766_H__ + +#include +#include +#include +#include +#include +#include + +struct rt766_sdca_priv { + struct regmap *regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; + int jack_type; + bool fu41_dapm_mute; + bool fu41_mixer_l_mute; + bool fu41_mixer_r_mute; + bool fu113_dapm_mute; + bool fu113_mixer_mute[4]; + bool fu21_dapm_mute; + bool fu21_mixer_l_mute; + bool fu21_mixer_r_mute; + bool fu36_dapm_mute; + bool fu36_mixer_l_mute; + bool fu36_mixer_r_mute; + struct sdca_function_data *uaj_func_data; + struct sdca_function_data *sm_func_data; + struct sdca_function_data *sa_func_data; + struct sdca_function_data *hid_func_data; + struct sdca_interrupt_info *irq_info; +}; + +/* vendor registers */ +#define RT766_VERSION_ID 0xc404 +#define RT766_DEV_ID1 0xc405 +#define RT766_DEV_ID0 0xc406 +#define RT766_BOND_LATCH_ID 0xc407 + +#define RT766_HP_POWER_STATE 0x1000004 +#define RT766_HP_FSM_CTL2_1 0x100000d + +/* MCU Patch address */ +#define RT766_MCU_PATCH_ADDR1_START 0x10010000 +#define RT766_MCU_PATCH_ADDR1_END 0x10011fff +#define RT766_MCU_PATCH_ADDR2_START 0x10020000 +#define RT766_MCU_PATCH_ADDR2_END 0x10023fff + +/* Buffer address for HID */ +#define RT766_BUF_ADDR_HID1 0x44030000 +#define RT766_BUF_ADDR_HID2 0x44030020 + +/* SDCA (Channel) */ +#define RT766_CH_1 0x01 +#define RT766_CH_2 0x02 +#define RT766_CH_3 0x03 +#define RT766_CH_4 0x04 + +/* RT766 SDCA Control - function number */ +#define RT766_FUNC_NUM_UAJ 0x01 +#define RT766_FUNC_NUM_MIC 0x02 +#define RT766_FUNC_NUM_HID 0x03 +#define RT766_FUNC_NUM_AMP 0x04 + +/* RT766 SDCA entity */ +#define RT766_SDCA_ENT_0 0x00 +#define RT766_SDCA_ENT_HID101 0x01 +#define RT766_SDCA_ENT_GE49 0x49 +#define RT766_SDCA_ENT_USER_FU41 0x05 +#define RT766_SDCA_ENT_USER_FU36 0x0f +#define RT766_SDCA_ENT_USER_FU21 0x03 +#define RT766_SDCA_ENT_USER_FU113 0x30 +#define RT766_SDCA_ENT_PDE23 0x33 +#define RT766_SDCA_ENT_PDE47 0x28 +#define RT766_SDCA_ENT_PDE11 0x2a +#define RT766_SDCA_ENT_PDE34 0x29 +#define RT766_SDCA_ENT_CS41 0x01 +#define RT766_SDCA_ENT_CS36 0x11 +#define RT766_SDCA_ENT_CS113 0x12 +#define RT766_SDCA_ENT_CS21 0x21 +#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 +#define RT766_SDCA_ENT_PPU21 0x04 + +/* sample frequency index */ +#define RT766_SDCA_RATE_44100HZ 0x08 +#define RT766_SDCA_RATE_48000HZ 0x09 +#define RT766_SDCA_RATE_96000HZ 0x0b +#define RT766_SDCA_RATE_192000HZ 0x0d + +/* SDCA Register macros */ +#define RT766_MUTE_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) + +#define RT766_VOLUME_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) + +#define RT766_GAIN_REG(func, fu, ch) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) + +#define RT766_PDE_REQ_REG(func, pde) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) + +#define RT766_PDE_ACTUAL_REG(func, pde) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) + +#define RT766_FUNC_STATUS_REG(func) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) + +#define RT766_SDCA_CTL(func, ent, ctl) \ + SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) + +enum { + RT766_AIF1, + RT766_AIF2, + RT766_AIF3, +}; + +enum { + RT766_DAI_UAJ, + RT766_DAI_AMP, + RT766_DAI_MIC, +}; + +int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); +#endif /* __RT766_H__ */ From 431c15610d017a470e488284c3b8f977f35e3309 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Tue, 4 Aug 2026 19:14:33 +0800 Subject: [PATCH 544/791] ASoC: tas2781: add capture_profile_id field and update the tuning_switch function Currently, the TAS2781 SmartAMP driver uses the same profile ID for both playback and capture scenarios (e.g., PDM microphone recording or IV data capture). This makes it impossible to apply different DSP configurations for capture and playback, which is required in real-world tuning and production use cases. With these changes, capture and playback paths now use their own DSP profiles, improving tuning flexibility and avoiding unintended profile conflicts between SmartAMP capture and playback scenarios. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260804111433.1148-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781-dsp.h | 7 +- .../hda/codecs/side-codecs/tas2781_hda_i2c.c | 10 +- .../hda/codecs/side-codecs/tas2781_hda_spi.c | 12 +- sound/soc/codecs/tas2781-fmwlib.c | 6 +- sound/soc/codecs/tas2781-i2c.c | 133 +++++++++++++++++- 5 files changed, 151 insertions(+), 17 deletions(-) diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index dd6ee45ad096..e845687decf9 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -201,6 +201,11 @@ struct tasdevice_rca { int ncfgs; struct tasdevice_config_info **cfg_info; int profile_cfg_id; + /* + * Used among SmartAMP for PDM microphone recording or IV data + * capture. + */ + int capture_profile_id; /* * Since version 0x105, the keyword 'init' was introduced into the * profile, which is used for chip initialization, particularly to @@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); int tasdevice_select_tuningprm_cfg(void *context, int prm, int cfg_no, int rca_conf_no); int tasdevice_prmg_load(void *context, int prm_no); -void tasdevice_tuning_switch(void *context, int state); +void tasdevice_tuning_switch(void *context, int state, bool is_cap); int tas2781_load_calibration(void *context, char *file_name, unsigned short i); diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 624db967f17b..8b08b35b1dde 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -173,13 +173,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) case HDA_GEN_PCM_ACT_OPEN: pm_runtime_get_sync(dev); scoped_guard(mutex, &tas_hda->priv->codec_lock) { - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); tas_hda->priv->playback_started = true; } break; case HDA_GEN_PCM_ACT_CLOSE: scoped_guard(mutex, &tas_hda->priv->codec_lock) { - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); tas_hda->priv->playback_started = false; } @@ -722,7 +722,7 @@ static int tas2781_runtime_suspend(struct device *dev) * Stop the playback if it's unused. */ if (tas_hda->priv->playback_started) { - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); tas_hda->priv->playback_started = false; } @@ -752,7 +752,7 @@ static int tas2781_system_suspend(struct device *dev) /* Shutdown chip before system suspend */ if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 1); + tasdevice_tuning_switch(tas_hda->priv, 1, false); /* * Reset GPIO may be shared, so cannot reset here. @@ -785,7 +785,7 @@ static int tas2781_system_resume(struct device *dev) TASDEVICE_BIN_BLK_PRE_POWER_UP); if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); return 0; } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 4899ea372798..e05f12a0c8bb 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -399,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) pm_runtime_get_sync(dev); guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_hda->priv, 0); + tasdevice_tuning_switch(tas_hda->priv, 0, false); } else if (action == HDA_GEN_PCM_ACT_CLOSE) { guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); pm_runtime_put_autosuspend(dev); } } @@ -847,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); tas_priv->tasdevice[tas_priv->index].cur_book = -1; tas_priv->tasdevice[tas_priv->index].cur_conf = -1; @@ -864,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); + tasdevice_tuning_switch(tas_priv, 0, false); return 0; } @@ -882,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) /* Shutdown chip before system suspend */ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); + tasdevice_tuning_switch(tas_priv, 1, false); return 0; } @@ -917,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; if (tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); + tasdevice_tuning_switch(tas_priv, 0, false); } return ret; diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index dcbeb9618195..11d1c2ac865b 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2797,11 +2797,12 @@ int tasdevice_prmg_load(void *context, int prm_no) } EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); -void tasdevice_tuning_switch(void *context, int state) +void tasdevice_tuning_switch(void *context, int state, bool is_cap) { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; struct tasdevice_fw *tas_fmw = tas_priv->fmw; - int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : + tas_priv->rcabin.profile_cfg_id; /* * Only RCA-based Playback can still work with no dsp program running @@ -2818,7 +2819,6 @@ void tasdevice_tuning_switch(void *context, int state) if (state == 0) { if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { /* dsp mode or tuning mode */ - profile_cfg_id = tas_priv->rcabin.profile_cfg_id; tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog, tas_priv->cur_conf, profile_cfg_id); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 01442fd57d7e..70229e8279a3 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1000,6 +1000,50 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, return ret; } +/** + * tasdevice_get_capture_profile_id - Report current active capture profile + * ID to user space + * @kcontrol: ALSA kcontrol structure passed from ALSA core + * @ucontrol: User-space control element value buffer to write the result back + * + * This function ensures the returned profile ID is always clamped inside the + * valid range advertised by the info callback, preventing accidental invalid + * values from being exposed to applications even if internal driver state is + * temporarily inconsistent. + * + * Returns 0 on successful fill of the control value, no error conditions + * are defined for this getter callback. + */ +static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int user_prof_id = ucontrol->value.integer.value[0]; + unsigned int max_valid_id; + int ret = 0; + + /* + * Align valid range with the bound defined in + * tasdevice_info_profile() + */ + max_valid_id = tas_priv->rcabin.ncfgs - 1; + + /* + * Reject invalid input including zero total configuration edge + * case + */ + if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) + return -EINVAL; + + if (tas_priv->rcabin.capture_profile_id != user_prof_id) { + tas_priv->rcabin.capture_profile_id = user_prof_id; + ret = 1; + } + + return ret; +} + static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1080,6 +1124,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, return 0; } +/** + * tasdevice_get_capture_profile_id - Report current active capture profile + * ID to user space + * @kcontrol: ALSA kcontrol structure passed from ALSA core + * @ucontrol: User-space control element value buffer to write the result back + * + * This function ensures the returned profile ID is always clamped inside the + * valid range advertised by the info callback, preventing accidental invalid + * values from being exposed to applications even if internal driver state is + * temporarily inconsistent. + * + * Returns 0 on successful fill of the control value, no error conditions + * are defined for this getter callback. + */ +static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int max_valid_id, current_prof_id; + + max_valid_id = tas_priv->rcabin.ncfgs > 0 ? + (tas_priv->rcabin.ncfgs - 1U) : 0; + + /* + * Cast current profile id to unsigned to match type with max_valid_id, + * avoid signedness mismatch; + */ + current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; + /* Prevent underflow when there are no loaded capture profiles. */ + ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); + + return 0; +} + static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1122,6 +1201,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) ret = snd_soc_add_component_controls(tas_priv->codec, prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + mix_index = 0; + switch (tas_priv->chip_id) { + case TAS2563: + case TAS2568: + case TAS2570: + case TAS2572: + case TAS2573: + case TAS2574: + case TAS2781: + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the capture profile */ + name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + break; + } + out: return ret; } @@ -1773,7 +1887,22 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, guard(mutex)(&tas_priv->codec_lock); if (event == SND_SOC_DAPM_PRE_PMD) state = 1; - tasdevice_tuning_switch(tas_priv, state); + tasdevice_tuning_switch(tas_priv, state, false); + + return 0; +} + +static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + guard(mutex)(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state, true); return 0; } @@ -1781,7 +1910,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, - 0, 0, tasdevice_dapm_event, + 0, 0, tasdevice_capture_dapm_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), SND_SOC_DAPM_OUTPUT("OUT"), From 99ddfbba9e71617494c14b95ed85b9d25bc3202c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 3 Aug 2026 16:18:23 -0700 Subject: [PATCH 545/791] ASoC: xilinx: replace OF with device handlers All of these usages of OF handlers use the node from the platform_device. Use the device member to simplify slightly. Signed-off-by: Rosen Penev Reviewed-by: Vincenzo Frascino Link: https://patch.msgid.link/20260803231823.95147-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 2 -- sound/soc/xilinx/xlnx_i2s.c | 12 +++++------- sound/soc/xilinx/xlnx_spdif.c | 8 +++----- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 8f7a76758535..55b5b473d85f 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c index 0676da122edd..273258125497 100644 --- a/sound/soc/xilinx/xlnx_i2s.c +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -9,9 +9,8 @@ #include #include -#include -#include #include +#include #include #include @@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) int ret; u32 format; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) @@ -184,13 +182,13 @@ static int xlnx_i2s_probe(struct platform_device *pdev) if (IS_ERR(drv_data->base)) return PTR_ERR(drv_data->base); - ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); + ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); if (ret < 0) return dev_err_probe(dev, ret, "cannot get supported channels\n"); drv_data->channels *= 2; - ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); + ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); if (ret < 0) return dev_err_probe(dev, ret, "cannot get data width\n"); @@ -205,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) return -EINVAL; } - if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { + if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { drv_data->dai_drv.name = "xlnx_i2s_playback"; drv_data->dai_drv.playback.stream_name = "Playback"; drv_data->dai_drv.playback.formats = format; @@ -213,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) drv_data->dai_drv.playback.channels_max = drv_data->channels; drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; - } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { + } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { drv_data->dai_drv.name = "xlnx_i2s_capture"; drv_data->dai_drv.capture.stream_name = "Capture"; drv_data->dai_drv.capture.formats = format; diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index ae05818ba064..a70ca6beb646 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -10,8 +10,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -242,7 +241,6 @@ static int xlnx_spdif_probe(struct platform_device *pdev) struct spdif_dev_data *ctx; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -257,7 +255,7 @@ static int xlnx_spdif_probe(struct platform_device *pdev) if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); - ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); + ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); if (ret < 0) return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); @@ -278,7 +276,7 @@ static int xlnx_spdif_probe(struct platform_device *pdev) dai_drv = &xlnx_spdif_rx_dai; } - ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); + ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); if (ret < 0) return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); From 222e8029a7b08240c035c8522c5c0ec42576959a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 1 Aug 2026 21:09:57 +0200 Subject: [PATCH 546/791] ASoC: wm8940: drop unneeded semicolon When a function-like macro expands to an expression, that expression doesn't need a semicolon after it. All uses have been verified to have their own semicolons. This was found using the following Coccinelle semantic patch: @r@ identifier i : script:ocaml() { String.lowercase_ascii i = i }; expression e; @@ *#define i(...) e; Signed-off-by: Julia Lawall Link: https://patch.msgid.link/20260801191002.1383835-11-Julia.Lawall@inria.fr Signed-off-by: Mark Brown --- sound/soc/codecs/wm8940.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index e631ec072249..229252363188 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { {"ADC", NULL, "Boost Mixer"}, }; -#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); +#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) From 23b64e9b9ba2feb1884386042ddbc4872aeab31b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:20 +0000 Subject: [PATCH 547/791] ASoC: soc-component: move soc_component_field_shift() soc_component_field_shift() is used from snd_soc_component_{read/write}_field(). It is better to located around them. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87wlumrz83.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-component.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 2ce24513fac5..7fbd6a711ed8 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -29,18 +29,6 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, func, component->name, reg); } -static inline int soc_component_field_shift(struct snd_soc_component *component, - unsigned int mask) -{ - if (!mask) { - dev_err(component->dev, "ASoC: error field mask is zero for %s\n", - component->name); - return 0; - } - - return (ffs(mask) - 1); -} - /* * We might want to check substream by using list. * In such case, we can update these macros. @@ -820,6 +808,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); +static inline int soc_component_field_shift(struct snd_soc_component *component, + unsigned int mask) +{ + if (!mask) { + dev_err(component->dev, "ASoC: error field mask is zero for %s\n", + component->name); + return 0; + } + + return (ffs(mask) - 1); +} + /** * snd_soc_component_read_field() - Read register field value * @component: Component to read from From de182d84b3a73f93fbd524843bcf6987bae76120 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:24 +0000 Subject: [PATCH 548/791] ASoC: soc-component: add snd_soc_component_alloc() struct snd_soc_component will be capsuled soon, then, we will can't alloc it. Adds snd_soc_component_alloc() to alloc it. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87v7a6rz80.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 1 + sound/soc/soc-component.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 4b7d7954953d..874559458bd5 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -283,6 +283,7 @@ static inline int snd_soc_component_cache_sync( return regcache_sync(component->regmap); } +struct snd_soc_component *snd_soc_component_alloc(struct device *dev); void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux); int snd_soc_component_init(struct snd_soc_component *component); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 7fbd6a711ed8..60531e9f9f9a 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -29,6 +29,19 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, func, component->name, reg); } +struct snd_soc_component *snd_soc_component_alloc(struct device *dev) +{ + struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); + + if (!component) + return NULL; + + component->dev = dev; + + return component; +} +EXPORT_SYMBOL_GPL(snd_soc_component_alloc); + /* * We might want to check substream by using list. * In such case, we can update these macros. From 3e53dc96a007a40ee9a8b24e5884aa216381964a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:27 +0000 Subject: [PATCH 549/791] ASoC: soc-component: add snd_soc_register_component_{c/d}() We have snd_soc_register_component() (A), but we can't setup component specific setting, like name, etc from driver, because component itself is allocated in that function (x). (A) int snd_soc_register_component(...) { ... (x) component = devm_kzalloc(...); if (!component) return -ENOMEM; (B) ret = snd_soc_component_initialize(...); if (ret < 0) return ret; (C) return snd_soc_add_component(...); } So each driver needs to use snd_soc_component_{initialize/add}() (= B/C) instead of using snd_soc_register_component() (A), but it looks unbalanced with its paired unregiser function. Let's merge (B) and (C) into new register function, and allows component as parameter. We can use both snd_soc_register_component(dev, ...); // already exists snd_soc_register_component(component, ...); // new function Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87tspqrz7w.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 9 ++++++++- sound/soc/soc-core.c | 26 ++++++++++++++++++-------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 10ad80f930c2..cb0de41e2ada 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -453,9 +453,16 @@ int snd_soc_component_initialize(struct snd_soc_component *component, int snd_soc_add_component(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, int num_dai); -int snd_soc_register_component(struct device *dev, +int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); +int snd_soc_register_component_d(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); +#define snd_soc_register_component(x, ...) _Generic((x), \ +struct device * : snd_soc_register_component_d, \ +struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) + int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 44f9bb4473f5..c4c99944d4b6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2780,25 +2780,35 @@ int snd_soc_add_component(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_add_component); -int snd_soc_register_component(struct device *dev, +int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai) { - struct snd_soc_component *component; int ret; - component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); - if (!component) - return -ENOMEM; - - ret = snd_soc_component_initialize(component, component_driver, dev); + ret = snd_soc_component_initialize(component, component_driver, component->dev); if (ret < 0) return ret; return snd_soc_add_component(component, dai_drv, num_dai); } -EXPORT_SYMBOL_GPL(snd_soc_register_component); +EXPORT_SYMBOL_GPL(snd_soc_register_component_c); + +int snd_soc_register_component_d(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *component; + + component = snd_soc_component_alloc(dev); + if (!component) + return -ENOMEM; + + return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); +} +EXPORT_SYMBOL_GPL(snd_soc_register_component_d); /** * snd_soc_unregister_component_by_driver - Unregister component using a given driver From f6cfdd246c256f3f87f50254cd6e105c7568ae57 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:31 +0000 Subject: [PATCH 550/791] ASoC: soc-component: add snd_soc_component_{set_}name() struct snd_soc_component will be capsuled soon, its member will not be able to access from non soc-component.c. Add snd_soc_component_{set_}name() to access name. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87se5arz7s.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 4 ++++ sound/soc/soc-component.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 874559458bd5..a13d5c98e0f3 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -284,6 +284,10 @@ static inline int snd_soc_component_cache_sync( } struct snd_soc_component *snd_soc_component_alloc(struct device *dev); + +void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); +const char *snd_soc_component_name(struct snd_soc_component *component); + void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux); int snd_soc_component_init(struct snd_soc_component *component); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 60531e9f9f9a..5131e2bf3a10 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -42,6 +42,18 @@ struct snd_soc_component *snd_soc_component_alloc(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_component_alloc); +void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) +{ + component->name = name; +} +EXPORT_SYMBOL_GPL(snd_soc_component_set_name); + +const char *snd_soc_component_name(struct snd_soc_component *component) +{ + return component->name; +} +EXPORT_SYMBOL_GPL(snd_soc_component_name); + /* * We might want to check substream by using list. * In such case, we can update these macros. From d99690513a9aff85ad282e5d622844b66471319c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:35 +0000 Subject: [PATCH 551/791] ASoC: soc-component: add snd_soc_component_{set/to}_priv() struct snd_soc_component will be capsuled soon, its member will not be able to access from non soc-component.c. Basically, each drivers are using dev_{set/get}_drvdata() to set own data, but it is not enough. Let's add .priv. Add snd_soc_component_{set/to}_priv() to access priv. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87qzkurz7o.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 6 ++++++ sound/soc/soc-component.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index a13d5c98e0f3..c49b59630101 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -253,6 +253,9 @@ struct snd_soc_component { void *mark_pm; struct dentry *debugfs_root; + + /* Component private data */ + void *priv; }; #define for_each_component_dais(component, dai)\ @@ -288,6 +291,9 @@ struct snd_soc_component *snd_soc_component_alloc(struct device *dev); void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); const char *snd_soc_component_name(struct snd_soc_component *component); +void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); +void *snd_soc_component_to_priv(struct snd_soc_component *component); + void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux); int snd_soc_component_init(struct snd_soc_component *component); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 5131e2bf3a10..dc7d203cb76a 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -54,6 +54,18 @@ const char *snd_soc_component_name(struct snd_soc_component *component) } EXPORT_SYMBOL_GPL(snd_soc_component_name); +void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) +{ + component->priv = priv; +} +EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); + +void *snd_soc_component_to_priv(struct snd_soc_component *component) +{ + return component->priv; +} +EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); + /* * We might want to check substream by using list. * In such case, we can update these macros. From 6105b0c10c57ce93c2a407522c4dbe0885a94253 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:39 +0000 Subject: [PATCH 552/791] ASoC: intel: avs: use snd_soc_register_component() It is calling snd_soc_component_initialize() / snd_soc_add_component(). We can now use snd_soc_register_component() instead. It is using container_of() to get avs_soc_component from component, but will not be able to use it when capsuling has done. We can now use snd_soc_component_to_priv() instead. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87pl0erz7k.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 5 ++--- sound/soc/intel/avs/ipc.c | 2 +- sound/soc/intel/avs/pcm.c | 19 +++++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 0f8ddd0e9e5f..b4f9d66d55ad 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); /* Soc component members */ struct avs_soc_component { - struct snd_soc_component base; + struct snd_soc_component *base; struct avs_tplg *tplg; struct list_head node; }; -#define to_avs_soc_component(comp) \ - container_of(comp, struct avs_soc_component, base) +#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) extern const struct snd_soc_dai_ops avs_dai_fe_ops; diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 71e7997e52c2..39b0de9831da 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -106,7 +106,7 @@ static void avs_dsp_recovery(struct avs_dev *adev) struct snd_soc_pcm_runtime *rtd; struct snd_soc_card *card; - card = acomp->base.card; + card = acomp->base->card; if (!card) continue; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 797b9c9163b4..2b886fae8209 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -958,7 +958,7 @@ static const struct file_operations topology_name_fops = { static int avs_component_load_libraries(struct avs_soc_component *acomp) { struct avs_tplg *tplg = acomp->tplg; - struct avs_dev *adev = to_avs_dev(acomp->base.dev); + struct avs_dev *adev = to_avs_dev(acomp->base->dev); int ret; if (!tplg->num_libs) @@ -1387,25 +1387,28 @@ int avs_register_component(struct device *dev, const char *name, struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) { struct avs_soc_component *acomp; - int ret; + const char *comp_name; acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); if (!acomp) return -ENOMEM; - acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); - if (!acomp->base.name) + acomp->base = snd_soc_component_alloc(dev); + if (!acomp->base) + return -ENOMEM; + + comp_name = devm_kstrdup(dev, name, GFP_KERNEL); + if (!comp_name) return -ENOMEM; INIT_LIST_HEAD(&acomp->node); drv->use_dai_pcm_id = !obsolete_card_names; - ret = snd_soc_component_initialize(&acomp->base, drv, dev); - if (ret < 0) - return ret; + snd_soc_component_set_name(acomp->base, comp_name); + snd_soc_component_set_priv(acomp->base, acomp); - return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); + return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); } static struct snd_soc_dai_driver dmic_cpu_dais[] = { From 82552166455946c95d87fe813789cdd9689008e8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:43 +0000 Subject: [PATCH 553/791] ASoC: intel: avs: probes: use snd_soc_register_component() It is calling snd_soc_component_initialize() / snd_soc_add_component(). We can now use snd_soc_register_component() instead. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87o6fyrz7h.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/probes.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 099119ad28b3..e957ef46198a 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { int avs_register_probe_component(struct avs_dev *adev, const char *name) { struct snd_soc_component *component; - int ret; + const char *comp_name; - component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); + component = snd_soc_component_alloc(adev->dev); if (!component) return -ENOMEM; - component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); - if (!component->name) + comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); + if (!comp_name) return -ENOMEM; - ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); - if (ret) - return ret; + snd_soc_component_set_name(component, comp_name); - return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); + return snd_soc_register_component(component, + &avs_probe_component_driver, + probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); } From c8eaeedcace341eedc712f97aeee3aeb0c9b9f47 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:46 +0000 Subject: [PATCH 554/791] ASoC: intel: catpt: pcm: use snd_soc_register_component() It is calling snd_soc_component_initialize() / snd_soc_add_component(). We can now use snd_soc_register_component() instead. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87mrvirz7d.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index f23c580ba051..bbba0d959ea2 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -1072,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) int catpt_register_plat_component(struct catpt_dev *cdev) { struct snd_soc_component *component; - int ret; - component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); + component = snd_soc_component_alloc(cdev->dev); if (!component) return -ENOMEM; - ret = snd_soc_component_initialize(component, &catpt_comp_driver, - cdev->dev); - if (ret) - return ret; + snd_soc_component_set_name(component, catpt_comp_driver.name); - component->name = catpt_comp_driver.name; - return snd_soc_add_component(component, dai_drivers, - ARRAY_SIZE(dai_drivers)); + return snd_soc_register_component(component, + &catpt_comp_driver, + dai_drivers, ARRAY_SIZE(dai_drivers)); } From 695cc675f0d587822ffed64e46680beaa6541fc2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:50 +0000 Subject: [PATCH 555/791] ASoC: soc-generic-dmaengine-pcm: use snd_soc_register_component() it is calling snd_soc_component_initialize() / snd_soc_add_component(). We can now use snd_soc_register_component() instead. It is using container_of() to get dmaengine_pcm from component, but will not be able to use it when capsuling has done. We can now use snd_soc_component_to_priv() instead. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87ldb2rz79.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/dmaengine_pcm.h | 6 ------ sound/soc/fsl/fsl_asrc_dma.c | 4 +++- sound/soc/soc-generic-dmaengine-pcm.c | 30 ++++++++++++++------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 9472f0a966a2..81422219ed39 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct dmaengine_pcm { struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; const struct snd_dmaengine_pcm_config *config; - struct snd_soc_component component; unsigned int flags; }; - -static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) -{ - return container_of(p, struct dmaengine_pcm, component); -} #endif diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 38f2b7c63133..2f662bdf14d0 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, */ component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); if (component_be) { - be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); + + be_chan = pcm->chan[substream->stream]; tmp_chan = be_chan; } if (!tmp_chan) { diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 98ba9a836936..3b18d90e81c3 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -78,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct dma_slave_config slave_config; int ret; @@ -100,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct device *dma_dev = dmaengine_dma_dev(pcm, substream); struct dma_chan *chan = pcm->chan[substream->stream]; struct snd_dmaengine_dai_dma_data *dma_data; @@ -149,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, static int dmaengine_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct dma_chan *chan = pcm->chan[substream->stream]; int ret; @@ -177,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); struct snd_dmaengine_dai_dma_data *dma_data; if (rtd->dai_link->num_cpus > 1) { @@ -220,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, static int dmaengine_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); const struct snd_dmaengine_pcm_config *config = pcm->config; struct device *dev = component->dev; size_t prealloc_buffer_size; @@ -280,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) return snd_dmaengine_pcm_pointer_no_residue(substream); @@ -294,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, struct iov_iter *iter, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, unsigned long bytes) = pcm->config->process; @@ -463,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = int snd_dmaengine_pcm_register(struct device *dev, const struct snd_dmaengine_pcm_config *config, unsigned int flags) { + struct snd_soc_component *component; const struct snd_soc_component_driver *driver; struct dmaengine_pcm *pcm; int ret; + component = snd_soc_component_alloc(dev); + if (!component) + return -ENOMEM; + pcm = kzalloc_obj(*pcm); if (!pcm) return -ENOMEM; @@ -477,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, pcm->flags = flags; if (config->name) - pcm->component.name = config->name; + snd_soc_component_set_name(component, config->name); + snd_soc_component_set_priv(component, pcm); ret = dmaengine_pcm_request_chan_of(pcm, dev, config); if (ret) @@ -488,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, else driver = &dmaengine_pcm_component; - ret = snd_soc_component_initialize(&pcm->component, driver, dev); - if (ret) - goto err_free_dma; - - ret = snd_soc_add_component(&pcm->component, NULL, 0); + ret = snd_soc_register_component(component, driver, NULL, 0); if (ret) goto err_free_dma; @@ -521,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) if (!component) return; - pcm = soc_component_to_pcm(component); + pcm = snd_soc_component_to_priv(component); snd_soc_unregister_component_by_driver(dev, component->driver); dmaengine_pcm_release_chan(pcm); From 10c062304d2d13c5f8012ce82845b88cfd5fb815 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:53 +0000 Subject: [PATCH 556/791] ASoC: soc-topology-test: use snd_soc_register_component() It is calling snd_soc_component_initialize() / snd_soc_add_component(). We can now use snd_soc_register_component() instead. It is using container_of() to get kunit_soc_component from component, but will not be able to use it when capsuling has done. We can use snd_soc_component_to_priv() instead. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87jyqmrz76.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-topology-test.c | 133 +++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 52 deletions(-) diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c index c8f2ec29e970..346a52d3006a 100644 --- a/sound/soc/soc-topology-test.c +++ b/sound/soc/soc-topology-test.c @@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) struct kunit_soc_component { struct kunit *kunit; int expect; /* what result we expect when loading topology */ - struct snd_soc_component comp; struct snd_soc_card card; struct firmware fw; }; static int d_probe(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); @@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) static void d_remove(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; ret = snd_soc_tplg_component_remove(component); @@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { */ static int d_probe_null_comp(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; /* instead of passing component pointer as first argument, pass NULL here */ @@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) */ static int d_probe_null_fw(struct snd_soc_component *component) { - struct kunit_soc_component *kunit_comp = - container_of(component, struct kunit_soc_component, comp); + struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); int ret; /* instead of passing fw pointer as third argument, pass NULL here */ @@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; int ret; /* prepare */ @@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; struct tplg_tmpl_001 *data; int size; int ret; @@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); /* cleanup */ @@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); snd_soc_unregister_component(test_dev); @@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); + + snd_soc_component_set_priv(component, kunit_comp); + /* run test */ ret = snd_soc_register_card(&kunit_comp->card); if (ret != 0 && ret != -EPROBE_DEFER) KUNIT_FAIL(test, "Failed to register card"); for (i = 0; i < 100; i++) { - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); snd_soc_unregister_component(test_dev); @@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) { struct kunit_soc_component *kunit_comp; + struct snd_soc_component *component; u8 *data; int size; int ret; @@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); kunit_comp->card.fully_routed = true; - /* run test */ - ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); - KUNIT_EXPECT_EQ(test, 0, ret); + component = snd_soc_component_alloc(test_dev); + KUNIT_ASSERT_NOT_NULL(test, component); - ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); + snd_soc_component_set_priv(component, kunit_comp); + + /* run test */ + ret = snd_soc_register_component(component, &test_component, NULL, 0); KUNIT_EXPECT_EQ(test, 0, ret); for (i = 0; i < 100; i++) { From 7050ee33c2db8e96bdbcd97f49a7ffa1d9b3ce1a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:34:57 +0000 Subject: [PATCH 557/791] ASoC: soc-core: makes snd_soc_component_initialize() / snd_soc_add_component() local No one is calling snd_soc_component_initialize() / snd_soc_add_component() calling from driver. Makes them local functions. It renames - snd_soc_add_component() + snd_soc_component_add() Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87ik66rz73.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 6 ------ sound/soc/soc-core.c | 18 ++++++++---------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index cb0de41e2ada..f46b2bc2a022 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -447,12 +447,6 @@ static inline int snd_soc_resume(struct device *dev) } #endif int snd_soc_poweroff(struct device *dev); -int snd_soc_component_initialize(struct snd_soc_component *component, - const struct snd_soc_component_driver *driver, - struct device *dev); -int snd_soc_add_component(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, - int num_dai); int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c4c99944d4b6..ec23ba261897 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2707,9 +2707,9 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) list_del(&component->list); } -int snd_soc_component_initialize(struct snd_soc_component *component, - const struct snd_soc_component_driver *driver, - struct device *dev) +static int soc_component_initialize(struct snd_soc_component *component, + const struct snd_soc_component_driver *driver, + struct device *dev) { component->dapm = snd_soc_dapm_alloc(dev); if (!component->dapm) @@ -2735,11 +2735,10 @@ int snd_soc_component_initialize(struct snd_soc_component *component, return 0; } -EXPORT_SYMBOL_GPL(snd_soc_component_initialize); -int snd_soc_add_component(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, - int num_dai) +static int soc_component_add(struct snd_soc_component *component, + struct snd_soc_dai_driver *dai_drv, + int num_dai) { struct snd_soc_card *card, *c; int ret; @@ -2778,7 +2777,6 @@ int snd_soc_add_component(struct snd_soc_component *component, return ret; } -EXPORT_SYMBOL_GPL(snd_soc_add_component); int snd_soc_register_component_c(struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, @@ -2787,11 +2785,11 @@ int snd_soc_register_component_c(struct snd_soc_component *component, { int ret; - ret = snd_soc_component_initialize(component, component_driver, component->dev); + ret = soc_component_initialize(component, component_driver, component->dev); if (ret < 0) return ret; - return snd_soc_add_component(component, dai_drv, num_dai); + return soc_component_add(component, dai_drv, num_dai); } EXPORT_SYMBOL_GPL(snd_soc_register_component_c); From 157936199385fbd16d5e25152ac07a246e64a6e1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jul 2026 06:35:01 +0000 Subject: [PATCH 558/791] ASoC: soc-core: remove dev from soc_component_initialize() Component already has component->dev. No longer need to set dev on soc_component_initialize(). Remove it. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87h5lqrz6z.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ec23ba261897..b9c70268d49f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2708,9 +2708,10 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) } static int soc_component_initialize(struct snd_soc_component *component, - const struct snd_soc_component_driver *driver, - struct device *dev) + const struct snd_soc_component_driver *driver) { + struct device *dev = component->dev; + component->dapm = snd_soc_dapm_alloc(dev); if (!component->dapm) return -ENOMEM; @@ -2730,7 +2731,6 @@ static int soc_component_initialize(struct snd_soc_component *component, } } - component->dev = dev; component->driver = driver; return 0; @@ -2785,7 +2785,7 @@ int snd_soc_register_component_c(struct snd_soc_component *component, { int ret; - ret = soc_component_initialize(component, component_driver, component->dev); + ret = soc_component_initialize(component, component_driver); if (ret < 0) return ret; From eaf46ee96599e20c56c46401c937102f18e081bf Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Mon, 20 Jul 2026 11:35:04 +0100 Subject: [PATCH 559/791] ALSA: control: tidy up whitespaces Clean up trailing whitespace in preparation for the card components changes. Signed-off-by: Maciej Strozek Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260720103505.1860399-1-mstrozek@opensource.cirrus.com --- sound/core/control_compat.c | 2 +- sound/core/init.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 16bc80555f26..4ad571087ff5 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -417,7 +417,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, break; } return snd_ctl_elem_add(file, data, replace); -} +} enum { SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32), diff --git a/sound/core/init.c b/sound/core/init.c index 8c5850ce08a0..0372756048cd 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -721,7 +721,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, int len, loops; bool is_default = false; char *id; - + copy_valid_id_string(card, src, nid); id = card->id; @@ -1030,7 +1030,7 @@ int __init snd_card_info_init(void) * * Return: Zero otherwise a negative error code. */ - + int snd_component_add(struct snd_card *card, const char *component) { char *ptr; From 86cac980c9668106b32ffca3bda106cbb6d7ac2b Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Mon, 20 Jul 2026 11:35:05 +0100 Subject: [PATCH 560/791] ALSA: control: add ioctl to retrieve full card components The fixed-size components field in SNDRV_CTL_IOCTL_CARD_INFO can be too small on systems with many audio devices. Keep the existing struct snd_ctl_card_info ABI intact and add a new ioctl SNDRV_CTL_IOCTL_CARD_BYTES that carries a variable-length payload selected by a type discriminator. The first defined type SND_CTL_CARD_BTYPE_COMPONENTS returns the full components string. The ioctl is designed to be reused for other variable-length card payloads in the future. The user-space caller may set data_allocated == 0 (or data == NULL) to query the required length; otherwise the kernel copies the payload into the user buffer and writes back the actual length in data_len. When the legacy components field in struct snd_ctl_card_info is truncated, '>' is written just before the NUL terminator to signal to user-space that the full string is available via the new ioctl. card->components is now dynamically allocated and grown in 32 byte increments via krealloc(), capped at 512 bytes. Link: https://github.com/alsa-project/alsa-lib/pull/494 Suggested-by: Jaroslav Kysela Suggested-by: Takashi Iwai Signed-off-by: Maciej Strozek Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260720103505.1860399-2-mstrozek@opensource.cirrus.com --- include/sound/control.h | 3 ++ include/sound/core.h | 4 +-- include/uapi/sound/asound.h | 22 ++++++++++++- sound/core/control.c | 64 +++++++++++++++++++++++++++++++++++-- sound/core/control_compat.c | 1 + sound/core/init.c | 36 ++++++++++++++++++--- 6 files changed, 120 insertions(+), 10 deletions(-) diff --git a/include/sound/control.h b/include/sound/control.h index e07f6b960641..909db0d0485d 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -7,6 +7,7 @@ * Copyright (c) by Jaroslav Kysela */ +#include #include #include #include @@ -167,6 +168,8 @@ snd_ctl_find_id_mixer(struct snd_card *card, const char *name) int snd_ctl_create(struct snd_card *card); +extern struct rw_semaphore snd_ioctl_rwsem; + int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); #ifdef CONFIG_COMPAT diff --git a/include/sound/core.h b/include/sound/core.h index 404785b7d885..2ca24ac7e37f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -108,8 +108,8 @@ struct snd_card { char longname[80]; /* name of this soundcard */ char irq_descr[32]; /* Interrupt description */ char mixername[80]; /* mixer name */ - char components[128]; /* card components delimited with - space */ + char *components; /* card components, space-delimited */ + unsigned int components_alloc_size; /* current allocation size of components */ struct module *module; /* top-level module */ void *private_data; /* private data for soundcard */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index d3ce75ba938a..500599213f93 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -1058,7 +1058,7 @@ struct snd_timer_tread { * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) struct snd_ctl_card_info { int card; /* card number */ @@ -1072,6 +1072,25 @@ struct snd_ctl_card_info { unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */ }; +/* + * Card components can exceed the fixed 128 bytes in snd_ctl_card_info. + * Use SNDRV_CTL_IOCTL_CARD_BYTES with type SND_CTL_CARD_BTYPE_COMPONENTS + * to retrieve the full string. + */ + +/* Type values for struct snd_ctl_card_bytes::type */ +enum { + SND_CTL_CARD_BTYPE_COMPONENTS = 1, /* full card components string */ +}; + +struct snd_ctl_card_bytes { + __u32 type; /* SND_CTL_CARD_BTYPE_* */ + __u32 data_allocated; /* size of @data buffer in bytes */ + __u32 data_len; /* in/out: actual data length in bytes */ + __u32 reserved; /* explicit pad */ + __u64 data; /* user buffer (pointer stored as __u64) */ +}; + typedef int __bitwise snd_ctl_elem_type_t; #define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) /* invalid */ #define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) /* boolean type */ @@ -1198,6 +1217,7 @@ struct snd_ctl_tlv { #define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int) #define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info) +#define SNDRV_CTL_IOCTL_CARD_BYTES _IOWR('U', 0x02, struct snd_ctl_card_bytes) #define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list) #define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info) #define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value) diff --git a/sound/core/control.c b/sound/core/control.c index 1116a40d11ae..78ce7bc936d2 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -38,7 +38,7 @@ struct snd_kctl_ioctl { snd_kctl_ioctl_func_t fioctl; }; -static DECLARE_RWSEM(snd_ioctl_rwsem); +DECLARE_RWSEM(snd_ioctl_rwsem); static DECLARE_RWSEM(snd_ctl_layer_rwsem); static LIST_HEAD(snd_control_ioctls); #ifdef CONFIG_COMPAT @@ -872,23 +872,81 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, { struct snd_ctl_card_info *info __free(kfree) = kzalloc(sizeof(*info), GFP_KERNEL); + ssize_t n; if (! info) return -ENOMEM; + + static_assert(sizeof(info->components) >= 2); + scoped_guard(rwsem_read, &snd_ioctl_rwsem) { + const char *components = card->components; + + if (!components) + components = ""; + info->card = card->number; strscpy(info->id, card->id, sizeof(info->id)); strscpy(info->driver, card->driver, sizeof(info->driver)); strscpy(info->name, card->shortname, sizeof(info->name)); strscpy(info->longname, card->longname, sizeof(info->longname)); strscpy(info->mixername, card->mixername, sizeof(info->mixername)); - strscpy(info->components, card->components, sizeof(info->components)); + n = strscpy(info->components, components, sizeof(info->components)); + if (n < 0) // mark the truncation with '>' before NULL terminator + info->components[sizeof(info->components) - 2] = '>'; } if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) return -EFAULT; return 0; } +static int snd_ctl_card_bytes(struct snd_card *card, + struct snd_ctl_card_bytes *info, + unsigned int __user *data_len_out) +{ + unsigned int data_len; + + switch (info->type) { + case SND_CTL_CARD_BTYPE_COMPONENTS: + scoped_guard(rwsem_read, &snd_ioctl_rwsem) { + const char *components = card->components; + + if (!components) + components = ""; + + data_len = strlen(components) + 1; + + if (!info->data || info->data_allocated == 0) + break; + + if (info->data_allocated < data_len) + return -ENOMEM; + + if (copy_to_user(u64_to_user_ptr(info->data), components, data_len)) + return -EFAULT; + } + break; + default: + return -EINVAL; + } + + if (put_user(data_len, data_len_out)) + return -EFAULT; + + return 0; +} + +static int snd_ctl_card_bytes_user(struct snd_card *card, + struct snd_ctl_card_bytes __user *_info) +{ + struct snd_ctl_card_bytes info; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + + return snd_ctl_card_bytes(card, &info, &_info->data_len); +} + static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list *list) { @@ -1986,6 +2044,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; case SNDRV_CTL_IOCTL_CARD_INFO: return snd_ctl_card_info(card, ctl, cmd, argp); + case SNDRV_CTL_IOCTL_CARD_BYTES: + return snd_ctl_card_bytes_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_LIST: return snd_ctl_elem_list_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 4ad571087ff5..f14d9f5e94be 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -446,6 +446,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns switch (cmd) { case SNDRV_CTL_IOCTL_PVERSION: case SNDRV_CTL_IOCTL_CARD_INFO: + case SNDRV_CTL_IOCTL_CARD_BYTES: case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: case SNDRV_CTL_IOCTL_POWER: case SNDRV_CTL_IOCTL_POWER_STATE: diff --git a/sound/core/init.c b/sound/core/init.c index 0372756048cd..19ec68db561b 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -589,6 +589,9 @@ static int snd_card_do_free(struct snd_card *card) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); #endif snd_device_free_all(card); + kfree(card->components); + card->components = NULL; + card->components_alloc_size = 0; if (card->private_free) card->private_free(card); #ifdef CONFIG_SND_CTL_DEBUG @@ -1035,16 +1038,39 @@ int snd_component_add(struct snd_card *card, const char *component) { char *ptr; int len = strlen(component); + unsigned int cur_len, need_len; - ptr = strstr(card->components, component); - if (ptr != NULL) { - if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ - return 1; + guard(rwsem_write)(&snd_ioctl_rwsem); + + if (card->components) { + ptr = strstr(card->components, component); + if (ptr) { + if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ + return 1; + } + cur_len = strlen(card->components) + 1; + } else { + cur_len = 0; } - if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { + + need_len = cur_len + len + 1; + if (need_len > 512) { snd_BUG(); return -ENOMEM; } + + if (need_len > card->components_alloc_size) { + unsigned int new_alloc = roundup(need_len, 32); + + ptr = krealloc(card->components, new_alloc, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + if (!card->components) + ptr[0] = '\0'; + card->components = ptr; + card->components_alloc_size = new_alloc; + } + if (card->components[0] != '\0') strcat(card->components, " "); strcat(card->components, component); From a478893b59e36cfe7d77a76b352f2db55502e879 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 5 Aug 2026 10:34:23 +0900 Subject: [PATCH 561/791] ALSA: 6fire: bound the MIDI event length from the device usb6fire_comm_receiver_handler() forwards a MIDI event using a length byte the device supplies, with no bound and no check that the transfer delivered that many bytes: if (!urb->status) { if (rt->receiver_buffer[0] == 0x10) /* midi in event */ if (midi_rt) midi_rt->in_received(midi_rt, rt->receiver_buffer + 2, rt->receiver_buffer[1]); } receiver_buffer is a 64-byte kzalloc() buffer (COMM_RECEIVER_BUFSIZE), so only 62 bytes follow the two-byte header. receiver_buffer[1] is a u8 the device chooses, so a device that answers with 0x10 and a length of 0xFF makes snd_rawmidi_receive() read 255 bytes starting two bytes into a 64-byte object. The bytes past the buffer are handed to userspace through the rawmidi read path. urb->actual_length is not consulted either, so a short transfer leaves both the type byte and the length byte at their previous values and the handler acts on stale data. The receiver URB is submitted from usb6fire_comm_init() at probe, so the read happens on plug with no user action; forwarding to userspace also needs a MIDI input substream open, since usb6fire_midi_in_received() only calls snd_rawmidi_receive() when rt->in is set. KASAN on 7.2.0-rc5 (arm64), single packet from an emulated device: BUG: KASAN: slab-out-of-bounds in snd_rawmidi_receive Read of size 255 at addr ffff000009f64682 by task bash/183 __asan_memcpy snd_rawmidi_receive usb6fire_midi_in_received [snd_usb_6fire] usb6fire_comm_receiver_handler [snd_usb_6fire] Allocated by task 11: usb6fire_comm_init [snd_usb_6fire] usb6fire_chip_probe [snd_usb_6fire] The buggy address is located 2 bytes inside of allocated 64-byte region [ffff000009f64680, ffff000009f646c0) Reject the event when the length exceeds the bytes that follow the header, and require the transfer to have delivered the header plus that many bytes. The receiver URB is submitted with a 64-byte transfer_buffer_length, so a genuine device cannot deliver an event longer than those 62 bytes and nothing valid is dropped. Discovered by XBOW, triaged by Baul Lee Fixes: c6d43ba816d1 ("ALSA: usb/6fire - Driver for TerraTec DMX 6Fire USB") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260805013423.38175-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/6fire/comm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index 9a0b85653c61..d3b7cab85699 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handler(struct urb *urb) struct midi_runtime *midi_rt = rt->chip->midi; if (!urb->status) { - if (rt->receiver_buffer[0] == 0x10) /* midi in event */ + u8 len = rt->receiver_buffer[1]; + + if (rt->receiver_buffer[0] == 0x10 && /* midi in event */ + len <= COMM_RECEIVER_BUFSIZE - 2 && + urb->actual_length >= len + 2) if (midi_rt) midi_rt->in_received(midi_rt, - rt->receiver_buffer + 2, - rt->receiver_buffer[1]); + rt->receiver_buffer + 2, len); } if (!rt->chip->shutdown) { From 459d3a64766f5ca2f1886daeaf24582831a5f5ab Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 5 Aug 2026 10:34:28 +0900 Subject: [PATCH 562/791] ALSA: bcd2000: clear the URB pointers on disconnect bcd2000_free_usb_related_resources() frees both URBs and leaves the pointers behind: usb_kill_urb(bcd2k->midi_out_urb); usb_kill_urb(bcd2k->midi_in_urb); usb_free_urb(bcd2k->midi_out_urb); usb_free_urb(bcd2k->midi_in_urb); The rawmidi device outlives that call. A substream that is still open when the device is unplugged reaches bcd2000_midi_send() from the trigger path on close. That function writes to the freed URB and then hands it to the USB core: bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE; ... ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC); usb_kill_urb() does not stop a later submission either, so a submit that races the disconnect can requeue the URB after it has been reaped. midi_in_urb is exposed the same way: bcd2000_input_complete() resubmits it from the completion handler. KASAN on 7.2.0-rc5 (arm64): BUG: KASAN: slab-use-after-free in bcd2000_midi_send [snd_bcd2000] Write of size 4 at addr ffff00001827d388 by task bpoc/168 __asan_store4 bcd2000_midi_send [snd_bcd2000] bcd2000_midi_output_trigger [snd_bcd2000] snd_rawmidi_kernel_write1 close_substream.part.0 Freed by task 168: usb_free_urb bcd2000_disconnect [snd_bcd2000] BUG: KASAN: slab-use-after-free in usb_submit_urb Read of size 8 at addr ffff00001827d3b8 by task bpoc/168 Clear both pointers after freeing and test them on the paths that can still run. Poison the URBs before freeing them: usb_poison_urb() waits for a running completion handler and rejects any later submission, so after it returns the input path is quiesced and only the rawmidi trigger path can still reach bcd2000_midi_send(). No unpoison is needed; the URBs are freed on the next line. Discovered by XBOW, triaged by Baul Lee Fixes: b47a22290d58 ("ALSA: MIDI driver for Behringer BCD2000 USB device") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260805013428.38204-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/bcd2000/bcd2000.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index bebb48cb9abc..c5c542d17ccc 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd2000 *bcd2k) if (!midi_out_substream) return; + if (!bcd2k->midi_out_urb) + return; + /* copy command prefix bytes */ memcpy(bcd2k->midi_out_buf, device_cmd_prefix, sizeof(device_cmd_prefix)); @@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream) { struct bcd2000 *bcd2k = substream->rmidi->private_data; - if (bcd2k->midi_out_active) { + if (bcd2k->midi_out_active && bcd2k->midi_out_urb) { usb_kill_urb(bcd2k->midi_out_urb); bcd2k->midi_out_active = 0; } @@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k, struct usb_interface *interface) { - usb_kill_urb(bcd2k->midi_out_urb); - usb_kill_urb(bcd2k->midi_in_urb); + usb_poison_urb(bcd2k->midi_out_urb); + usb_poison_urb(bcd2k->midi_in_urb); usb_free_urb(bcd2k->midi_out_urb); usb_free_urb(bcd2k->midi_in_urb); + bcd2k->midi_out_urb = NULL; + bcd2k->midi_in_urb = NULL; if (bcd2k->intf) { usb_set_intfdata(bcd2k->intf, NULL); From 4335e387786479889e6db691fe06d345e52ea536 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 5 Aug 2026 10:38:04 +0900 Subject: [PATCH 563/791] ALSA: FCP: do not copy out an uninitialised init response fcp_ioctl_init() allocates its response buffer with kmalloc() and copies the whole buffer back to userspace: buf_size = init.step0_resp_size + init.step2_resp_size; void *resp __free(kfree) = kmalloc(buf_size, GFP_KERNEL); ... if (copy_to_user(arg->resp, resp, buf_size)) return -EFAULT; Nothing clears the buffer, and the only writer of its leading step0_resp_size bytes is the step-0 control transfer: err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), FCP_USB_REQ_STEP0, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 0, private->bInterfaceNumber, step0_resp, private->step0_resp_size); if (err < 0) return err; usb_fill_control_urb() does not set URB_SHORT_NOT_OK, so a short or zero-length data stage completes with status 0 and snd_usb_ctl_msg() returns a small actual_length. The only check is err < 0, so a short transfer is accepted as success. snd_usb_ctl_msg() copies the full size back unconditionally: buf = kmemdup(data, size, GFP_KERNEL); ... memcpy(data, buf, size); Bytes the device never wrote are therefore restored into resp unchanged and copied to userspace. step0_resp_size and step2_resp_size are each validated only to 1..255, so the caller also picks the slab cache, from kmalloc-8 up to kmalloc-512. On 7.2.0-rc5 (arm64), device answering step 0 with a zero-length data stage, s0 = s2 = 255: # init_on_alloc off, no spray step0 window [0,255): nonzero=94/255 000: 00 80 60 06 00 00 ff ff 18 00 00 00 57 01 ea 01 010: 08 78 22 13 00 00 ff ff a8 c4 5f 80 00 80 ff ff # same kernel, kmalloc-512 pre-seeded with an 8-byte tag step0 window [0,255): nonzero=219/255 tagbytes=232 # identical run, init_on_alloc=1 step0 window [0,255): nonzero=0/255 tagbytes=0 # all three runs step2 window [255,510): device words matched=62/62 a8 c4 5f 80 00 80 ff ff is the little-endian kernel text address ffff8000805fc4a8. The step-2 window is unaffected, so the disclosure is exactly the step-0 region. Zero the buffer, and require the step-0 transfer to deliver the full step0_resp_size bytes so a short data stage is reported as an error. Discovered by XBOW, triaged by Baul Lee Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260805013804.38839-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/fcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 6f5dcd35e1d4..8f52a3dc9ec3 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -486,7 +486,7 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer, buf_size = init.step0_resp_size + init.step2_resp_size; void *resp __free(kfree) = - kmalloc(buf_size, GFP_KERNEL); + kzalloc(buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -1022,6 +1022,8 @@ static int fcp_init(struct usb_mixer_interface *mixer, step0_resp, private->step0_resp_size); if (err < 0) return err; + if (err != private->step0_resp_size) + return -EIO; err = fcp_init_notify(mixer); if (err < 0) From 2b38a2713116963fe4d0a4c06231f1968b483f20 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 5 Aug 2026 02:53:24 +0800 Subject: [PATCH 564/791] ALSA: hda/realtek: Fix headset mic on Legion AW88399 laptops The ALC287 codec on Lenovo Legion AW88399 laptops does not mark the combo-jack microphone as a headset mic, so the HDA parser treats it as a plain microphone. The headset microphone route and inline headset buttons are therefore unavailable. Enable Realtek headset mode without treating the jack as a headphone microphone, and enable headset jack button handling. Suppress automatic microphone selection so the internal microphone remains selectable while a headset is connected. The existing 0x1d override is redundant: firmware already marks that pin unused, and the override triggers a "SKU not ready 0x411111f0" warning. Drop it. Signed-off-by: Aaron Ma Reviewed-by: Marco Giunta Tested-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260804185325.22861-1-mapengyu@gmail.com --- sound/hda/codecs/realtek/alc269.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index bd1958bcf698..ad94d6b6085f 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3285,21 +3285,19 @@ static void aw88399_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup static void alc287_fixup_legion_16iax10h_aw88399(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - static const struct hda_pintbl pincfgs[] = { - { 0x1d, 0x411111f0 }, /* unused bogus pin */ - { } - }; - /* * Force DAC 0x02 for the bass speaker 0x17, as the default 0x06 lacks volume controls. */ static const hda_nid_t conn[] = { 0x02 }; + struct alc_spec *spec = codec->spec; alc269_fixup_limit_int_mic_boost(codec, fix, action); + alc_fixup_headset_mode_no_hp_mic(codec, fix, action); + alc_fixup_headset_jack(codec, fix, action); switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); + spec->gen.suppress_auto_mic = 1; snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); break; } From 5801c193b4f703e25130f5a15bada5dca8f4a09d Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 5 Aug 2026 02:53:25 +0800 Subject: [PATCH 565/791] ALSA: hda/realtek: Limit Legion AW88399 playback to stereo The Legion AW88399 speaker routing sends a stereo FL/FR stream to both speaker pairs. A four-channel stream leaves the front pair silent, so advertising four channels exposes an unusable playback mode. Limit the analogue PCM and the multi-output runtime constraint to two channels for the affected Legion codec SSIDs. This exposes the usable stereo configuration and rejects four-channel playback. Signed-off-by: Aaron Ma Reviewed-by: Marco Giunta Tested-by: Marco Giunta Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260804185325.22861-2-mapengyu@gmail.com --- sound/hda/codecs/realtek/alc269.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index ad94d6b6085f..bd53507c68fe 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3300,6 +3300,10 @@ static void alc287_fixup_legion_16iax10h_aw88399(struct hda_codec *codec, spec->gen.suppress_auto_mic = 1; snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); break; + case HDA_FIXUP_ACT_BUILD: + spec->gen.multiout.max_channels = 2; + spec->gen.pcm_rec[0]->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 2; + break; } } From c53cb1f46be5e67e2a87c4d06418718869707886 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 5 Aug 2026 08:44:35 +0200 Subject: [PATCH 566/791] ASoC: Intel: Remove obsolete UAPI headers Both target Intel's skylake-driver, previously found in sound/soc/intel which has been removed few years ago and succeeded by the avs-driver. snd_sst_tokens.h is succeeded by uapi/sound/intel/avs/tokens.h skl-tplg-interface.h to the best of my knowledge has no users. Its types are reflected in sound/soc/intel/avs/messages.h and are not intended for public use. Closest public equivalent would be the firmware's processing modules but in such case a user has to compile against Intel's AudioDSP headers, not Linux ones. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260805064435.429654-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/uapi/sound/skl-tplg-interface.h | 168 ------------ include/uapi/sound/snd_sst_tokens.h | 324 ------------------------ 2 files changed, 492 deletions(-) delete mode 100644 include/uapi/sound/skl-tplg-interface.h delete mode 100644 include/uapi/sound/snd_sst_tokens.h diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h deleted file mode 100644 index 940c4269322b..000000000000 --- a/include/uapi/sound/skl-tplg-interface.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * skl-tplg-interface.h - Intel DSP FW private data interface - * - * Copyright (C) 2015 Intel Corp - * Author: Jeeja KP - * Nilofer, Samreen - */ - -#ifndef __HDA_TPLG_INTERFACE_H__ -#define __HDA_TPLG_INTERFACE_H__ - -#include - -/* - * Default types range from 0~12. type can range from 0 to 0xff - * SST types start at higher to avoid any overlapping in future - */ -#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 -#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 -#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 -#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 - -#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ -#define MAX_IN_QUEUE 8 -#define MAX_OUT_QUEUE 8 - -#define SKL_UUID_STR_SZ 40 -/* Event types goes here */ -/* Reserve event type 0 for no event handlers */ -enum skl_event_types { - SKL_EVENT_NONE = 0, - SKL_MIXER_EVENT, - SKL_MUX_EVENT, - SKL_VMIXER_EVENT, - SKL_PGA_EVENT -}; - -/** - * enum skl_ch_cfg - channel configuration - * - * @SKL_CH_CFG_MONO: One channel only - * @SKL_CH_CFG_STEREO: L & R - * @SKL_CH_CFG_2_1: L, R & LFE - * @SKL_CH_CFG_3_0: L, C & R - * @SKL_CH_CFG_3_1: L, C, R & LFE - * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs - * @SKL_CH_CFG_4_0: L, C, R & Cs - * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs - * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE - * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two - * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] - * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] - * @SKL_CH_CFG_INVALID: Invalid - */ -enum skl_ch_cfg { - SKL_CH_CFG_MONO = 0, - SKL_CH_CFG_STEREO = 1, - SKL_CH_CFG_2_1 = 2, - SKL_CH_CFG_3_0 = 3, - SKL_CH_CFG_3_1 = 4, - SKL_CH_CFG_QUATRO = 5, - SKL_CH_CFG_4_0 = 6, - SKL_CH_CFG_5_0 = 7, - SKL_CH_CFG_5_1 = 8, - SKL_CH_CFG_DUAL_MONO = 9, - SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, - SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, - SKL_CH_CFG_7_1 = 12, - SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, - SKL_CH_CFG_INVALID -}; - -enum skl_module_type { - SKL_MODULE_TYPE_MIXER = 0, - SKL_MODULE_TYPE_COPIER, - SKL_MODULE_TYPE_UPDWMIX, - SKL_MODULE_TYPE_SRCINT, - SKL_MODULE_TYPE_ALGO, - SKL_MODULE_TYPE_BASE_OUTFMT, - SKL_MODULE_TYPE_KPB, - SKL_MODULE_TYPE_MIC_SELECT, -}; - -enum skl_core_affinity { - SKL_AFFINITY_CORE_0 = 0, - SKL_AFFINITY_CORE_1, - SKL_AFFINITY_CORE_MAX -}; - -enum skl_pipe_conn_type { - SKL_PIPE_CONN_TYPE_NONE = 0, - SKL_PIPE_CONN_TYPE_FE, - SKL_PIPE_CONN_TYPE_BE -}; - -enum skl_hw_conn_type { - SKL_CONN_NONE = 0, - SKL_CONN_SOURCE = 1, - SKL_CONN_SINK = 2 -}; - -enum skl_dev_type { - SKL_DEVICE_BT = 0x0, - SKL_DEVICE_DMIC = 0x1, - SKL_DEVICE_I2S = 0x2, - SKL_DEVICE_SLIMBUS = 0x3, - SKL_DEVICE_HDALINK = 0x4, - SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_NONE -}; - -/** - * enum skl_interleaving - interleaving style - * - * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] - * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] - */ -enum skl_interleaving { - SKL_INTERLEAVING_PER_CHANNEL = 0, - SKL_INTERLEAVING_PER_SAMPLE = 1, -}; - -enum skl_sample_type { - SKL_SAMPLE_TYPE_INT_MSB = 0, - SKL_SAMPLE_TYPE_INT_LSB = 1, - SKL_SAMPLE_TYPE_INT_SIGNED = 2, - SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, - SKL_SAMPLE_TYPE_FLOAT = 4 -}; - -enum module_pin_type { - /* All pins of the module takes same PCM inputs or outputs - * e.g. mixout - */ - SKL_PIN_TYPE_HOMOGENEOUS, - /* All pins of the module takes different PCM inputs or outputs - * e.g mux - */ - SKL_PIN_TYPE_HETEROGENEOUS, -}; - -enum skl_module_param_type { - SKL_PARAM_DEFAULT = 0, - SKL_PARAM_INIT, - SKL_PARAM_SET, - SKL_PARAM_BIND -}; - -struct skl_dfw_algo_data { - __u32 set_params:2; - __u32 rsvd:30; - __u32 param_id; - __u32 max; - char params[]; -} __packed; - -enum skl_tkn_dir { - SKL_DIR_IN, - SKL_DIR_OUT -}; - -enum skl_tuple_type { - SKL_TYPE_TUPLE, - SKL_TYPE_DATA -}; - -#endif diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h deleted file mode 100644 index defeb0c6ed20..000000000000 --- a/include/uapi/sound/snd_sst_tokens.h +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * snd_sst_tokens.h - Intel SST tokens definition - * - * Copyright (C) 2016 Intel Corp - * Author: Shreyas NC - */ -#ifndef __SND_SST_TOKENS_H__ -#define __SND_SST_TOKENS_H__ - -/** - * %SKL_TKN_UUID: Module UUID - * - * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: - * tuples, bytes, short and words - * - * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, - * homogenous=0, heterogenous=1 - * - * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, - * homogenous=0, heterogenous=1 - * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically - * if true - * - * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically - * if true - * - * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins - * - * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins - * - * %SKL_TKN_U8_TIME_SLOT: TDM slot number - * - * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take - * the values: - * SKL_AFFINITY_CORE_0 = 0, - * SKL_AFFINITY_CORE_1, - * SKL_AFFINITY_CORE_MAX - * - * %SKL_TKN_U8_MOD_TYPE: Module type value. - * - * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, - * BE or NONE as defined : - * SKL_PIPE_CONN_TYPE_NONE = 0, - * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) - * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) - * - * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is - * connected - * Can take the values: - * SKL_DEVICE_BT = 0x0, - * SKL_DEVICE_DMIC = 0x1, - * SKL_DEVICE_I2S = 0x2, - * SKL_DEVICE_SLIMBUS = 0x3, - * SKL_DEVICE_HDALINK = 0x4, - * SKL_DEVICE_HDAHOST = 0x5, - * SKL_DEVICE_NONE - * - * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the - * module is connected - * SKL_CONN_NONE = 0, - * SKL_CONN_SOURCE = 1, - * SKL_CONN_SINK = 2 - * - * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id - * - * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id - * - * %SKL_TKN_U32_MAX_MCPS: Module max mcps value - * - * %SKL_TKN_U32_MEM_PAGES: Module resource pages - * - * %SKL_TKN_U32_OBS: Stores Output Buffer size - * - * %SKL_TKN_U32_IBS: Stores input buffer size - * - * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, - * SSP1=1,SSP2=2, - * SSP3=3, SSP4=4, - * SSP5=5, SSP6=6,INVALID - * - * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask - * %SKL_TKN_U32_CONVERTER: Module params converter mask - * %SKL_TKN_U32_PIPE_ID: Stores the pipe id - * - * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is - * connected to. It can be - * SKL_PIPE_CONN_TYPE_NONE = 0, - * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), - * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), - * - * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value - * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages - * - * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output - * formats and the pin count. - * The first 4 bits have the direction - * value and the next 4 have - * the pin count value. - * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. - * The input and output formats - * share the same set of tokens - * with the distinction between input - * and output made by reading direction - * token. - * - * %SKL_TKN_U32_FMT_CH: Supported channel count - * - * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate - * - * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size - * - * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container - * - * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the - * input/output. - * - * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per - * channel or per sample. The values can be : - * SKL_INTERLEAVING_PER_CHANNEL = 0, - * SKL_INTERLEAVING_PER_SAMPLE = 1, - * - * %SKL_TKN_U32_FMT_SAMPLE_TYPE: - * Specifies the sample type. Can take the - * values: SKL_SAMPLE_TYPE_INT_MSB = 0, - * SKL_SAMPLE_TYPE_INT_LSB = 1, - * SKL_SAMPLE_TYPE_INT_SIGNED = 2, - * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, - * SKL_SAMPLE_TYPE_FLOAT = 4 - * - * %SKL_TKN_U32_CH_MAP: Channel map values - * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: - * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, - * SKL_PARAM_SET, SKL_PARAM_BIND - * - * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params - * - * %SKL_TKN_U32_CAPS_SET_PARAMS: - * Set params value - * - * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID - * - * %SKL_TKN_U32_CAPS_SIZE: Caps size - * - * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain - * - * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries - * - * %SKL_TKN_STR_LIB_NAME: Specifies the library name - * - * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe - * - * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module - * - * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec - * - * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be - * playback/capture. - * - * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs - * - * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline - * and its data - * - * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe - * and PCM params supported by that pipe - * config. This is used as index to fill - * up the pipe config and module config - * structure. - * - * %SKL_TKN_U32_CFG_FREQ: - * %SKL_TKN_U8_CFG_CHAN: - * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) - * supported for each of the pipe configs. - * - * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the - * pipe config - * - * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the - * pipe config - * - * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest - * - * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest - * - * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module - * - * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module - * - * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to - * be filled into. - * A module can support multiple resource - * configuration and is represnted as a - * resource table. This index is used to - * fill information into appropriate index. - * - * %SKL_TKN_MM_U32_CPS: DSP cycles per second - * - * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA - * - * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame - * - * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module - * - * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module - * - * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin - * - * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ - * format information to be filled into. - * - * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats - * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats - * - * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled - * with kcps and clock source - * - * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table - * - * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo - * cycles per second) below which DSP is clocked - * from source specified by clock source. - * - * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry - * - * %SKL_TKN_U32_FMT_CFG_IDX: Format config index - * - * module_id and loadable flags dont have tokens as these values will be - * read from the DSP FW manifest - * - * Tokens defined can be used either in the manifest or widget private data. - * - * SKL_TKN_MM is used as a suffix for all tokens that represent - * module data in the manifest. - */ -enum SKL_TKNS { - SKL_TKN_UUID = 1, - SKL_TKN_U8_NUM_BLOCKS, - SKL_TKN_U8_BLOCK_TYPE, - SKL_TKN_U8_IN_PIN_TYPE, - SKL_TKN_U8_OUT_PIN_TYPE, - SKL_TKN_U8_DYN_IN_PIN, - SKL_TKN_U8_DYN_OUT_PIN, - SKL_TKN_U8_IN_QUEUE_COUNT, - SKL_TKN_U8_OUT_QUEUE_COUNT, - SKL_TKN_U8_TIME_SLOT, - SKL_TKN_U8_CORE_ID, - SKL_TKN_U8_MOD_TYPE, - SKL_TKN_U8_CONN_TYPE, - SKL_TKN_U8_DEV_TYPE, - SKL_TKN_U8_HW_CONN_TYPE, - SKL_TKN_U16_MOD_INST_ID, - SKL_TKN_U16_BLOCK_SIZE, - SKL_TKN_U32_MAX_MCPS, - SKL_TKN_U32_MEM_PAGES, - SKL_TKN_U32_OBS, - SKL_TKN_U32_IBS, - SKL_TKN_U32_VBUS_ID, - SKL_TKN_U32_PARAMS_FIXUP, - SKL_TKN_U32_CONVERTER, - SKL_TKN_U32_PIPE_ID, - SKL_TKN_U32_PIPE_CONN_TYPE, - SKL_TKN_U32_PIPE_PRIORITY, - SKL_TKN_U32_PIPE_MEM_PGS, - SKL_TKN_U32_DIR_PIN_COUNT, - SKL_TKN_U32_FMT_CH, - SKL_TKN_U32_FMT_FREQ, - SKL_TKN_U32_FMT_BIT_DEPTH, - SKL_TKN_U32_FMT_SAMPLE_SIZE, - SKL_TKN_U32_FMT_CH_CONFIG, - SKL_TKN_U32_FMT_INTERLEAVE, - SKL_TKN_U32_FMT_SAMPLE_TYPE, - SKL_TKN_U32_FMT_CH_MAP, - SKL_TKN_U32_PIN_MOD_ID, - SKL_TKN_U32_PIN_INST_ID, - SKL_TKN_U32_MOD_SET_PARAMS, - SKL_TKN_U32_MOD_PARAM_ID, - SKL_TKN_U32_CAPS_SET_PARAMS, - SKL_TKN_U32_CAPS_PARAMS_ID, - SKL_TKN_U32_CAPS_SIZE, - SKL_TKN_U32_PROC_DOMAIN, - SKL_TKN_U32_LIB_COUNT, - SKL_TKN_STR_LIB_NAME, - SKL_TKN_U32_PMODE, - SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ - SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, - SKL_TKN_U32_DMA_BUF_SIZE, - - SKL_TKN_U32_PIPE_DIRECTION, - SKL_TKN_U32_PIPE_CONFIG_ID, - SKL_TKN_U32_NUM_CONFIGS, - SKL_TKN_U32_PATH_MEM_PGS, - - SKL_TKN_U32_CFG_FREQ, - SKL_TKN_U8_CFG_CHAN, - SKL_TKN_U8_CFG_BPS, - SKL_TKN_CFG_MOD_RES_ID, - SKL_TKN_CFG_MOD_FMT_ID, - SKL_TKN_U8_NUM_MOD, - - SKL_TKN_MM_U8_MOD_IDX, - SKL_TKN_MM_U8_NUM_RES, - SKL_TKN_MM_U8_NUM_INTF, - SKL_TKN_MM_U32_RES_ID, - SKL_TKN_MM_U32_CPS, - SKL_TKN_MM_U32_DMA_SIZE, - SKL_TKN_MM_U32_CPC, - SKL_TKN_MM_U32_RES_PIN_ID, - SKL_TKN_MM_U32_INTF_PIN_ID, - SKL_TKN_MM_U32_PIN_BUF, - SKL_TKN_MM_U32_FMT_ID, - SKL_TKN_MM_U32_NUM_IN_FMT, - SKL_TKN_MM_U32_NUM_OUT_FMT, - - SKL_TKN_U32_ASTATE_IDX, - SKL_TKN_U32_ASTATE_COUNT, - SKL_TKN_U32_ASTATE_KCPS, - SKL_TKN_U32_ASTATE_CLK_SRC, - - SKL_TKN_U32_FMT_CFG_IDX = 96, - SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, -}; - -#endif From 1a08e82ef6aff3c55d39652f8862ddad4b7d5f3b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 5 Aug 2026 11:52:23 +0100 Subject: [PATCH 567/791] firmware: cs_dsp: Fix mock register default typo in KUnit test Correct the address of the HALO_SCRATCH4 entry in halo_register_defaults[]. This doesn't affect the validity of the KUnit testing because none of the tests rely on this value. It's only defaulted because cs_dsp will read it when the DSP state changes from running to stopped - this would only have logged a warning about failure to read the register but it doesn't cause anything to fail. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260805105223.956785-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c index 5167305521cd..c966b30e1082 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c @@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ - { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ + { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ From daf55a381dca9bda12cc8c3a47605698f2d560e9 Mon Sep 17 00:00:00 2001 From: Luca Castaldini Date: Wed, 5 Aug 2026 14:29:07 +0200 Subject: [PATCH 568/791] ALSA: hda/realtek: Add mute LED support for HP Pavilion 15-eh2xxx Add the subsystem ID 103c:8a0e to the ALC287 HP GPIO LED quirk table so the mute LED follows the speaker mute state. Tested on HP Pavilion Laptop 15-eh2xxx with ALC287 codec. The mute LED now follows the speaker mute state. Signed-off-by: Luca Castaldini Link: https://patch.msgid.link/20260805122907.52302-1-luca.castaldini96@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index bd53507c68fe..b00b22e96afe 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7352,6 +7352,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0e, "HP Pavilion Laptop 15-eh2xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), From fde30db1c849e1a4dabb4aecf84e7cddd0a42070 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Wed, 5 Aug 2026 10:10:17 +0200 Subject: [PATCH 569/791] ALSA: hda/realtek: enable AW88399 on Lenovo Legion R9000P ADR10H Add codec SSID entries for the Lenovo Legion R9000P ADR10H (83RV), which uses the same ALC287 + AW88399 smart amplifier configuration as the existing supported Legion models. DSDT inspection confirms identical AWDZ8399 ACPI device layout with reversed I2C addresses (0x35 before 0x34). Register dumps show the same BSTS behavior as the other Legions. Both the channel swap and BSTS bypass quirks apply. Codec SSIDs (Lenovo vendor ID 0x17aa): * 0x3936: Legion R9000P ADR10H (AMD) * 0x3937: Legion R9000P ADR10H (AMD) Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB7724EE8DED946545C55717C1FCD32@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ sound/hda/codecs/side-codecs/aw88399_hda.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b00b22e96afe..360ccc62ab89 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8097,6 +8097,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + HDA_CODEC_QUIRK(0x17aa, 0x3936, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3937, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), HDA_CODEC_QUIRK(0x17aa, 0x3938, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), HDA_CODEC_QUIRK(0x17aa, 0x3939, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), SND_PCI_QUIRK(0x17aa, 0x393e, "Lenovo ThinkBook 14 G8+ IPH", ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK), diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.c b/sound/hda/codecs/side-codecs/aw88399_hda.c index 5175d341ecbe..11cef4923024 100644 --- a/sound/hda/codecs/side-codecs/aw88399_hda.c +++ b/sound/hda/codecs/side-codecs/aw88399_hda.c @@ -186,6 +186,8 @@ static const struct aw88399_prop_model aw88399_prop_model_table[] = { { "17AA3907", aw88399_apply_legion_quirks }, { "17AA3927", aw88399_apply_legion_quirks }, { "17AA3928", aw88399_apply_legion_quirks }, + { "17AA3936", aw88399_apply_legion_quirks }, + { "17AA3937", aw88399_apply_legion_quirks }, { "17AA3938", aw88399_apply_legion_quirks }, { "17AA3939", aw88399_apply_legion_quirks }, { } From cc606b6c2328b4864885db6afcad7e78c0ac7a73 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:13 +0300 Subject: [PATCH 570/791] ASoC: adau1761: sort the register default table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). The table lists the ADAU1761 specific registers (0x4008 and up) before the block shared with the ADAU1381/ADAU1781, which starts at ADAU17X1_CLOCK_CONTROL (0x4000), so bsearch() descends into the wrong half and 28 of the 52 entries are unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: dab464b60b24 ("ASoC: Add ADAU1361/ADAU1761 audio CODEC support") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Acked-by: Nuno Sá Link: https://patch.msgid.link/20260805122713.11376-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/adau1761.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index a70c46dd5d76..27ce03b0fb91 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -68,23 +68,33 @@ #define ADAU1761_FIRMWARE "adau1761.bin" static const struct reg_default adau1761_reg_defaults[] = { - { ADAU1761_DEJITTER, 0x03 }, + { ADAU17X1_CLOCK_CONTROL, 0x00 }, + { ADAU17X1_PLL_CONTROL, 0x00 }, { ADAU1761_DIGMIC_JACKDETECT, 0x00 }, + { ADAU17X1_REC_POWER_MGMT, 0x00 }, { ADAU1761_REC_MIXER_LEFT0, 0x00 }, { ADAU1761_REC_MIXER_LEFT1, 0x00 }, { ADAU1761_REC_MIXER_RIGHT0, 0x00 }, { ADAU1761_REC_MIXER_RIGHT1, 0x00 }, { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, + { ADAU17X1_MICBIAS, 0x00 }, { ADAU1761_ALC_CTRL0, 0x00 }, { ADAU1761_ALC_CTRL1, 0x00 }, { ADAU1761_ALC_CTRL2, 0x00 }, { ADAU1761_ALC_CTRL3, 0x00 }, - { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, - { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, + { ADAU17X1_SERIAL_PORT0, 0x00 }, + { ADAU17X1_SERIAL_PORT1, 0x00 }, + { ADAU17X1_CONVERTER0, 0x00 }, + { ADAU17X1_CONVERTER1, 0x00 }, + { ADAU17X1_ADC_CONTROL, 0x00 }, + { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT0, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT1, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 }, + { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 }, { ADAU1761_PLAY_MIXER_MONO, 0x00 }, { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 }, @@ -93,20 +103,6 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 }, { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 }, { ADAU1761_POP_CLICK_SUPPRESS, 0x00 }, - { ADAU1761_JACK_DETECT_PIN, 0x00 }, - { ADAU1761_CLK_ENABLE0, 0x00 }, - { ADAU1761_CLK_ENABLE1, 0x00 }, - { ADAU17X1_CLOCK_CONTROL, 0x00 }, - { ADAU17X1_PLL_CONTROL, 0x00 }, - { ADAU17X1_REC_POWER_MGMT, 0x00 }, - { ADAU17X1_MICBIAS, 0x00 }, - { ADAU17X1_SERIAL_PORT0, 0x00 }, - { ADAU17X1_SERIAL_PORT1, 0x00 }, - { ADAU17X1_CONVERTER0, 0x00 }, - { ADAU17X1_CONVERTER1, 0x00 }, - { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_ADC_CONTROL, 0x00 }, { ADAU17X1_PLAY_POWER_MGMT, 0x00 }, { ADAU17X1_DAC_CONTROL0, 0x00 }, { ADAU17X1_DAC_CONTROL1, 0x00 }, @@ -114,12 +110,16 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU17X1_SERIAL_PORT_PAD, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD0, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD1, 0x00 }, + { ADAU1761_JACK_DETECT_PIN, 0x00 }, + { ADAU1761_DEJITTER, 0x03 }, { ADAU17X1_DSP_SAMPLING_RATE, 0x01 }, { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 }, { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 }, { ADAU17X1_DSP_ENABLE, 0x00 }, { ADAU17X1_DSP_RUN, 0x00 }, { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 }, + { ADAU1761_CLK_ENABLE0, 0x00 }, + { ADAU1761_CLK_ENABLE1, 0x00 }, }; static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1); From 24fb36c1936bdf4fcda6ad3c99c862fbc8703e93 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 5 Aug 2026 17:44:33 +0300 Subject: [PATCH 571/791] ASoC: cpcap: Remove modem-specific voice call support Revert commit 0dedbde5062d ("ASoC: cpcap: Implement set_tdm_slot for voice call support"). The reverted implementation was added to support a modem driver that directly locates and configures the codec DAI using snd_soc_find_dai() together with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() and snd_soc_dai_set_tdm_slot(). The DAI configuration should instead be provided by the ASoC DAI link, allowing the machine driver or DT to describe the interface rather than having a client driver configure the codec directly. Remove the ad hoc voice call implementation. Signed-off-by: Ivaylo Dimitrov Link: https://patch.msgid.link/20260805144434.1290261-2-ivo.g.dimitrov.75@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cpcap.c | 127 +-------------------------------------- 1 file changed, 2 insertions(+), 125 deletions(-) diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index 6b80c455b074..987c63fc5fa4 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -26,14 +26,6 @@ /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ -/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ -#define CPCAP_BIT_AUDIO_LOW_PWR 6 -#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 -#define CPCAP_BIT_VAUDIOPRISTBY 4 -#define CPCAP_BIT_VAUDIO_MODE1 2 -#define CPCAP_BIT_VAUDIO_MODE0 1 -#define CPCAP_BIT_V_AUDIO_EN 0 - /* Register 513 CPCAP_REG_CC --- CODEC */ #define CPCAP_BIT_CDC_CLK2 15 #define CPCAP_BIT_CDC_CLK1 14 @@ -239,7 +231,6 @@ struct cpcap_reg_info { }; static const struct cpcap_reg_info cpcap_default_regs[] = { - { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, { CPCAP_REG_CC, 0xFFFF, 0x0000 }, { CPCAP_REG_CC, 0xFFFF, 0x0000 }, { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, @@ -1391,121 +1382,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } - -/* - * Configure codec for voice call if requested. - * - * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() - * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the - * cpcap related hardware as CPU is not involved in the voice call. - */ -static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, - bool voice_call) -{ - int mask, err; - - /* Modem to codec VAUDIO_MODE1 */ - mask = BIT(CPCAP_BIT_VAUDIO_MODE1); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Clear MIC1_MUX for call */ - mask = BIT(CPCAP_BIT_MIC1_MUX); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, - mask, voice_call ? 0 : mask); - if (err) - return err; - - /* Set MIC2_MUX for call */ - mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | - BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable LDSP for call */ - mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable CPCAP_BIT_PGA_CDC_EN for call */ - mask = BIT(CPCAP_BIT_PGA_CDC_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Unmute voice for call */ - if (dai) { - err = snd_soc_dai_digital_mute(dai, !voice_call, - SNDRV_PCM_STREAM_PLAYBACK); - if (err) - return err; - } - - /* Set modem to codec mic CDC and HPF for call */ - mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | - BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | - BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, - mask, voice_call ? mask : 0); - if (err) - return err; - - /* Enable modem to codec CDC for call*/ - mask = BIT(CPCAP_BIT_CDC_CLK_EN); - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, - mask, voice_call ? mask : 0); - - return err; -} - -static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) -{ - struct snd_soc_component *component = dai->component; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); - int err, ts_mask, mask; - bool voice_call; - - /* - * Primitive test for voice call, probably needs more checks - * later on for 16-bit calls detected, Bluetooth headset etc. - */ - if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) - voice_call = true; - else - voice_call = false; - - ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; - ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; - - mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; - mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; - - err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, - ts_mask, mask); - if (err) - return err; - - err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); - if (err) - return err; - - err = cpcap_voice_call(cpcap, dai, voice_call); - if (err) - return err; - - return 0; -} - -static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) +static int cpcap_voice_set_mute(struct snd_soc_dai *dai, + int mute, int direction) { struct snd_soc_component *component = dai->component; struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); @@ -1526,7 +1404,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { .hw_params = cpcap_voice_hw_params, .set_sysclk = cpcap_voice_set_dai_sysclk, .set_fmt = cpcap_voice_set_dai_fmt, - .set_tdm_slot = cpcap_voice_set_tdm_slot, .mute_stream = cpcap_voice_set_mute, .no_capture_mute = 1, }; From 26a92a696897a1746e507febc0b00ecd4ceaa165 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 5 Aug 2026 17:44:34 +0300 Subject: [PATCH 572/791] ASoC: codecs: cpcap: set voice DAI format as specified in DT We can have port endpoints with different DAI formats, make sure those are actually set-up Signed-off-by: Ivaylo Dimitrov Link: https://patch.msgid.link/20260805144434.1290261-3-ivo.g.dimitrov.75@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cpcap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index 987c63fc5fa4..fc41e1547bda 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -1256,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component = dai->component; struct device *dev = component->dev; struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); @@ -1289,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, return err; } - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); } static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, From 253010acfc8ad908446347b30a613032d1517bb5 Mon Sep 17 00:00:00 2001 From: Edson Juliano Drosdeck Date: Wed, 5 Aug 2026 12:45:18 -0300 Subject: [PATCH 573/791] ALSA: hda/realtek: Limit mic boost on Positivo N15RPE-S The internal mic boost on the Positivo N15RPE-S is too high. Fix this by applying the ALC269_FIXUP_LIMIT_INT_MIC_BOOST fixup to the machine to limit the gain. Signed-off-by: Edson Juliano Drosdeck Link: https://patch.msgid.link/20260805154518.19093-1-edson.drosdeck@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 360ccc62ab89..cd65cf584d7e 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8197,6 +8197,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), From eb2b516900f2cff6c931a0d664a625ee93855779 Mon Sep 17 00:00:00 2001 From: Carl Quist Date: Thu, 6 Aug 2026 11:47:18 +0200 Subject: [PATCH 574/791] ALSA: hda/realtek: Enable mute LED on HP Laptop 15-dy0xxx The mute LED on the HP Laptop 15-dy0xxx (PCI SSID 103c:864f, Realtek ALC236) does not work, because the machine has no entry in the quirk table. No fixup is applied, so no mute LED classdev is registered and nothing ever drives the LED. The LED is controlled by COEF index 0x07, bit 0. This was verified on the hardware with hda-verb: setting the bit lights the mute LED and clearing it turns the LED off. hda-verb /dev/snd/hwC0D0 0x20 SET_COEF_INDEX 0x07 hda-verb /dev/snd/hwC0D0 0x20 SET_PROC_COEF 0x1 # LED on hda-verb /dev/snd/hwC0D0 0x20 SET_PROC_COEF 0x200 # LED off That is exactly what ALC236_FIXUP_HP_MUTE_LED_COEFBIT2 configures, and the closely related HP Laptop 15-dw0xxx (103c:85f0) already uses it. Bit 9 (0x200) is set by default on this board and is preserved by the fixup's read-modify-write. Tested on an HP Laptop 15-dy0xxx (SKU 7FU54UA#ABA, board 864F, BIOS F.40). Signed-off-by: Carl Quist Link: https://lore.kernel.org/CAOtcGaxXndKxTK5MVSEcmF-LUy+V51K7fhE=qvLA+VvW5ZyCNA@mail.gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index cd65cf584d7e..1b2fb3940d74 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7242,6 +7242,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x864f, "HP Laptop 15-dy0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), From 1e3d326c657e63eb38c8abb1f25b084f725e2060 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 6 Aug 2026 13:45:04 +0800 Subject: [PATCH 575/791] ALSA: hda/realtek: Merge duplicate quirk entries for ASUS Strix G615 The ASUS Strix G615 series (subsystem IDs 0x1043:0x1204 and 0x1043:0x1214) currently have duplicate quirk entries: one using HDA_CODEC_QUIRK with ALC287_FIXUP_TAS2781_I2C, and another using SND_PCI_QUIRK with ALC287_FIXUP_TXNW2781_I2C_ASUS. Since these entries cover the same machines, the duplicate entries are redundant and may cause confusion. The correct fixup for these models should be ALC287_FIXUP_TXNW2781_I2C_ASUS, as the TAS2781 fixup was likely a mistake. Merge the two entries into a single SND_PCI_QUIRK entry with the correct fixup, removing the redundant HDA_CODEC_QUIRK entries. Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260806054505.43717-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 1b2fb3940d74..b50a403fe11d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7617,10 +7617,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - HDA_CODEC_QUIRK(0x1043, 0x1204, "ASUS Strix G16 G615JMR", ALC287_FIXUP_TXNW2781_I2C_ASUS), - SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - HDA_CODEC_QUIRK(0x1043, 0x1214, "ASUS ROG Strix G615LP", ALC287_FIXUP_TXNW2781_I2C_ASUS), - SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TXNW2781_I2C_ASUS), + SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C_ASUS), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), From a7e2cca7941bb99d0b1081bf05f00f6c9ecde2d4 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 6 Aug 2026 13:45:05 +0800 Subject: [PATCH 576/791] ALSA: hda/realtek: Merge duplicate quirk entries for ASUS UM6702RA/RC The ASUS UM6702RA/RC (subsystem 0x1043:0x1ee2) currently has two duplicate quirk entries: one using HDA_CODEC_QUIRK with ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1, and another using SND_PCI_QUIRK with ALC287_FIXUP_CS35L41_I2C_2. Since these entries cover the same machine, the duplicate is redundant and should be merged. The correct fixup to keep is ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1, as it additionally addresses the issue where the volume cannot be adjusted properly. Merge the two entries into a single SND_PCI_QUIRK entry with the appropriate fixup. Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260806054505.43717-2-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b50a403fe11d..a6cfea43d88d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7719,8 +7719,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), - HDA_CODEC_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), From d4c669890ed0862bc2abb7d9a6e50c7057124081 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 14:19:42 +0100 Subject: [PATCH 577/791] ASoC: dt-bindings: cirrus,cs42l43: Add CS42L44 variant The cs42l44 is a cost optimised variant of cs42l43b. Acked-by: Rob Herring (Arm) Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260805131942.45729-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml index 376928d1f64b..c1ef9f308396 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml @@ -27,6 +27,7 @@ properties: enum: - cirrus,cs42l43 - cirrus,cs42l43b + - cirrus,cs42l44 reg: maxItems: 1 From 6a236928b43c54541c428085996e66aa94f3fcfe Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:41:58 +0100 Subject: [PATCH 578/791] ASoC: SDCA: Tidy up error message Bring the entity_pde_event() error message slightly more in line with the other SDCA error messages. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 9a6c0036b7be..03486d1c1b2f 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -458,7 +458,7 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, entity->pde.max_delay, entity->pde.num_max_delay); if (ret) - dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", + dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", entity->label, from, to, ret); return ret; From 3dcc74d51b223e07b54e0ad83700601598cae994 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:41:59 +0100 Subject: [PATCH 579/791] ASoC: SDCA: Remove unused dev pointer argument Remove the now unused device pointer from sdca_asoc_pde_poll_actual_ps(). Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 8 ++++---- sound/soc/codecs/rt766-sdca.c | 2 +- sound/soc/codecs/tac5xx2-sdw.c | 4 ++-- sound/soc/sdca/sdca_asoc.c | 11 +++++------ 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index d3024c3b38b9..599a72f33515 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -107,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays); + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays); #endif // __SDCA_ASOC_H__ diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 49ee9cef5c54..4c5acd950d70 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -616,7 +616,7 @@ static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, return -EINVAL; } - ret = sdca_asoc_pde_poll_actual_ps(component->dev, rt766->regmap, + ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, func_num, pde_num, from_ps, to_ps, diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index ace06f5ab58c..ea0408b71713 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -800,7 +800,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); if (ret) dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", @@ -847,7 +847,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, NULL, 0); if (ret) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 03486d1c1b2f..ce2e7c270765 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, /** * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state - * @dev: Pointer to the device for error logging. * @regmap: Register map for reading ACTUAL_PS register. * @function_id: SDCA function identifier. * @entity_id: SDCA entity identifier for the power domain. @@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, * polling times out before reaching the target state, or a negative error code if * a register read fails. */ -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays) + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays) { static const int polls = 100; static const int default_poll_us = 1000; @@ -451,7 +450,7 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, return 0; } - ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, + ret = sdca_asoc_pde_poll_actual_ps(component->regmap, SDW_SDCA_CTL_FUNC(widget->reg), SDW_SDCA_CTL_ENT(widget->reg), from, to, From c03f0b9a7d9966b1846f561e4f49ed481c524a0a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:00 +0100 Subject: [PATCH 580/791] ASoC: SDCA: Move HID registration to IRQ time Currently, the SDCA code registers the HID device whilst parsing the DisCo information. This necessitates storing the HID device in the DisCo structs, which are intended to only store the parsed DisCo. Having the HID device registered so early in the process also causes some issues with cleaning up. Update the code to register the HID device as the IRQs are handled, this alleviates the previous concerns and brings the support inline with the other SDCA event handling. As part of this move the naming for the SDCA HID is also updated, it saves some complexity around the passing of the SoundWire device to include this in this patch. Update to using the dev_name for the phys, which is more consistent with other HID users, and use the actual function name/address for the HID name itself. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 2 -- include/sound/sdca_hid.h | 6 ++---- sound/soc/codecs/rt766-sdca.c | 22 +++++++++++++++++++--- sound/soc/codecs/rt766-sdca.h | 2 ++ sound/soc/sdca/sdca_functions.c | 24 ++++++------------------ sound/soc/sdca/sdca_hid.c | 21 ++++++++++----------- sound/soc/sdca/sdca_interrupts.c | 4 ++++ 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index fb931ae735a2..35799a977145 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1116,7 +1116,6 @@ struct sdca_entity_ge { /** * struct sdca_entity_hide - information specific to HIDE Entities - * @hid: HID device structure * @num_hidtx_ids: number of HIDTx Report ID * @num_hidrx_ids: number of HIDRx Report ID * @hidtx_ids: HIDTx Report ID @@ -1131,7 +1130,6 @@ struct sdca_entity_ge { * @hid_desc: HID descriptor for the HIDE Entity */ struct sdca_entity_hide { - struct hid_device *hid; unsigned int *hidtx_ids; unsigned int *hidrx_ids; int num_hidtx_ids; diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h index 18bebbe428c9..83d1c7768133 100644 --- a/include/sound/sdca_hid.h +++ b/include/sound/sdca_hid.h @@ -16,14 +16,12 @@ struct sdca_interrupt; #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity); +int sdca_add_hid_device(struct sdca_interrupt *interrupt); int sdca_hid_process_report(struct sdca_interrupt *interrupt); #else -static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) { return 0; } diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 4c5acd950d70..54ed0c42fba2 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) { struct rt766_sdca_priv *rt766 = interrupt->priv; - struct sdca_entity *ent_hid = interrupt->entity; unsigned char *buf = NULL; unsigned int offset, owner, length; unsigned int det_mode, idx, val; @@ -85,8 +85,8 @@ static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) buf[idx] = val & 0xff; } - if (ent_hid) - hid_input_report(ent_hid->hide.hid, HID_INPUT_REPORT, + if (rt766->hid) + hid_input_report(rt766->hid, HID_INPUT_REPORT, buf, length, 1); } @@ -191,6 +191,13 @@ static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) return IRQ_HANDLED; } +static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct rt766_sdca_priv *rt766 = interrupt->priv; + + hid_destroy_device(rt766->hid); +} + static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, struct sdca_function_data *function, struct snd_soc_component *component, @@ -231,6 +238,15 @@ static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, if (ret) return ret; + if (handler == rt766_sdca_irq_btn_handler) { + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + + interrupt->free_priv = rt766_sdca_destroy_hid_device; + rt766->hid = interrupt->priv; + } + interrupt->priv = rt766; ret = sdca_irq_request(dev, info, irq, interrupt->name, handler, interrupt); diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h index 5acdb83a42fb..de4064007eb9 100644 --- a/sound/soc/codecs/rt766-sdca.h +++ b/sound/soc/codecs/rt766-sdca.h @@ -8,6 +8,7 @@ #ifndef __RT766_H__ #define __RT766_H__ +#include #include #include #include @@ -41,6 +42,7 @@ struct rt766_sdca_priv { struct sdca_function_data *sa_func_data; struct sdca_function_data *hid_func_data; struct sdca_interrupt_info *irq_info; + struct hid_device *hid; }; /* vendor registers */ diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index cdf1e68d60ac..e9b449b67033 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -18,7 +18,6 @@ #include #include #include -#include /* * Should be long enough to encompass all the MIPI DisCo properties. @@ -1366,8 +1365,7 @@ static int find_sdca_entity_ge(struct device *dev, } static int -find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; @@ -1440,13 +1438,6 @@ find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, hide->hid_report_desc = report_desc; fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", report_desc, nval); - - /* add HID device */ - ret = sdca_add_hid_device(dev, sdw, entity); - if (ret) { - dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); - return ret; - } } } @@ -1475,8 +1466,7 @@ static int find_sdca_entity_xu(struct device *dev, return 0; } -static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function, +static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) @@ -1528,8 +1518,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, ret = find_sdca_entity_ge(dev, entity_node, entity); break; case SDCA_ENTITY_TYPE_HIDE: - ret = find_sdca_entity_hide(dev, sdw, function_node, - entity_node, entity); + ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); break; default: break; @@ -1544,8 +1533,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, return 0; } -static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { struct sdca_entity *entities; @@ -1596,7 +1584,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return -EINVAL; } - ret = find_sdca_entity(dev, sdw, function, function_node, + ret = find_sdca_entity(dev, function, function_node, entity_node, &entities[i]); fwnode_handle_put(entity_node); if (ret) @@ -2214,7 +2202,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret) return ret; - ret = find_sdca_entities(dev, sdw, node, function); + ret = find_sdca_entities(dev, node, function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index abbd56a3d297..ea511c6f9798 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -85,10 +86,11 @@ static const struct hid_ll_driver sdw_hid_driver = { .raw_request = sdwhid_raw_request, }; -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +int sdca_add_hid_device(struct sdca_interrupt *interrupt) { - struct sdw_bus *bus = sdw->bus; + struct device *dev = interrupt->dev; + struct sdca_function_data *function = interrupt->function; + struct sdca_entity *entity = interrupt->entity; struct hid_device *hid; int ret; @@ -102,12 +104,9 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, hid->bus = BUS_SDW; hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); - snprintf(hid->name, sizeof(hid->name), - "HID sdw:%01x:%01x:%04x:%04x:%02x", - bus->controller_id, bus->link_id, sdw->id.mfg_id, - sdw->id.part_id, sdw->id.class_id); - - snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); + strscpy(hid->phys, dev_name(dev)); + snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", + function->desc->name, function->desc->adr); hid->driver_data = entity; @@ -118,7 +117,7 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, return ret; } - entity->hide.hid = hid; + interrupt->priv = hid; return 0; } @@ -133,7 +132,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); int sdca_hid_process_report(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; - struct hid_device *hid = interrupt->entity->hide.hid; + struct hid_device *hid = interrupt->priv; void *val __free(kfree) = NULL; int len, ret; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 42fbd3af8a75..71037189a057 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -487,6 +487,10 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, } break; case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + interrupt->handler = hid_handler; break; default: From f3243b79026e31da037d1cbbbbda44317e3cbcb2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:01 +0100 Subject: [PATCH 581/791] ASoC: SDCA: Move HID descriptors to function The HID descriptors are defined at the function level in DisCo and as such it makes more sense to parse and store them at that level in the SDCA code. This shouldn't really make much practical difference but is conceptually better and avoids passing the function node down to the entity parsing code. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 19 ++++++++-- sound/soc/sdca/sdca_functions.c | 65 ++++++++++++++++++++++----------- sound/soc/sdca/sdca_hid.c | 11 +++--- 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 35799a977145..72441ed5eba4 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1126,8 +1126,6 @@ struct sdca_entity_ge { * within this Device * @max_delay: the maximum time in microseconds allowed for the Device * to change the ownership from Device to Host - * @hid_report_desc: HID Report Descriptor for the HIDE Entity - * @hid_desc: HID descriptor for the HIDE Entity */ struct sdca_entity_hide { unsigned int *hidtx_ids; @@ -1137,8 +1135,6 @@ struct sdca_entity_hide { unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; unsigned int hide_reside_function_num; unsigned int max_delay; - unsigned char *hid_report_desc; - struct hid_descriptor hid_desc; }; /** @@ -1399,6 +1395,16 @@ struct sdca_fdl_data { int num_sets; }; +/** + * struct sdca_function_hid - information about a function's HID descriptors + * @report_desc: HID Report Descriptor for the HID Function + * @desc: HID descriptor for the HID Function + */ +struct sdca_function_hid { + unsigned char *report_desc; + struct hid_descriptor desc; +}; + /** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. @@ -1413,6 +1419,7 @@ struct sdca_fdl_data { * @reset_max_delay: Maximum Function reset delay in microseconds, before an * error should be reported. * @fdl_data: FDL data for this Function, if available. + * @hid: HID data for this Function, if available. */ struct sdca_function_data { struct sdca_function_desc *desc; @@ -1428,6 +1435,10 @@ struct sdca_function_data { unsigned int reset_max_delay; struct sdca_fdl_data fdl_data; + + union { + struct sdca_function_hid hid; + }; }; static inline u32 sdca_range(struct sdca_control_range *range, diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e9b449b67033..e49acfe49e28 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1364,14 +1364,13 @@ static int find_sdca_entity_ge(struct device *dev, return -EINVAL; } -static int -find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, - struct fwnode_handle *entity_node, struct sdca_entity *entity) +static int find_sdca_entity_hide(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; unsigned int delay, *af_list = hide->af_number_list; int nval, ret; - unsigned char *report_desc = NULL; ret = fwnode_property_read_u32(entity_node, "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); @@ -1424,23 +1423,6 @@ find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, fwnode_property_read_u32_array(entity_node, "mipi-sdca-hide-related-audio-function-list", af_list, nval); - nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); - if (nval) - fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", - (u8 *)&hide->hid_desc, nval); - - if (hide->hid_desc.bNumDescriptors) { - nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); - if (nval) { - report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); - if (!report_desc) - return -ENOMEM; - hide->hid_report_desc = report_desc; - fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", - report_desc, nval); - } - } - return 0; } @@ -1518,7 +1500,7 @@ static int find_sdca_entity(struct device *dev, struct sdca_function_data *funct ret = find_sdca_entity_ge(dev, entity_node, entity); break; case SDCA_ENTITY_TYPE_HIDE: - ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); + ret = find_sdca_entity_hide(dev, entity_node, entity); break; default: break; @@ -2167,6 +2149,35 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, return 0; } +static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + int nval; + + nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (nval) + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&function->hid.desc, nval); + + if (function->hid.desc.bNumDescriptors) { + nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (nval) { + unsigned char *report_desc; + + report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); + if (!report_desc) + return -ENOMEM; + + function->hid.report_desc = report_desc; + fwnode_property_read_u8_array(function_node, + "mipi-sdca-report-descriptor", + report_desc, nval); + } + } + + return 0; +} + /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. @@ -2218,6 +2229,16 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret) return ret; + switch (function->desc->type) { + case SDCA_FUNCTION_TYPE_HID: + ret = find_sdca_hid(dev, node, function); + if (ret) + return ret; + break; + default: + break; + } + return 0; } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index ea511c6f9798..5000d73657b4 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -24,18 +24,18 @@ static int sdwhid_parse(struct hid_device *hid) { - struct sdca_entity *entity = hid->driver_data; + struct sdca_function_data *function = hid->driver_data; unsigned int rsize; int ret; - rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); + rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); return -EINVAL; } - ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); + ret = hid_parse_report(hid, function->hid.report_desc, rsize); if (!ret) return 0; @@ -90,7 +90,6 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; struct sdca_function_data *function = interrupt->function; - struct sdca_entity *entity = interrupt->entity; struct hid_device *hid; int ret; @@ -102,13 +101,13 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) hid->dev.parent = dev; hid->bus = BUS_SDW; - hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); + hid->version = le16_to_cpu(function->hid.desc.bcdHID); strscpy(hid->phys, dev_name(dev)); snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", function->desc->name, function->desc->adr); - hid->driver_data = entity; + hid->driver_data = function; ret = hid_add_device(hid); if (ret && ret != -ENODEV) { From 01dba3e93486d333c2a192b5250c1ed4dbb3093b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:02 +0100 Subject: [PATCH 582/791] ASoC: SDCA: Update HID DisCo parsing Add more error checking on the parsing of the HID DisCo and bring the code more inline with the rest of the DisCo parsing. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 125 ++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e49acfe49e28..1196cc09389a 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -1369,59 +1370,71 @@ static int find_sdca_entity_hide(struct device *dev, struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; - unsigned int delay, *af_list = hide->af_number_list; - int nval, ret; + int num_reports, ret; + unsigned int delay; ret = fwnode_property_read_u32(entity_node, - "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); + "mipi-sdca-RxUMP-ownership-transition-max-delay", + &delay); if (!ret) hide->max_delay = delay; - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); - if (nval > 0) { - hide->num_hidtx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDTx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidtx_ids = num_reports; hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, sizeof(*hide->hidtx_ids), GFP_KERNEL); if (!hide->hidtx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDTx-supported-report-ids", - hide->hidtx_ids, - hide->num_hidtx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDTx-supported-report-ids", + hide->hidtx_ids, hide->num_hidtx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); - if (nval > 0) { - hide->num_hidrx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDRx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidrx_ids = num_reports; hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, sizeof(*hide->hidrx_ids), GFP_KERNEL); if (!hide->hidrx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDRx-supported-report-ids", - hide->hidrx_ids, - hide->num_hidrx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDRx-supported-report-ids", + hide->hidrx_ids, hide->num_hidrx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); - if (nval <= 0) { + /* + * FIXME: This should probably link to the actual sdca_function_data pointer, + * but updating to do so should probably wait until we have a user. + */ + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-hide-related-audio-function-list"); + if (num_reports <= 0) { dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", - entity_node, nval); + entity_node, num_reports); return -EINVAL; - } else if (nval > SDCA_MAX_FUNCTION_COUNT) { - dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); + } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { + dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", + entity_node); return -EINVAL; } - hide->hide_reside_function_num = nval; + hide->hide_reside_function_num = num_reports; fwnode_property_read_u32_array(entity_node, - "mipi-sdca-hide-related-audio-function-list", af_list, nval); + "mipi-sdca-hide-related-audio-function-list", + hide->af_number_list, num_reports); return 0; } @@ -2152,29 +2165,45 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { - int nval; + int num_desc; - nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); - if (nval) - fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", - (u8 *)&function->hid.desc, nval); - - if (function->hid.desc.bNumDescriptors) { - nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); - if (nval) { - unsigned char *report_desc; - - report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); - if (!report_desc) - return -ENOMEM; - - function->hid.report_desc = report_desc; - fwnode_property_read_u8_array(function_node, - "mipi-sdca-report-descriptor", - report_desc, nval); - } + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (!num_desc) { + return 0; + } else if (num_desc < 0) { + dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", + function_node, num_desc); + return num_desc; + } else if (num_desc > sizeof(function->hid.desc)) { + dev_err(dev, "%pfwP: hid descriptor too large: %d\n", + function_node, num_desc); + return -EINVAL; } + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&function->hid.desc, num_desc); + + if (!function->hid.desc.bNumDescriptors) + return 0; + + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (num_desc <= 0) { + dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", + function_node, num_desc); + + if (!num_desc) + return -EINVAL; + + return num_desc; + } + + function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); + if (!function->hid.report_desc) + return -ENOMEM; + + fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", + function->hid.report_desc, num_desc); + return 0; } From df1fd5d8ab403c264b364abe187ce94b2fa7b96f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:03 +0100 Subject: [PATCH 583/791] ASoC: SDCA: Add missing destroy for HID device The SDCA code is currently missing a cleanup for HID devices when the drivers are unbound. Add the missing HID cleanup as part of the IRQ cleanup to mirror when the HID device is created. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_hid.h | 5 +++++ sound/soc/sdca/sdca_hid.c | 12 ++++++++++++ sound/soc/sdca/sdca_interrupts.c | 2 ++ 3 files changed, 19 insertions(+) diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h index 83d1c7768133..848081df3e4e 100644 --- a/include/sound/sdca_hid.h +++ b/include/sound/sdca_hid.h @@ -17,6 +17,7 @@ struct sdca_interrupt; #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) int sdca_add_hid_device(struct sdca_interrupt *interrupt); +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); int sdca_hid_process_report(struct sdca_interrupt *interrupt); #else @@ -26,6 +27,10 @@ static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) return 0; } +static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) { return 0; diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index 5000d73657b4..514f895bd90d 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -122,6 +122,18 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); +/** + * sdca_destroy_hid_device - destroy the HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + */ +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct hid_device *hid = interrupt->priv; + + hid_destroy_device(hid); +} +EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); + /** * sdca_hid_process_report - read a HID event from the device and report * @interrupt: Pointer to the SDCA interrupt information structure. diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 71037189a057..7aebc721a847 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -487,6 +487,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, } break; case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + interrupt->free_priv = sdca_destroy_hid_device; + ret = sdca_add_hid_device(interrupt); if (ret) return ret; From 3ede9e98ca1c85c176b1ee3de664495790e84ea5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:04 +0100 Subject: [PATCH 584/791] ASoC: SDCA: Add missing HID kernel doc Add missing kernel doc for the function sdca_add_hid_device() Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_hid.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index 514f895bd90d..bee1b83c05f2 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -86,6 +86,12 @@ static const struct hid_ll_driver sdw_hid_driver = { .raw_request = sdwhid_raw_request, }; +/** + * sdca_add_hid_device - create a new SDCA HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + * + * Return: Zero on success, and a negative error code on failure. + */ int sdca_add_hid_device(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; From a50e530e05fad766003f84c0e206058c6efd70c8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:05 +0100 Subject: [PATCH 585/791] ASoC: SDCA: Pass swft table through sdca_dev_register() Rather than passing the SoundWire slave into find_sdca_filesets(), stash the swift table whilst processing sdca_dev_register(). This allows us to completely remove the passing of the sdw_slave into the ACPI parsing code. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-9-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 3 +-- sound/soc/codecs/rt766-sdca.c | 2 +- sound/soc/codecs/tac5xx2-sdw.c | 2 +- sound/soc/codecs/tas2783-sdw.c | 2 +- sound/soc/sdca/sdca_class_function.c | 2 +- sound/soc/sdca/sdca_function_device.c | 7 +++++-- sound/soc/sdca/sdca_functions.c | 10 +++------- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 72441ed5eba4..f65a1d6784e8 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1460,8 +1460,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, return 0; } -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function); +int sdca_parse_function(struct device *dev, struct sdca_function_data *function); const char *sdca_find_terminal_name(enum sdca_terminal_type type); diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 54ed0c42fba2..64d763b96a06 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -1201,7 +1201,7 @@ int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave } func_data_ptr->desc = &slave->sdca_data.function[i]; - ret = sdca_parse_function(dev, slave, func_data_ptr); + ret = sdca_parse_function(dev, func_data_ptr); if (ret) { devm_kfree(dev, func_data_ptr); goto _free_dai_drv_; diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index ea0408b71713..fdb6213d1360 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -1940,7 +1940,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, "failed to allocate %s function data", func_name); function_data->desc = &peripheral->sdca_data.function[i]; - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) *func_ptr = function_data; else diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 34f17e063fa3..94f11e3b0c20 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1348,7 +1348,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, function_data->desc = &peripheral->sdca_data.function[i]; /* Parse the function */ - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) tas_dev->sa_func_data = function_data; else diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 2fb2b043c979..cc7045dc26e6 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, drv->core = core; drv->function = &sdev->function; - ret = sdca_parse_function(dev, core->sdw, drv->function); + ret = sdca_parse_function(dev, drv->function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c index b5ca98283a88..54604872ae0d 100644 --- a/sound/soc/sdca/sdca_function_device.c +++ b/sound/soc/sdca/sdca_function_device.c @@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) /* alloc, init and add link devices */ static struct sdca_dev *sdca_dev_register(struct device *parent, - struct sdca_function_desc *function_desc) + struct sdca_function_desc *function_desc, + struct acpi_table_swft *swft) { struct sdca_dev *sdev; struct auxiliary_device *auxdev; @@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, auxdev->dev.release = sdca_dev_release; sdev->function.desc = function_desc; + sdev->function.fdl_data.swft = swft; rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); if (rc < 0) { @@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) struct sdca_dev *func_dev; func_dev = sdca_dev_register(&slave->dev, - &sdca_data->function[i]); + &sdca_data->function[i], + sdca_data->swft); if (IS_ERR(func_dev)) { ret = PTR_ERR(func_dev); /* diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 1196cc09389a..e01d91eb3cc8 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -2072,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, return 0; } -static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { static const int mult_fileset = 3; @@ -2155,7 +2154,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, set->files = files; } - function->fdl_data.swft = sdw->sdca_data.swft; function->fdl_data.num_sets = num_sets; function->fdl_data.sets = sets; @@ -2210,13 +2208,11 @@ static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. - * @sdw: SoundWire slave device to be processed. * @function: Pointer to the Function information, to be populated. * * Return: Returns 0 for success. */ -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function) +int sdca_parse_function(struct device *dev, struct sdca_function_data *function) { struct fwnode_handle *node = function->desc->node; u32 tmp; @@ -2254,7 +2250,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret < 0) return ret; - ret = find_sdca_filesets(dev, sdw, node, function); + ret = find_sdca_filesets(dev, node, function); if (ret) return ret; From 02442d5fe8ee365a084b055d4fa81a0c1abfc3fd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 12:04:31 +0200 Subject: [PATCH 586/791] ALSA: dummy: Check card index validity at probe snd_dummy_probe() blindly trusts that the given devptr->id value is within the proper card index range. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Reported-by: syzbot+2fb5d1f7cc4c1f132bcc@syzkaller.appspotmail.com Closes: https://lore.kernel.org/6a73bd4d.01d0871a.3a0d52.0005.GAE@google.com Cc: Link: https://patch.msgid.link/20260806100433.1287393-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/drivers/dummy.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 8836799727ea..022247354732 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1042,6 +1042,12 @@ static int snd_dummy_probe(struct platform_device *devptr) int idx, err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_dummy), &card); if (err < 0) From 9c04742e73b32fb3912e1d9fb9f804affb340dce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 12:13:51 +0200 Subject: [PATCH 587/791] ALSA: rawmidi: Work around false-positive mutex lockdep warning When opening a legacy rawmidi device for a UMP, it may re-open an existing rawmidi device for appending to a substream, leading to a lockdep warning due to rmidi->open_mutex taken twice -- but the rawmidi devices are completely individual, hence it's a false-positive. For avoiding the warning, modify the helper to open a rawmidi instance with a proper locking subclass from the UMP legacy open. Unfortunately, there is no good way to achieve it with guard(), so reverted to the manual mutex calls again. Reported-by: syzbot+d10d58fc99caa0489796@syzkaller.appspotmail.com Closes: https://lore.kernel.org/6a6a9634.57649fcc.360844.000b.GAE@google.com Link: https://patch.msgid.link/20260806101352.1291581-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/rawmidi.h | 14 ++++++++++++-- sound/core/rawmidi.c | 12 +++++++----- sound/core/ump.c | 9 +++++---- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 6916f7133597..88a6159364d0 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -176,8 +176,9 @@ int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream); /* main midi functions */ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info); -int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile); +int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth); int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params *params); @@ -191,6 +192,15 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream, const unsigned char *buf, long count); +/* non-nested version */ +static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, + int subdevice, + int mode, + struct snd_rawmidi_file *rfile) +{ + return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); +} + /* set up the tied devices */ static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, struct snd_rawmidi *r2) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 1d55da2dcb01..bf504e27f73e 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -408,9 +408,10 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode, return 0; } -/* called from sound/core/seq/seq_midi.c */ -int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile) +/* called from sound/core/seq/seq_midi.c and sound/core/ump.c */ +int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth) { int err; @@ -419,13 +420,14 @@ int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, if (!try_module_get(rmidi->card->module)) return -ENXIO; - guard(mutex)(&rmidi->open_mutex); + mutex_lock_nested(&rmidi->open_mutex, depth); err = rawmidi_open_priv(rmidi, subdevice, mode, rfile); if (err < 0) module_put(rmidi->card->module); + mutex_unlock(&rmidi->open_mutex); return err; } -EXPORT_SYMBOL(snd_rawmidi_kernel_open); +EXPORT_SYMBOL(snd_rawmidi_kernel_open_nested); static int snd_rawmidi_open(struct inode *inode, struct file *file) { diff --git a/sound/core/ump.c b/sound/core/ump.c index 632c13baf21e..82ad155c56e6 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1157,10 +1157,11 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) return -ENODEV; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!ump->legacy_out_opens) { - err = snd_rawmidi_kernel_open(&ump->core, 0, - SNDRV_RAWMIDI_LFLG_OUTPUT | - SNDRV_RAWMIDI_LFLG_APPEND, - &ump->legacy_out_rfile); + err = snd_rawmidi_kernel_open_nested(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, + &ump->legacy_out_rfile, + SINGLE_DEPTH_NESTING); if (err < 0) return err; } From 2a611c4a1cbcb179cd8079a7ccadee390dac66f6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 12:44:14 +0200 Subject: [PATCH 588/791] ALSA: hda: cix-ipbloq: Avoid build with 32bit archs The cix-ipbloq driver has an assumption of 64bit DMA address, and building it for 32bit dma_addr_t leads to a sparse / compile warning. Simply disable the builds for 32bit archs for avoiding such reports. Fixes: d91e9bd10125 ("ALSA: hda: add CIX IPBLOQ HDA controller support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202608061559.Kxqvi5LZ-lkp@intel.com/ Link: https://patch.msgid.link/20260806104431.1300304-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/controllers/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 5d6a77e68588..26ca69b09a9c 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -33,6 +33,7 @@ config SND_HDA_TEGRA config SND_HDA_CIX_IPBLOQ tristate "CIX IPBLOQ HD Audio" depends on ARCH_CIX || COMPILE_TEST + depends on ARCH_DMA_ADDR_T_64BIT select SND_HDA select SND_HDA_ALIGNED_MMIO help From dac366eb036e179addf51f08f4cf2fcde007a904 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Thu, 6 Aug 2026 11:04:59 +0530 Subject: [PATCH 589/791] ASoC: tac5xx2-sdw: add rev_id 0x30 support * HID interrupts: SDCA_12/SDCA_17 instead of was SDCA_11/SDCA_16 in in older revision. * UAJ port prepare: write 0xff (jack connected) or 0xdf (disconnected) This is required to solve the channel prepare timeout error during boot time with the 0x30 silicon. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20260806053500.1955-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tac5xx2-sdw.c | 58 ++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index fdb6213d1360..3ebb26f00801 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -88,6 +88,9 @@ #define TAC_FW_FILE_HDR 20 #define TAC_MAX_FW_CHUNKS 512 +#define TAC_UAJ_PREP_CONNECTED 0xff +#define TAC_UAJ_PREP_DISCONNECTED 0xdf + struct tac_fw_hdr { u32 size; u32 version_offset; @@ -138,6 +141,7 @@ struct tac5xx2_prv { bool hw_init; bool first_hw_init_done; u32 part_id; + u32 rev_id; struct snd_soc_jack *hs_jack; int jack_type; /* Custom fw binary. UMP File Download is not used. */ @@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { {TAC_REG_SDW(0, 0, 0x78), 0x0}, {TAC_REG_SDW(0, 0, 0x7b), 0x0}, {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, - {TAC_REG_SDW(0, 0, 0x7d), 0x0}, {TAC_REG_SDW(0, 0, 0x7e), 0x0}, {TAC_REG_SDW(0, 1, 0x1), 0x0}, {TAC_REG_SDW(0, 1, 0x2), 0x0}, @@ -942,6 +945,7 @@ static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) { int val, ret; + u8 jack_prep; ret = regmap_read(tac_dev->regmap, SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, @@ -951,6 +955,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) return ret; } + jack_prep = TAC_UAJ_PREP_CONNECTED; + switch (val) { case 4: tac_dev->jack_type = SND_JACK_MICROPHONE; @@ -964,6 +970,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) case 0: default: tac_dev->jack_type = 0; + jack_prep = TAC_UAJ_PREP_DISCONNECTED; break; } @@ -973,26 +980,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) if (ret) dev_err(tac_dev->dev, "Failed to update the jack type to device"); + /* + * When uaj is uplugged and booted, we end up with the channel prepare + * timeout error for the uaj ports. This writes allows the channel prepare + * to succeed when the uaj is not plugged in. + */ + if (tac_dev->rev_id >= 0x30) { + ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); + if (ret) + dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); + } + return 0; } static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) { + u32 jd_int_mask, hid_int_mask; int ret = 0; + if (tac_dev->rev_id >= 0x30) { + jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; + hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; + } else { + jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; + hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; + } + if (!tac_dev->hs_jack) goto disable_interrupts; - ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, - SDW_SCP_SDCA_INTMASK_SDCA_11); + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); if (ret) { dev_err(tac_dev->dev, "Failed to register jack detection interrupt: %d\n", ret); goto disable_interrupts; } - ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, - SDW_SCP_SDCA_INTMASK_SDCA_16); + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); if (ret) { dev_err(tac_dev->dev, "Failed to register for button detect interrupt: %d\n", ret); @@ -1051,6 +1076,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); struct device *dev = &slave->dev; + u32 headset_detect_chk, hid_detect_chk; int btn_type = 0; int ret = 0; @@ -1081,14 +1107,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", sdca_int2, sdca_int3); - if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { + if (tac_dev->rev_id >= 0x30) { + headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; + hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; + } else { + headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; + hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; + } + + if (sdca_int2 & headset_detect_chk) { ret = tac5xx2_sdca_headset_detect(tac_dev); if (ret < 0) goto clear; jack_report_mask |= SND_JACK_HEADSET; } - if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { + if (sdca_int3 & hid_detect_chk) { btn_type = tac5xx2_sdca_button_detect(tac_dev); if (btn_type < 0) btn_type = 0; @@ -1695,6 +1729,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) goto io_init_err; } + if (!tac_dev->rev_id) { + ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); + if (ret) { + dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); + goto io_init_err; + } + dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); + } + if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { ret = tac_download_fw_to_hw(tac_dev); if (ret) { @@ -1959,6 +2002,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, tac_dev->hw_init = false; tac_dev->first_hw_init_done = false; tac_dev->part_id = id->part_id; + tac_dev->rev_id = 0x0; dev_set_drvdata(dev, tac_dev); regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, From 92dd0ba8ac4bb379e86cc325a7a52ded08982b1e Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Thu, 6 Aug 2026 11:05:00 +0530 Subject: [PATCH 590/791] ASoC: tac5xx2-sdw: remove firmware check restriction Firmware support should be available for all the devices in the family so that basic pre-processing blocks can be tuned with the firmware if it is available. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20260806053500.1955-2-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tac5xx2-sdw.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index 3ebb26f00801..c062065a807d 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -510,18 +510,6 @@ static const struct regmap_config tac_regmap = { .use_single_write = true, }; -/* Check if device has DSP algo that needs status monitoring */ -static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) -{ - switch (tac_dev->part_id) { - case 0x5682: - case 0x2883: - return true; - default: - return false; - } -} - /* Check if device has UAJ (Universal Audio Jack) support */ static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) { @@ -2016,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, tac_dev->jack_type = 0; init_completion(&tac_dev->fw_caching_complete); - if (tac_has_dsp_algo(tac_dev)) { - tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, - sizeof(tac_dev->fw_binaryname)); + tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, + sizeof(tac_dev->fw_binaryname)); - ret = tac_load_and_cache_firmware_async(tac_dev); - if (ret) { - complete_all(&tac_dev->fw_caching_complete); - dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); - } - } else { + ret = tac_load_and_cache_firmware_async(tac_dev); + if (ret) { complete_all(&tac_dev->fw_caching_complete); + dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); } ret = tac_init(tac_dev); From 819b106a9fd2ef3fd8abf898b9a8e4524eca8f48 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:18 +0200 Subject: [PATCH 591/791] ALSA: aloop: Check card index validity at probe aloop driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-2-tiwai@suse.de --- sound/drivers/aloop.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index d520d83c2577..92ef821ddbeb 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1914,6 +1914,12 @@ static int loopback_probe(struct platform_device *devptr) int dev = devptr->id; int err; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct loopback), &card); if (err < 0) From f7dcecb92ed192ff5fcf842918fb1aaea84b5bdd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:19 +0200 Subject: [PATCH 592/791] ALSA: mpu401: Check card index validity at probe mpu401 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-3-tiwai@suse.de --- sound/drivers/mpu401/mpu401.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index c217c427bf1e..b93291ad067a 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platform_device *devptr) int err; struct snd_card *card; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (port[dev] == SNDRV_AUTO_PORT) { dev_err(&devptr->dev, "specify port\n"); return -EINVAL; From e0fb960b227fcdebe22e4f26c9486d60943c0424 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:20 +0200 Subject: [PATCH 593/791] ALSA: serial-u16550: Check card index validity at probe serial-u16550 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-4-tiwai@suse.de --- sound/drivers/serial-u16550.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 3c28961091b1..cc9c325df910 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -846,6 +846,12 @@ static int snd_serial_probe(struct platform_device *devptr) int err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + switch (adaptor[dev]) { case SNDRV_SERIAL_SOUNDCANVAS: ins[dev] = 1; From b65d5182ecd6b7a24a83d980a0d06e809ef876c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:21 +0200 Subject: [PATCH 594/791] ALSA: virmidi: Check card index validity at probe virmidi driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-5-tiwai@suse.de --- sound/drivers/virmidi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index a204f42d1026..70e16105f1c4 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) int idx, err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_card_virmidi), &card); if (err < 0) From d18a260720f86a5f8b5fcfefc4ba2e9dd01c10f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:22 +0200 Subject: [PATCH 595/791] ALSA: mts64: Check card index validity at probe Although mts64 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-6-tiwai@suse.de --- sound/drivers/mts64.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 36e9eab204ca..cefaa00b83e7 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -900,6 +900,12 @@ static int snd_mts64_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) From 3690ef20469d5959378260e2752f2314a2572913 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 17:32:23 +0200 Subject: [PATCH 596/791] ALSA: portman2x4: Check card index validity at probe Although portman2x4 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters. Add a sanity check for the card index and warn/correct it if it's a value out of the range. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806153227.1460166-7-tiwai@suse.de --- sound/drivers/portman2x4.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index dcc0899cfb99..03ed3c3c27fb 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -697,6 +697,12 @@ static int snd_portman_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) From 6d29372998a308bb77e91548b69c9de64850d648 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:12 -0700 Subject: [PATCH 597/791] ASoC: codecs: NeoFidelity: repair the kernel-doc format Don't use "/**" for a non-kernel-doc comment. Use kernel-doc notation to document the parameters and return value of ntpfw_load(). Fixes these warnings: Warning: ../sound/soc/codecs/ntpfw.h:2 This comment starts with '/**', but isn't a kernel-doc comment. * ntpfw.h - Firmware helper functions for Neofidelity codecs Warning: sound/soc/codecs/ntpfw.h:20 function parameter 'i2c' not described in 'ntpfw_load' Warning: sound/soc/codecs/ntpfw.h:20 function parameter 'name' not described in 'ntpfw_load' Warning: sound/soc/codecs/ntpfw.h:20 function parameter 'magic' not described in 'ntpfw_load' Warning: sound/soc/codecs/ntpfw.h:20 No description found for return value of 'ntpfw_load' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-2-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/codecs/ntpfw.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h index 1cf10d5480ee..efbe9aa1eca5 100644 --- a/sound/soc/codecs/ntpfw.h +++ b/sound/soc/codecs/ntpfw.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** +/* * ntpfw.h - Firmware helper functions for Neofidelity codecs * * Copyright (c) 2024, SaluteDevices. All Rights Reserved. @@ -13,10 +13,11 @@ /** * ntpfw_load - load firmware to amplifier over i2c interface. * - * @i2c Pointer to amplifier's I2C client. - * @name Firmware file name. - * @magic Magic number to validate firmware. - * @return 0 or error code upon error. + * @i2c: Pointer to amplifier's I2C client. + * @name: Firmware file name. + * @magic: Magic number to validate firmware. + * + * Returns: 0 or error code upon error. */ int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); From a4ed0860e3a66e12e174f4f64153428134f997db Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:14 -0700 Subject: [PATCH 598/791] ASoC: fsl_asrc: avoid kernel-doc warnings Use the struct keyword to describe structs in kernel-doc format. This prevents kernel-doc warnings: Warning: ../sound/soc/fsl/fsl_asrc.h:452 cannot understand function prototype: 'struct fsl_asrc_soc_data' Warning: ../sound/soc/fsl/fsl_asrc.h:463 cannot understand function prototype: 'struct fsl_asrc_pair_priv' Warning: ../sound/soc/fsl/fsl_asrc.h:475 cannot understand function prototype: 'struct fsl_asrc_priv' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-4-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 7a81366a0ee4..f57e684d1cca 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -444,7 +444,7 @@ struct dma_block { }; /** - * fsl_asrc_soc_data: soc specific data + * struct fsl_asrc_soc_data - soc specific data * * @use_edma: using edma as dma device or not * @channel_bits: width of ASRCNCR register for each pair @@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { }; /** - * fsl_asrc_pair_priv: ASRC Pair private data + * struct fsl_asrc_pair_priv - ASRC Pair private data * * @config: configuration profile */ @@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { }; /** - * fsl_asrc_priv: ASRC private data + * struct fsl_asrc_priv - ASRC private data * * @asrck_clk: clock sources to driver ASRC internal logic * @soc: soc specific data From 11c3fb527b8bc27dc8d801f43133e1b3af61804e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:15 -0700 Subject: [PATCH 599/791] ASoC: fsl-dma: fix all kernel-doc warnings Don't use "/**" for non-kernel-doc comments to avoid kernel-doc warnings: Warning: ../sound/soc/fsl/fsl_dma.h:95 This comment starts with '/**', but isn't a kernel-doc comment. * List Descriptor for extended chaining mode DMA operations. Warning: ../sound/soc/fsl/fsl_dma.h:110 This comment starts with '/**', but isn't a kernel-doc comment. * Link Descriptor for basic and extended chaining mode DMA operations. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-5-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h index f19ae765b656..928d91e5236c 100644 --- a/sound/soc/fsl/fsl_dma.h +++ b/sound/soc/fsl/fsl_dma.h @@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) #define CCSR_DMA_ATR_SNOOP 0x00050000 #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F -/** +/* * List Descriptor for extended chaining mode DMA operations. * * The CLSDAR register points to the first (in a linked-list) List @@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { u8 res[8]; /* Reserved */ } __attribute__ ((aligned(32), packed)); -/** +/* * Link Descriptor for basic and extended chaining mode DMA operations. * * A Link Descriptor points to a single DMA buffer. Each link descriptor From 6a59c9a79aa06e860976d68cc2952018d8b8c11f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:16 -0700 Subject: [PATCH 600/791] ASoC: fsl_easrc: use struct keyword on structs Use the documented format for kernel-doc of structs to prevent kernel-doc warnings: Warning: ../sound/soc/fsl/fsl_easrc.h:606 cannot understand function prototype: 'struct fsl_easrc_ctx_priv' Warning: ../sound/soc/fsl/fsl_easrc.h:641 cannot understand function prototype: 'struct fsl_easrc_priv' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-6-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index c9f770862662..36569cf8be9a 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -584,7 +584,7 @@ struct fsl_easrc_slot { }; /** - * fsl_easrc_ctx_priv: EASRC context private data + * struct fsl_easrc_ctx_priv - EASRC context private data * * @in_params: input parameter * @out_params: output parameter @@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { }; /** - * fsl_easrc_priv: EASRC private data + * struct fsl_easrc_priv - EASRC private data * * @slot: slot setting * @firmware_hdr: the header of firmware From 728b72e10742b71e5b023ac96c85ab17153b75cd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:18 -0700 Subject: [PATCH 601/791] ASoC: fsl: mpc5200_psc_i2s: avoid kernel-doc warnings Add missing kernel-doc for function parameters. Use kernel-doc format for function return value descriptions. Use the "var" keyword to describe a data definition. These changes avoid all kernel-doc warnings in this file: Warning: ../sound/soc/fsl/mpc5200_psc_i2s.c:123 cannot understand function prototype: 'const struct snd_soc_dai_ops psc_i2s_dai_ops =' Warning: sound/soc/fsl/mpc5200_psc_i2s.c:87 function parameter 'cpu_dai' not described in 'psc_i2s_set_sysclk' Warning: sound/soc/fsl/mpc5200_psc_i2s.c:87 No description found for return value of 'psc_i2s_set_sysclk' Warning: sound/soc/fsl/mpc5200_psc_i2s.c:106 function parameter 'cpu_dai' not described in 'psc_i2s_set_fmt' Warning: sound/soc/fsl/mpc5200_psc_i2s.c:106 No description found for return value of 'psc_i2s_set_fmt' Warning: sound/soc/fsl/mpc5200_psc_i2s.c:123 cannot understand function prototype: 'const struct snd_soc_dai_ops psc_i2s_dai_ops =' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-8-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_i2s.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 55a12be6ad18..c024a38e3b9c 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, * and we don't care about the frequency. Return an error if the direction * is not SND_SOC_CLOCK_IN. * + * @cpu_dai: DAI runtime data pointer * @clk_id: reserved, should be zero * @freq: the frequency of the given clock ID, currently ignored * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) + * + * Returns: %0 on success or %-EINVAL on failure. */ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) @@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, * This driver only supports I2S mode. Return an error if the format is * not SND_SOC_DAIFMT_I2S. * + * @cpu_dai: DAI runtime data pointer * @format: one of SND_SOC_DAIFMT_xxx + * + * Returns: %0 on success or %-EINVAL on failure. */ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) { @@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) */ /** - * psc_i2s_dai_template: template CPU Digital Audio Interface + * var psc_i2s_dai_ops - template CPU Digital Audio Interface */ static const struct snd_soc_dai_ops psc_i2s_dai_ops = { .hw_params = psc_i2s_hw_params, From f881c12ca1f0434b3262b23748f2c75107961060 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:20 -0700 Subject: [PATCH 602/791] ASoC: uniphier: don't use "/**" for non-kernel-doc comment Use a C-style "/*" comment to avoid multiple kernel-doc warnings: Warning: ../sound/soc/uniphier/aio.h:159 Cannot find identifier on line: * 'SoftWare MAPping' setting of UniPhier AIO registers. Warning: ../sound/soc/uniphier/aio.h:160 Cannot find identifier on line: * Warning: ../sound/soc/uniphier/aio.h:161 This comment starts with '/**', but isn't a kernel-doc comment. * We have to setup 'virtual' register maps to access 'real' registers of AIO. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-10-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/uniphier/aio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index d9fd61dd976f..1900ea01ce3d 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -156,7 +156,7 @@ struct uniphier_aio_selector { int hw; }; -/** +/* * 'SoftWare MAPping' setting of UniPhier AIO registers. * * We have to setup 'virtual' register maps to access 'real' registers of AIO. From ff322994c76c53d7fd2353c7c7c328bec6171510 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:21 -0700 Subject: [PATCH 603/791] ASoC: SDCA: correct enum names and add a missing struct field Add a kernel-doc comment for @is_volatile in struct sdca_control. Correct 2 malformed enum names to match the enums. Fixes 3 warnings: Warning: include/sound/sdca_function.h:306 expecting prototype for enum sdca_set_index_range. Prototype was for enum sdca_fdl_set_index_range instead Warning: include/sound/sdca_function.h:829 struct member 'is_volatile' not described in 'sdca_control' Warning: include/sound/sdca_function.h:1152 expecting prototype for enum sdca_xu_reset_machanism. Prototype was for enum sdca_xu_reset_mechanism instead Signed-off-by: Randy Dunlap Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260715000525.739874-11-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index f65a1d6784e8..e580201d69d5 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -298,7 +298,7 @@ enum sdca_xu_controls { }; /** - * enum sdca_set_index_range - Column definitions UMP SetIndex + * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex */ enum sdca_fdl_set_index_range { SDCA_FDL_SET_INDEX_SET_NUMBER = 0, @@ -803,6 +803,8 @@ struct sdca_control_range { * @mode: Access mode of the Control. * @layers: Bitmask of access layers of the Control. * @deferrable: Indicates if the access to the Control can be deferred. + * @is_volatile: Indicates the Control registers are forced to be treated + * as volatile. * @has_default: Indicates the Control has a default value to be written. * @has_reset: Indicates the Control has a defined reset value. * @has_fixed: Indicates the Control only supports a single value. @@ -1138,7 +1140,7 @@ struct sdca_entity_hide { }; /** - * enum sdca_xu_reset_machanism - SDCA FDL Resets + * enum sdca_xu_reset_mechanism - SDCA FDL Resets */ enum sdca_xu_reset_mechanism { SDCA_XU_RESET_FUNCTION = 0x0, From 62f85ba4023ba2257e397ab4c0df258176b91897 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:22 -0700 Subject: [PATCH 604/791] ASoC: soc-acpi: fix all kernel-doc warnings Add missing "struct" keyword to kernel-doc for structs. Describe @mach_params in struct snd_soc_acpi_mach. Don't document callback parameters with '@' as though they are kernel-doc. These changes avoid all kernel-doc warnings in this header file. Examples: Warning: ../include/sound/soc-acpi.h:77 cannot understand function prototype: 'struct snd_soc_acpi_mach_params' Warning: ../include/sound/soc-acpi.h:101 cannot understand function prototype: 'struct snd_soc_acpi_endpoint' Warning: ../include/sound/soc-acpi.h:115 cannot understand function prototype: 'struct snd_soc_acpi_adr_device' Warning: ../include/sound/soc-acpi.h:132 cannot understand function prototype: 'struct snd_soc_acpi_link_adr' Warning: ../include/sound/soc-acpi.h:209 cannot understand function prototype: 'struct snd_soc_acpi_mach' Warning: include/sound/soc-acpi.h:230 struct member 'mach_params' not described in 'snd_soc_acpi_mach' Warning: include/sound/soc-acpi.h:230 Excess struct member 'card' description in 'snd_soc_acpi_mach' Warning: include/sound/soc-acpi.h:230 Excess struct member 'mach' description in 'snd_soc_acpi_mach' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260715000525.739874-12-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 0519afd7217f..2ee303b791dc 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) #endif /** - * snd_soc_acpi_mach_params: interface for machine driver configuration + * struct snd_soc_acpi_mach_params - interface for machine driver configuration * * @acpi_ipc_irq_index: used for BYT-CR detection * @platform: string used for HDAudio codec support @@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { }; /** - * snd_soc_acpi_endpoint - endpoint descriptor + * struct snd_soc_acpi_endpoint - endpoint descriptor * @num: endpoint number (mandatory, unique per device) * @aggregated: 0 (independent) or 1 (logically grouped) * @group_position: zero-based order (only when @aggregated is 1) @@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { }; /** - * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device + * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device * @adr: 64 bit ACPI _ADR value * @num_endpoints: number of endpoints for this device * @endpoints: array of endpoints @@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { }; /** - * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices + * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices * @mask: one bit set indicates the link this list applies to * @num_adr: ARRAY_SIZE of devices * @adr_d: array of devices @@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) /** - * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are - * related to the hardware, except for the firmware and topology file names. + * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields + * are related to the hardware, except for the firmware and topology file names. * A platform supported by legacy and Sound Open Firmware (SOF) would expose * all firmware/topology related fields. * @@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { * the initial selection in the snd_soc_acpi_mach table. * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time + * @mach_params: machine driver configuration * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled * @tplg_quirk_mask: quirks to select different topology files dynamically * @get_function_tplg_files: This is an optional callback, if specified then instead of @@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { * files to be loaded. * Return value: The number of the files or negative ERRNO. 0 means that the single topology * file should be used, no function topology split can be used on the machine. - * @card: the pointer of the card - * @mach: the pointer of the machine driver - * @prefix: the prefix of the topology file name. Typically, it is the path. - * @tplg_files: the pointer of the array of the topology file names. - * @best_effort: ignore non supported links and try to build the card in best effort + * card: the pointer of the card + * mach: the pointer of the machine driver + * prefix: the prefix of the topology file name. Typically, it is the path. + * tplg_files: the pointer of the array of the topology file names. + * best_effort: ignore non supported links and try to build the card in best effort * with supported links */ /* Descriptor for SST ASoC machine driver */ From c53d7302023ded002ac2811ba66368edb8010c47 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:24 -0700 Subject: [PATCH 605/791] ASoC: qcom: audioreach: use C-style "/*" comment Modify the "/**" to use "/*" instead since this is not a kernel-doc comment. This avoids all kernel-doc warnings in this header file: Warning: include/uapi/sound/snd_ar_tokens.h:61 Cannot find identifier on line: * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id Warning: ../include/uapi/sound/snd_ar_tokens.h:62 Cannot find identifier on line: * Warning: ../include/uapi/sound/snd_ar_tokens.h:63 Cannot find identifier on line: * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph Warning: include/uapi/sound/snd_ar_tokens.h:64 This comment starts with '/**', but isn't a kernel-doc comment. Signed-off-by: Randy Dunlap Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260715000525.739874-14-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/uapi/sound/snd_ar_tokens.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 1700e3f5cb64..cf8ab6b85e34 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -58,7 +58,7 @@ enum ar_event_types { #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 #define SND_SOC_AR_TPLG_VOL_CTL 257 -/** +/* * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id * * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph From c185ef7749d16dc41c3cd8651369ebd11fec3312 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jul 2026 17:05:25 -0700 Subject: [PATCH 606/791] ASoC: wm8904: don't use "/**" for non-kernel-doc comments Modify these errant comments to use "/*" since they are not kernel-doc comments. Warning: include/sound/wm8904.h:119 This comment starts with '/**', but isn't a kernel-doc comment. * DRC configurations are specified with a label and a set of register Warning: ../include/sound/wm8904.h:134 This comment starts with '/**', but isn't a kernel-doc comment. * ReTune Mobile configurations are specified with a label, sample Signed-off-by: Randy Dunlap Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260715000525.739874-15-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/sound/wm8904.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h index 8b2c16b524f7..151a05b13dfc 100644 --- a/include/sound/wm8904.h +++ b/include/sound/wm8904.h @@ -116,7 +116,7 @@ #define WM8904_DRC_REGS 4 #define WM8904_EQ_REGS 24 -/** +/* * DRC configurations are specified with a label and a set of register * values to write (the enable bits will be ignored). At runtime an * enumerated control will be presented for each DRC block allowing @@ -131,7 +131,7 @@ struct wm8904_drc_cfg { u16 regs[WM8904_DRC_REGS]; }; -/** +/* * ReTune Mobile configurations are specified with a label, sample * rate and set of values to write (the enable bits will be ignored). * From 85665ff342c7432e1f77f58e005af53d23a62391 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 3 Aug 2026 13:16:04 +0700 Subject: [PATCH 607/791] ASoC: sophgo: Drop redundant error messages The called functions already log failures where appropriate. Return the original error directly and avoid duplicate error messages. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260803061605.35485-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-tdm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c index 4cbac8c1160f..a97f775aeddc 100644 --- a/sound/soc/sophgo/cv1800b-tdm.c +++ b/sound/soc/sophgo/cv1800b-tdm.c @@ -677,10 +677,8 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) return ret; ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); - if (ret) { - dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); + if (ret) return ret; - } return 0; } From 6d0451f9bf1a3d62e902bfdd68dc997409d65694 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 3 Aug 2026 13:16:05 +0700 Subject: [PATCH 608/791] ASoC: sophgo: remove unneeded devm_kmemdup() for DAI driver cv1800b_i2s_dai_template is never modified on a per-instance basis, so there is no need to duplicate it before registering the component. Pass the template directly to devm_snd_soc_register_component(), remove the now-unused local dai pointer, and drop the template's const qualifier to match the current ASoC API. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260803061605.35485-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-tdm.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c index a97f775aeddc..4badb6e3e08b 100644 --- a/sound/soc/sophgo/cv1800b-tdm.c +++ b/sound/soc/sophgo/cv1800b-tdm.c @@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { .set_sysclk = cv1800b_i2s_dai_set_sysclk, }; -static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { +static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { .name = "cv1800b-i2s", .playback = { .stream_name = "Playback", @@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) struct cv1800b_i2s *i2s; struct resource *res; void __iomem *regs; - struct snd_soc_dai_driver *dai; int ret; i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); @@ -666,13 +665,8 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2s); cv1800b_i2s_setup_tdm(i2s); - dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), - GFP_KERNEL); - if (!dai) - return -ENOMEM; - - ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, - 1); + ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, + &cv1800b_i2s_dai_template, 1); if (ret) return ret; From bfcdc1c0eaeae5525c726193568e8d638b2afdc0 Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Thu, 6 Aug 2026 22:51:27 -0700 Subject: [PATCH 609/791] ALSA: hda/realtek: Enable mute LEDs on HP Spectre x360 16-aa0xxx The HP Spectre x360 2-in-1 Laptop 16-aa0xxx with PCI subsystem ID 0x103c:0x8c17 only gets the CS35L41 amplifier setup from ALC287_FIXUP_CS35L41_I2C_2, so the speaker-mute and mic-mute keyboard LEDs do not work. Use ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX like subsystem ID 0x8c16 so the mute LEDs work. Tested on HP Spectre x360 2-in-1 Laptop 16-aa0xxx. Signed-off-by: Shang En Sim Link: https://patch.msgid.link/20260807055139.58707-1-sim@shangen.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index a6cfea43d88d..528ecd54197f 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7445,7 +7445,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), - SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), From 42597bb78a34ea80d8f3346779440f498510e96d Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Thu, 6 Aug 2026 20:03:52 +0200 Subject: [PATCH 610/791] ALSA: hda/realtek: enable headset buttons on Lenovo Yoga Pro 7 14ASP10 Inline headset buttons (play/pause, volume up/down) are unresponsive on the Lenovo Yoga Pro 7 14ASP10. Enable headset jack handling by chaining alc_fixup_headset_jack to the existing bass speaker fixup for this model. Signed-off-by: Marco Giunta Link: https://patch.msgid.link/IA1PR19MB77127DE4BC25300BAD284E30FCD22@IA1PR19MB7712.namprd19.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 528ecd54197f..ae572295550c 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4288,6 +4288,7 @@ enum { ALC274_FIXUP_HP_VERBS, ALC287_FIXUP_AW88399_I2C_2, ALC287_FIXUP_LENOVO_LEGION_AW88399, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6991,6 +6992,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC287_FIXUP_AW88399_I2C_2, }, + [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -8086,7 +8093,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), HDA_CODEC_QUIRK(0x17aa, 0x3906, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), HDA_CODEC_QUIRK(0x17aa, 0x3907, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), - SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET), SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), From 6a147d177819b0d831831d19fbb9c23f08a03734 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:24 +0300 Subject: [PATCH 611/791] ASoC: rt274: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). Four entries were appended to the end of rt274_reg[] instead of being inserted at their sorted position, which leaves 7 of the 33 entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: c7e79b2b2d2d ("ASoC: rt274: add rt274 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt274.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 63b5fc439773..f8c370106504 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -184,8 +184,10 @@ static const struct reg_default rt274_reg[] = { { 0x0023a000, 0x00000057 }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x00000097 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x00000097 }, { 0x00935000, 0x00000097 }, { 0x00936000, 0x00000097 }, { 0x00970500, 0x00000400 }, @@ -195,10 +197,12 @@ static const struct reg_default rt274_reg[] = { { 0x00c37000, 0x00000400 }, { 0x00c37100, 0x00000400 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01937000, 0x00000000 }, { 0x01970500, 0x00000400 }, + { 0x01970700, 0x00000020 }, { 0x02050000, 0x0000001b }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -207,10 +211,6 @@ static const struct reg_default rt274_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01970700, 0x00000020 }, - { 0x00830000, 0x00000097 }, - { 0x00930000, 0x00000097 }, - { 0x01270700, 0x00000000 }, }; static bool rt274_volatile_register(struct device *dev, unsigned int reg) From c30771968b5355617843a0ddfa0b9dcbcfd3ea84 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:25 +0300 Subject: [PATCH 612/791] ASoC: rt286: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). Four entries were appended to the end of rt286_reg[] instead of being inserted at their sorted position and the 0x01470100 entry is listed after 0x01470c00, which leaves 7 of the 39 entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 07cf7cbadb4d ("ASoC: add RT286 CODEC driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt286.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ded0ea332480..4217467904d6 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -77,8 +77,10 @@ static const struct reg_default rt286_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -88,16 +90,18 @@ static const struct reg_default rt286_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, - { 0x01470500, 0x00000400 }, - { 0x01470c00, 0x00000000 }, { 0x01470100, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, + { 0x01470c00, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -106,10 +110,6 @@ static const struct reg_default rt286_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt286_volatile_register(struct device *dev, unsigned int reg) From aa4c472b0f4a469c2e4599406fa4cff9de3e02bd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:26 +0300 Subject: [PATCH 613/791] ASoC: rt298: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). Four entries were appended to the end of rt298_reg[] instead of being inserted at their sorted position and the 0x01470100 entry is listed after 0x01470c00, which leaves 7 of the 39 entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 6adcafae6ed2 ("ASoC: add rt298 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 5414a1712b57..09aed08b5b79 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -78,8 +78,10 @@ static const struct reg_default rt298_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -89,16 +91,18 @@ static const struct reg_default rt298_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, - { 0x01470500, 0x00000400 }, - { 0x01470c00, 0x00000000 }, { 0x01470100, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, + { 0x01470c00, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -107,10 +111,6 @@ static const struct reg_default rt298_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt298_volatile_register(struct device *dev, unsigned int reg) From 90ad6a29809dc53e8c1af23fc51bafe2133da035 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:27 +0300 Subject: [PATCH 614/791] ASoC: rt700: drop duplicate reg_default entry rt700_reg_defaults[] lists register 0x7303 twice with the same value. The identical rt711 table has the entry only once, so this is a copy-paste error. Drop the duplicate. No functional change, regcache_lookup_reg() only ever finds one of the two entries. Fixes: 7d2a5f9ae41e ("ASoC: rt700: add rt700 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h index 4ad0dcfd16fd..d2e7381d03b8 100644 --- a/sound/soc/codecs/rt700-sdw.h +++ b/sound/soc/codecs/rt700-sdw.h @@ -313,7 +313,6 @@ static const struct reg_default rt700_reg_defaults[] = { { 0x3122, 0x0000 }, { 0x3123, 0x0000 }, { 0x7303, 0x0057 }, - { 0x7303, 0x0057 }, { 0x8383, 0x0057 }, { 0x7308, 0x0097 }, { 0x8388, 0x0097 }, From 18f21e34493b812d9c8ab9f083871470b016bed4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:28 +0300 Subject: [PATCH 615/791] ASoC: rt700: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). At the end of rt700_reg_defaults[] the 0x83xx entries are interleaved with the 0x73xx entries they belong to, which leaves 6 of the entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 7d2a5f9ae41e ("ASoC: rt700: add rt700 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h index d2e7381d03b8..002f94493b01 100644 --- a/sound/soc/codecs/rt700-sdw.h +++ b/sound/soc/codecs/rt700-sdw.h @@ -313,16 +313,16 @@ static const struct reg_default rt700_reg_defaults[] = { { 0x3122, 0x0000 }, { 0x3123, 0x0000 }, { 0x7303, 0x0057 }, - { 0x8383, 0x0057 }, { 0x7308, 0x0097 }, - { 0x8388, 0x0097 }, { 0x7309, 0x0097 }, - { 0x8389, 0x0097 }, { 0x7312, 0x0000 }, - { 0x8392, 0x0000 }, { 0x7313, 0x0000 }, - { 0x8393, 0x0000 }, { 0x7319, 0x0000 }, + { 0x8383, 0x0057 }, + { 0x8388, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x8392, 0x0000 }, + { 0x8393, 0x0000 }, { 0x8399, 0x0000 }, { 0x75201a, 0x8003 }, { 0x752045, 0x5289 }, From 55fe63530772fe95a99abe9497872975f3161a56 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:29 +0300 Subject: [PATCH 616/791] ASoC: rt711: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). At the end of rt711_reg_defaults[] the 0x83xx entries are interleaved with the 0x73xx entries they belong to, which leaves 5 of the 269 entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 320b8b0d13b8 ("ASoC: rt711: add rt711 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdw.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h index 6acf9858330d..82da0e62d9d7 100644 --- a/sound/soc/codecs/rt711-sdw.h +++ b/sound/soc/codecs/rt711-sdw.h @@ -256,16 +256,16 @@ static const struct reg_default rt711_reg_defaults[] = { { 0x3122, 0x00 }, { 0x3123, 0x00 }, { 0x7303, 0x57 }, - { 0x8383, 0x57 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7319, 0x00 }, + { 0x8383, 0x57 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, { 0x8399, 0x00 }, { 0x752008, 0xa807 }, { 0x752009, 0x1029 }, From b8fdd467bb5d2eb89b665331065e9e3ade964a3e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:30 +0300 Subject: [PATCH 617/791] ASoC: rt711-sdca: sort the register default tables reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). Both tables group the entries by SDCA entity instead: in rt711_sdca_reg_defaults[] the CS01 sample frequency index is listed before the FU05 controls (1 of 54 entries unreachable), and in rt711_sdca_mbq_defaults[] the MIC_ARRAY FU1E volumes are listed before the JACK_CODEC FU0F volumes (2 of 25 entries unreachable). regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort both tables by register address. Fixes: 7ad4d237e7c4 ("ASoC: rt711-sdca: Add RT711 SDCA vendor-specific driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdca-sdw.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h index 0d774e473ab9..9aa22b52d556 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.h +++ b/sound/soc/codecs/rt711-sdca-sdw.h @@ -58,12 +58,12 @@ static const struct reg_default rt711_sdca_reg_defaults[] = { { 0x2f0f, 0x00 }, { 0x2f50, 0x03 }, { 0x2f5a, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, }; @@ -86,14 +86,14 @@ static const struct reg_default rt711_sdca_mbq_defaults[] = { { 0x610003f, 0xff12 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, }; #endif /* __RT711_SDW_SDCA_H__ */ From b9339ee3fccc79e751a2f7bde42c5dd57b38b2a9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:31 +0300 Subject: [PATCH 618/791] ASoC: rt712-sdca-dmic: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt712_sdca_dmic_reg_defaults[] is grouped by SDCA entity instead, so the binary search does not find 3 of its entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 63a511284c9e ("ASoC: rt712-sdca: Add RT712 SDCA driver for Mic topology") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-dmic.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 110154e74efe..2f08e58ed519 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -36,6 +36,7 @@ struct rt712_sdca_dmic_kctrl_priv { #define CH_03 0x03 #define CH_04 0x04 +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x201a, 0x00 }, { 0x201b, 0x00 }, @@ -72,15 +73,16 @@ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x2f59, 0x07 }, { 0x3201, 0x01 }, { 0x320c, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = { { 0x0590001e, 0x0020 }, { 0x06100000, 0x0010 }, From efd430c4d0426e60fbec400c5a8ce62d886f6e21 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:32 +0300 Subject: [PATCH 619/791] ASoC: rt712-sdca-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt712_sdca_reg_defaults[] is grouped by SDCA function instead, so the binary search does not find 4 of its entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. One of them is the Mic Array Clock Source 0x1C Sample Frequency Index control, which a part without that function rejects: soundwire_intel.link.0: Msg ignored for Slave 6, addr: 0x8e00 Sort the table by register address. Fixes: 936abb09c1c7 ("ASoC: rt712-sdca: add the function for version B") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index 99fd2d67f04d..7ad25cc27f62 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -11,21 +11,21 @@ #include #include +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_reg_defaults[] = { - - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, @@ -34,6 +34,7 @@ static const struct reg_default rt712_sdca_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x2000004, 0xaa01 }, { 0x200000e, 0x21e0 }, From d729804a92dc4e74b8fb69da162f74660ea64385 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:33 +0300 Subject: [PATCH 620/791] ASoC: rt715: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). At the end of rt715_reg_defaults[] the 0x82xx and 0x83xx entries are interleaved with the 0x72xx and 0x73xx entries they belong to, and 0x385e is listed before 0x3859. This leaves 25 of the 323 entries unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: d1ede0641b05 ("ASoC: rt715: add RT715 codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdw.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 5d7661e335ae..f19fafb913f0 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -281,8 +281,8 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x371b, 0x00 }, { 0x371d, 0x00 }, { 0x3729, 0x00 }, - { 0x385e, 0x00 }, { 0x3859, 0x00 }, + { 0x385e, 0x00 }, { 0x4c12, 0x411111f0 }, { 0x4c13, 0x411111f0 }, { 0x4c1d, 0x411111f0 }, @@ -300,36 +300,36 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x4f1d, 0x411111f0 }, { 0x4f29, 0x411111f0 }, { 0x7207, 0x00 }, - { 0x8287, 0x00 }, { 0x7208, 0x00 }, - { 0x8288, 0x00 }, { 0x7209, 0x00 }, - { 0x8289, 0x00 }, { 0x7227, 0x00 }, - { 0x82a7, 0x00 }, { 0x7307, 0x97 }, - { 0x8387, 0x97 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7318, 0x00 }, - { 0x8398, 0x00 }, { 0x7319, 0x00 }, - { 0x8399, 0x00 }, { 0x731a, 0x00 }, - { 0x839a, 0x00 }, { 0x731b, 0x00 }, - { 0x839b, 0x00 }, { 0x731d, 0x00 }, - { 0x839d, 0x00 }, { 0x7327, 0x97 }, - { 0x83a7, 0x97 }, { 0x7329, 0x00 }, + { 0x8287, 0x00 }, + { 0x8288, 0x00 }, + { 0x8289, 0x00 }, + { 0x82a7, 0x00 }, + { 0x8387, 0x97 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, + { 0x8398, 0x00 }, + { 0x8399, 0x00 }, + { 0x839a, 0x00 }, + { 0x839b, 0x00 }, + { 0x839d, 0x00 }, + { 0x83a7, 0x97 }, { 0x83a9, 0x00 }, { 0x752039, 0xa500 }, }; From c9875bba469c19ad7f771b91b1e4c6f1c0f0a07d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:34 +0300 Subject: [PATCH 621/791] ASoC: rt715-sdca: drop duplicate reg_default entries The last two entries of rt715_reg_defaults_sdca[] repeat the ADC7_27 volume mute controls for CH_01 and CH_02, which are already listed a few lines above with the same value. Drop the duplicates. No functional change, regcache_lookup_reg() only ever finds one of the two copies. Fixes: 20d17057f0a8 ("ASoC: rt715-sdca: Add RT715 sdca vendor-specific driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdca-sdw.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h index 0cbc14844f8c..774762480195 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -102,10 +102,6 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, }; static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { From 61a0321e4bc2ff9ecb38cda7d5a32ffacef71a75 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:35 +0300 Subject: [PATCH 622/791] ASoC: rt715-sdca: sort the register default tables reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). Both tables group the entries by SDCA entity instead: in rt715_reg_defaults_sdca[] the CX_CLK_SEL control is listed before the ADC8_9, ADC10_11 and ADC7_27 mute controls (7 of 78 entries unreachable), and in rt715_mbq_reg_defaults_sdca[] the AMIC_GAIN_EN CH_08 entry is listed before the DMIC_GAIN_EN entries (1 of 32 entries unreachable). regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort both tables by register address. Fixes: 20d17057f0a8 ("ASoC: rt715-sdca: Add RT715 sdca vendor-specific driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdca-sdw.h | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h index 774762480195..d408f2d7d0f2 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -76,28 +76,28 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { { 0x2f52, 0x01 }, { 0x2f5a, 0x02 }, { 0x2f5b, 0x05 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, @@ -145,8 +145,6 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, @@ -161,6 +159,8 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, }; From 70e0481c196e4822e683bca384e6bda89d944839 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:36 +0300 Subject: [PATCH 623/791] ASoC: rt721-sdca-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt721_sdca_reg_defaults[] is grouped by SDCA function instead, so the binary search does not find 12 of its entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 86ce355c1f9a ("ASoC: rt721-sdca: Add RT721 SDCA driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt721-sdca-sdw.h | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h index 214b31b82583..13d6227f3392 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.h +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -29,22 +29,22 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { { 0x2f5b, 0x07 }, { 0x2f5c, 0x27 }, { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, - RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, - RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, - RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, - RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, @@ -53,30 +53,30 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F, RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26, RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, - RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, - RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, - RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, - RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, }; static const struct reg_default rt721_sdca_mbq_defaults[] = { From 5b48ce0356b134155722a61f7196516a7c2e66c5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:37 +0300 Subject: [PATCH 624/791] ASoC: rt1017-sdca-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt1017_sdca_reg_defaults[] places the SDCA controls before the lower vendor registers instead, so the binary search does not find 4 of its entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 2b7aecd58528 ("ASoC: rt1017: Add RT1017 SDCA amplifier driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-15-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1017-sdca-sdw.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h index 4932b5dbe3c0..1604e1cfd85e 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.h +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -165,19 +165,19 @@ static const struct reg_default rt1017_sdca_reg_defaults[] = { { 0xdb09, 0x0f }, { 0xdb0a, 0xff }, { 0xdb14, 0x00 }, - - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, - RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, - RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, }; #endif /* __RT1017_SDW_H__ */ From 7b48eccfbb9bf15e9b7377a5296bfd01815c62d8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:38 +0300 Subject: [PATCH 625/791] ASoC: rt1316-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt1316_reg_defaults[] is not in address order, so the binary search does not find one of its entries. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 2b719fd20f32 ("ASoC: rt1316: Add RT1316 SDCA vendor-specific driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-16-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1316-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index ca318dbd946e..0f6fc36e50f0 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -59,13 +59,13 @@ static const struct reg_default rt1316_reg_defaults[] = { { 0xd101, 0x00 }, { 0xd102, 0x30 }, { 0xd103, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, }; static const struct reg_sequence rt1316_blind_write[] = { From 2a8e4b7114f6493314348bda7e3d2141d218ef61 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:39 +0300 Subject: [PATCH 626/791] ASoC: rt1318: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). The 0xdd93 and 0xdd94 entries are listed after 0xddc8 in rt1318_reg[], which leaves them unreachable for the binary search. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: fe1ff61487ac ("ASoC: rt1318: Add RT1318 audio amplifier driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-17-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1318.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index d13fd0e14125..55607f1b9a1e 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -337,6 +337,8 @@ static const struct reg_default rt1318_reg[] = { { 0xdd08, 0x40 }, { 0xdd12, 0x00 }, { 0xdd35, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, { 0xddb5, 0x00 }, { 0xddb6, 0x40 }, { 0xddb7, 0x00 }, @@ -345,8 +347,6 @@ static const struct reg_default rt1318_reg[] = { { 0xddc6, 0x00 }, { 0xddc7, 0x00 }, { 0xddc8, 0x00 }, - { 0xdd93, 0x00 }, - { 0xdd94, 0x64 }, { 0xdf00, 0x00 }, { 0xdf5f, 0x00 }, { 0xdf60, 0x00 }, From 3673b33633a5daf2f52aff03b57f7b25352fe234 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:02:40 +0300 Subject: [PATCH 627/791] ASoC: rt1318-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). rt1318_reg_defaults[] is not in address order, so the binary search does not find 3 of its entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 6ad73a2b42ea ("ASoC: rt1318: Add RT1318 SDCA vendor-specific driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805090240.16991-18-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1318-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index c038ac0e3b76..2f1a776585fc 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -235,10 +235,10 @@ static const struct reg_default rt1318_reg_defaults[] = { { 0xf805, 0x00 }, { 0xf806, 0x07 }, { 0xf807, 0xff }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, }; From 8dd18d9956bfd74531bfd7088e59586e3e115789 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 14:39:11 +0300 Subject: [PATCH 628/791] ASoC: pm4125-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). PM4125_SWR_HPHPA_HD2 (0x3090) is listed before PM4125_ANA_HPHPA_SPARE_CTL (0x308e), which makes the latter unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 8ad529484937 ("ASoC: codecs: add new pm4125 audio codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805113911.21723-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/pm4125-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c index 307b66025426..182ff396301a 100644 --- a/sound/soc/codecs/pm4125-sdw.c +++ b/sound/soc/codecs/pm4125-sdw.c @@ -126,8 +126,8 @@ static const struct reg_default pm4125_defaults[] = { { PM4125_ANA_HPHPA_FSM_CLK, 0x12 }, { PM4125_ANA_HPHPA_L_GAIN, 0x00 }, { PM4125_ANA_HPHPA_R_GAIN, 0x00 }, - { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 }, + { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_SURGE_EN, 0x38 }, { PM4125_ANA_COMBOPA_CTL, 0x35 }, { PM4125_ANA_COMBOPA_CTL_4, 0x84 }, From ceba07ca24fab54e0e38ec96d196fca3e638d671 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:13:26 +0300 Subject: [PATCH 629/791] ASoC: tas2783-sdw: drop duplicate reg_default entry TAS2783_AMP_LEVEL is defined as TASDEV_REG_SDW(0x0, 0x00, 0x03), so tas2783_reg_default[] lists that register twice. Drop the open coded second entry. Fixes: 4cc9bd8d7b32 ("ASoc: tas2783A: Add soundwire based codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805091327.23944-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 94f11e3b0c20..49a1660d5177 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -107,7 +107,6 @@ struct tas2783_prv { static const struct reg_default tas2783_reg_default[] = { {TAS2783_AMP_LEVEL, 0x28}, - {TASDEV_REG_SDW(0, 0, 0x03), 0x28}, {TASDEV_REG_SDW(0, 0, 0x04), 0x21}, {TASDEV_REG_SDW(0, 0, 0x05), 0x41}, {TASDEV_REG_SDW(0, 0, 0x06), 0x00}, From b45fc97ebcfb27ec250025329a4f79ce5e327ec3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 12:13:27 +0300 Subject: [PATCH 630/791] ASoC: tas2783-sdw: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). tas2783_reg_default[] is grouped by SDCA entity name instead, so the binary search does not find 120 of its 196 entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 4cc9bd8d7b32 ("ASoc: tas2783A: Add soundwire based codec driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805091327.23944-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 176 ++++++++++++++++----------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 49a1660d5177..c217da5fccdf 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -142,6 +142,7 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x41), 0x14}, {TASDEV_REG_SDW(0, 0, 0x5c), 0x19}, {TASDEV_REG_SDW(0, 0, 0x5d), 0x80}, + {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 0, 0x63), 0x48}, {TASDEV_REG_SDW(0, 0, 0x65), 0x08}, {TASDEV_REG_SDW(0, 0, 0x66), 0xb2}, @@ -157,7 +158,6 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x73), 0x08}, {TASDEV_REG_SDW(0, 0, 0x75), 0xe0}, {TASDEV_REG_SDW(0, 0, 0x7a), 0x60}, - {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 1, 0x02), 0x00}, {TASDEV_REG_SDW(0, 1, 0x17), 0xc0}, {TASDEV_REG_SDW(0, 1, 0x19), 0x60}, @@ -176,63 +176,44 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0xfd, 0x39), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x3e), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x45), 0x00}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1), 0x9c00}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0}, @@ -242,19 +223,60 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1), 0x0}, @@ -264,6 +286,14 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa), 0x0}, @@ -272,36 +302,6 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, }; static const struct reg_sequence tas2783_init_seq[] = { From 767d9ae714e3e9b0ae86237c410fbfca7056570a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 13:41:45 +0300 Subject: [PATCH 631/791] ASoC: pcm512x: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). PCM512x_AUTO_MUTE (page 0, register 59) is listed before PCM512x_ERROR_DETECT (page 0, register 37) and PCM512x_VCOM_CTRL_2 (page 1, register 9) is listed before the page 0 clocking block, so the bsearch() descends into the wrong half of the table. 24 of the 45 entries are unreachable, among them every PLL coefficient and clock divider default. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 5a3af1293194 ("ASoC: pcm512x: Add PCM512x driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805104149.9795-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm512x.c | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fe3b5011fa16..03b1fcb1bf96 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -78,28 +78,10 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_POWER, 0x00 }, { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, - { PCM512x_PLL_REF, 0x00 }, - { PCM512x_DAC_REF, 0x00 }, - { PCM512x_DAC_ROUTING, 0x11 }, - { PCM512x_DSP_PROGRAM, 0x01 }, - { PCM512x_CLKDET, 0x00 }, - { PCM512x_AUTO_MUTE, 0x00 }, - { PCM512x_ERROR_DETECT, 0x00 }, - { PCM512x_DIGITAL_VOLUME_1, 0x00 }, - { PCM512x_DIGITAL_VOLUME_2, 0x30 }, - { PCM512x_DIGITAL_VOLUME_3, 0x30 }, - { PCM512x_DIGITAL_MUTE_1, 0x22 }, - { PCM512x_DIGITAL_MUTE_2, 0x00 }, - { PCM512x_DIGITAL_MUTE_3, 0x07 }, - { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, - { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, - { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, - { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, - { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, - { PCM512x_VCOM_CTRL_1, 0x00 }, - { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG, 0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_GPIO_DACIN, 0x00 }, { PCM512x_GPIO_PLLIN, 0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, @@ -117,8 +99,26 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_FS_SPEED_MODE, 0x00 }, { PCM512x_IDAC_1, 0x01 }, { PCM512x_IDAC_2, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, { PCM512x_I2S_1, 0x02 }, { PCM512x_I2S_2, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, + { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, + { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, + { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, + { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, + { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, + { PCM512x_VCOM_CTRL_1, 0x00 }, + { PCM512x_VCOM_CTRL_2, 0x01 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) From 1cc0cb62d306bb7c42e3d4649863df7c279ac850 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 13:41:46 +0300 Subject: [PATCH 632/791] ASoC: tas2552: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TAS2552_OUTPUT_DATA (0x07), TAS2552_PDM_CFG (0x11), TAS2552_PGA_GAIN (0x12) and TAS2552_BOOST_APT_CTRL (0x14) are listed before TAS2552_RESERVED_0D (0x0d), TAS2552_LIMIT_RATE_HYS (0x0e) and TAS2552_CFG_2 (0x02), which leaves 7 of the 21 entries unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 5df7f71d5cdf ("ASoC: tas2552: Support TI TAS2552 Amplifier") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805104149.9795-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2552.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 1a7650b9b2a7..ea083d1cec2b 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -31,25 +31,25 @@ static const struct reg_default tas2552_reg_defs[] = { {TAS2552_CFG_1, 0x22}, + {TAS2552_CFG_2, 0xef}, {TAS2552_CFG_3, 0x80}, {TAS2552_DOUT, 0x00}, - {TAS2552_OUTPUT_DATA, 0xc0}, - {TAS2552_PDM_CFG, 0x01}, - {TAS2552_PGA_GAIN, 0x00}, - {TAS2552_BOOST_APT_CTRL, 0x0f}, - {TAS2552_RESERVED_0D, 0xbe}, - {TAS2552_LIMIT_RATE_HYS, 0x08}, - {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, {TAS2552_SER_CTRL_2, 0x00}, + {TAS2552_OUTPUT_DATA, 0xc0}, {TAS2552_PLL_CTRL_1, 0x10}, {TAS2552_PLL_CTRL_2, 0x00}, {TAS2552_PLL_CTRL_3, 0x00}, {TAS2552_BTIP, 0x8f}, {TAS2552_BTS_CTRL, 0x80}, + {TAS2552_RESERVED_0D, 0xbe}, + {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_LIMIT_RELEASE, 0x04}, {TAS2552_LIMIT_INT_COUNT, 0x00}, + {TAS2552_PDM_CFG, 0x01}, + {TAS2552_PGA_GAIN, 0x00}, {TAS2552_EDGE_RATE_CTRL, 0x40}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, {TAS2552_VBAT_DATA, 0x00}, }; From e7643c3f7eb3292f8a98c2ddbf46ba78d848b83d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 13:41:47 +0300 Subject: [PATCH 633/791] ASoC: tas2764: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TAS2764_DVC (0x1a) is listed before TAS2764_CHNL_0 (0x03), which makes it unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 827ed8a0fa50 ("ASoC: tas2764: Add the driver for the TAS2764") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805104149.9795-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2764.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index b11b998aa32a..f9c65c9f6d0a 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -897,13 +897,13 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_PAGE, 0x00 }, { TAS2764_SW_RST, 0x00 }, { TAS2764_PWR_CTRL, 0x1a }, - { TAS2764_DVC, 0x00 }, { TAS2764_CHNL_0, 0x28 }, { TAS2764_TDM_CFG0, 0x09 }, { TAS2764_TDM_CFG1, 0x02 }, { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_DVC, 0x00 }, { TAS2764_INT_CLK_CFG, 0x19 }, }; From e725093e9e53db9298e38e7332a44dcaac2fd135 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 13:41:48 +0300 Subject: [PATCH 634/791] ASoC: tas2780: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TAS2780_DVC (0x1a) is listed before TAS2780_CHNL_0 (0x03), which makes it unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: eae9f9ce181b ("ASoC: add tas2780 driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805104149.9795-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2780.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 1ec1c076204f..f1fce4305fa1 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -527,13 +527,13 @@ static const struct reg_default tas2780_reg_defaults[] = { { TAS2780_PAGE, 0x00 }, { TAS2780_SW_RST, 0x00 }, { TAS2780_PWR_CTRL, 0x1a }, - { TAS2780_DVC, 0x00 }, { TAS2780_CHNL_0, 0x00 }, { TAS2780_TDM_CFG0, 0x09 }, { TAS2780_TDM_CFG1, 0x02 }, { TAS2780_TDM_CFG2, 0x0a }, { TAS2780_TDM_CFG3, 0x10 }, { TAS2780_TDM_CFG5, 0x42 }, + { TAS2780_DVC, 0x00 }, }; static const struct regmap_range_cfg tas2780_regmap_ranges[] = { From 3521d209ca70bf77b8c1dcccab5ca4df9468877f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 13:41:49 +0300 Subject: [PATCH 635/791] ASoC: tas675x: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TAS675X_AC_LDG_CTRL_REG (0xb5), TAS675X_TWEETER_DETECT_CTRL_REG (0xb6), TAS675X_TWEETER_DETECT_THRESH_REG (0xb7) and TAS675X_AC_LDG_FREQ_CTRL_REG (0xb8) are listed before the 0x7c - 0xa0 block, which leaves 14 of the 69 entries unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 133c81f84471 ("ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver") Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805104149.9795-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas675x.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c index 82526362de7b..404706b62156 100644 --- a/sound/soc/codecs/tas675x.c +++ b/sound/soc/codecs/tas675x.c @@ -1924,15 +1924,6 @@ static const struct reg_default tas675x_reg_defaults[] = { { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 }, { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 }, { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 }, - { TAS675X_DC_LDG_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, - { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, - { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, - { TAS675X_AC_LDG_CTRL_REG, 0x10 }, - { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, - { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, - { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, { TAS675X_REPORT_ROUTING_1_REG, 0x00 }, { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 }, { TAS675X_REPORT_ROUTING_2_REG, 0xA2 }, @@ -1943,6 +1934,15 @@ static const struct reg_default tas675x_reg_defaults[] = { { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 }, { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 }, { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL }, + { TAS675X_DC_LDG_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, + { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, + { TAS675X_AC_LDG_CTRL_REG, 0x10 }, + { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, + { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, + { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 }, { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 }, }; From 437fbdeb60693b8f2e8250d44d29e513a95df298 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:27 +0300 Subject: [PATCH 636/791] ASoC: sgtl5000: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). SGTL5000_CHIP_SHORT_CTRL (0x003c) is listed before SGTL5000_CHIP_ANA_TEST2 (0x003a), which makes the former unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 29aa37cddfb9 ("ASoC: sgtl5000: Fix the cache handling") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122728.12362-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 59642673b4cb..35df1a8c4c8c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -56,8 +56,8 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, - { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_DAP_CTRL, 0x0000 }, { SGTL5000_DAP_PEQ, 0x0000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, From 84c5d79aebe6c45e12e3112d14e68972f33c210a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:28 +0300 Subject: [PATCH 637/791] ASoC: fsl_easrc: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). The four REG_EASRC_RRL() entries are listed as a block before the four REG_EASRC_RRH() ones, but the two registers of a context alternate in the address map (RRL(n) at 0x110 + 8 * n, RRH(n) at 0x114 + 8 * n). This leaves REG_EASRC_RRL(1), REG_EASRC_RRL(2) and REG_EASRC_RRL(3) unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 955ac624058f ("ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122728.12362-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 8535ef844ce0..6ccba3706b58 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1711,12 +1711,12 @@ static const struct reg_default fsl_easrc_reg_defaults[] = { {REG_EASRC_SFS(2), 0x00000000}, {REG_EASRC_SFS(3), 0x00000000}, {REG_EASRC_RRL(0), 0x00000000}, - {REG_EASRC_RRL(1), 0x00000000}, - {REG_EASRC_RRL(2), 0x00000000}, - {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(0), 0x00000000}, + {REG_EASRC_RRL(1), 0x00000000}, {REG_EASRC_RRH(1), 0x00000000}, + {REG_EASRC_RRL(2), 0x00000000}, {REG_EASRC_RRH(2), 0x00000000}, + {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(3), 0x00000000}, {REG_EASRC_RUC(0), 0x00000000}, {REG_EASRC_RUC(1), 0x00000000}, From 597273563d90d03fda852a29c3a76c41a22bf6ac Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:46 +0300 Subject: [PATCH 638/791] ASoC: tegra210_i2s: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TEGRA210_I2S_ENABLE (0x80) is listed after TEGRA210_I2S_CG (0x88) and TEGRA210_I2S_TIMING (0xa4), so both it and TEGRA210_I2S_TIMING are unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: c0bfa98349d1 ("ASoC: tegra: Add Tegra210 based I2S driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122748.13090-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index e7e26e917291..8cac02671295 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -23,9 +23,9 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 }, { TEGRA210_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 }, + { TEGRA210_I2S_ENABLE, 0x1 }, { TEGRA210_I2S_CG, 0x1 }, { TEGRA210_I2S_TIMING, 0x0000001f }, - { TEGRA210_I2S_ENABLE, 0x1 }, /* * Below update does not have any effect on Tegra186 and Tegra194. * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update From 82da8df388004af4540e930ab3de2ce5787207e7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:47 +0300 Subject: [PATCH 639/791] ASoC: tegra210_i2s: sort the Tegra264 register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TEGRA264_I2S_ENABLE (0x100), TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE (0x30) and TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE (0xb0) are listed at the end of the table, after TEGRA264_I2S_TIMING (0x130), which leaves 4 of the 9 entries unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: b3354438d898 ("ASoC: tegra: I2S: Add Tegra264 support") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122748.13090-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_i2s.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index 8cac02671295..84506576437d 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -38,13 +38,13 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { static const struct reg_default tegra264_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_RX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, { TEGRA264_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA264_I2S_TX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, + { TEGRA264_I2S_ENABLE, 0x1 }, { TEGRA264_I2S_CG, 0x1 }, { TEGRA264_I2S_TIMING, 0x0000001f }, - { TEGRA264_I2S_ENABLE, 0x1 }, - { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, - { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, }; static void tegra210_i2s_set_slot_ctrl(struct tegra210_i2s *i2s, From f70bc276fc7f712ff5c8e995d5050558a3198df2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:27:48 +0300 Subject: [PATCH 640/791] ASoC: tegra210_mixer: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). TEGRA210_MIXER_ENABLE (0x400) is the last entry of the table, after TEGRA210_MIXER_PEAKM_RAM_CTRL (0x434), which makes it unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 05bb3d5ec64a ("ASoC: tegra: Add Tegra210 based Mixer driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122748.13090-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index a69774578d69..8eb4e54b954b 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -57,10 +57,10 @@ static const struct reg_default tegra210_mixer_reg_defaults[] = { MIXER_TX_REG_DEFAULTS(3), MIXER_TX_REG_DEFAULTS(4), + { TEGRA210_MIXER_ENABLE, 0x1 }, { TEGRA210_MIXER_CG, 0x00000001}, { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000}, { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000}, - { TEGRA210_MIXER_ENABLE, 0x1 }, }; /* Default gain parameters */ From d4d0e6e2355a6fe6517e5a0c3d9a0b8ab073b0b6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:28:08 +0300 Subject: [PATCH 641/791] ASoC: ml26124: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). The Mic Select Control register (0xe8) is listed in the analog path control group, between 0x5a and 0x60, which makes it unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Move the entry to the end of the table, where it belongs by address. Fixes: d808fe9f3e7f ("ASoC: Add LAPIS Semiconductor ML26124 driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122811.13713-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/ml26124.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 8a14626d43ff..7209b47c2029 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -224,7 +224,6 @@ static const struct reg_default ml26124_reg[] = { /* Analog Path Control Register */ {0x54, 0x00}, /* Speaker AMP Output Control */ {0x5a, 0x00}, /* Mic IF Control */ - {0xe8, 0x01}, /* Mic Select Control */ /* Audio Interface Control Register */ {0x60, 0x00}, /* SAI-Trans Control */ @@ -287,6 +286,9 @@ static const struct reg_default ml26124_reg[] = { {0xd0, 0x01}, /* VIDEO AMP Gain Control */ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */ {0xd4, 0x01}, /* VIDEO AMP Control2 */ + + /* Analog Path Control Register */ + {0xe8, 0x01}, /* Mic Select Control */ }; /* Get sampling rate value of sampling rate setting register (0x0) */ From b927853f70078262780a4e623631584a25eb7284 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:28:09 +0300 Subject: [PATCH 642/791] ASoC: cx2072x: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). The table is grouped by function rather than by address: for every node the amplifier gain registers (0x41c0, 0x45c0, ...) are listed before the power state and stream format registers of the same node (0x4014, 0x4414, ...). This leaves 75 of the 132 entries unreachable. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: a497a4363706 ("ASoC: Add support for Conexant CX2072X CODEC") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122811.13713-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cx2072x.c | 134 ++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 83c6cbd40804..ffd6a5119f5e 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -96,51 +96,60 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_GPIO_WAKE, 0x00000000 }, { CX2072X_GPIO_UM_ENABLE, 0x00000000 }, { CX2072X_GPIO_STICKY_MASK, 0x00000000 }, - { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, { CX2072X_DAC1_POWER_STATE, 0x00000433 }, { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 }, - { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, { CX2072X_DAC2_POWER_STATE, 0x00000433 }, { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a }, + { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, { CX2072X_ADC1_POWER_STATE, 0x00000433 }, { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a }, + { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, { CX2072X_ADC2_POWER_STATE, 0x00000433 }, { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_MIXER_POWER_STATE, 0x00000433 }, + { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 }, { CX2072X_PORTA_POWER_STATE, 0x00000433 }, { CX2072X_PORTA_PIN_CTRL, 0x000000c0 }, { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTA_PIN_SENSE, 0x00000000 }, { CX2072X_PORTA_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTG_POWER_STATE, 0x00000433 }, + { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_POWER_STATE, 0x00000433 }, { CX2072X_PORTB_PIN_CTRL, 0x00000000 }, { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 }, @@ -148,43 +157,16 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_PORTB_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTB_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTC_POWER_STATE, 0x00000433 }, - { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_PORTD_POWER_STATE, 0x00000433 }, { CX2072X_PORTD_PIN_CTRL, 0x00000020 }, { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTD_PIN_SENSE, 0x00000000 }, { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTD_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTE_POWER_STATE, 0x00000433 }, - { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTF_POWER_STATE, 0x00000433 }, - { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTG_POWER_STATE, 0x00000433 }, - { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTM_POWER_STATE, 0x00000433 }, - { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, - { CX2072X_MIXER_POWER_STATE, 0x00000433 }, - { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_PORTC_POWER_STATE, 0x00000433 }, + { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 }, { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 }, { CX2072X_SPKR_DRC_TEST, 0x00000000 }, @@ -195,12 +177,15 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_I2SPCM_CONTROL3, 0x00000000 }, { CX2072X_I2SPCM_CONTROL4, 0x00000000 }, { CX2072X_I2SPCM_CONTROL5, 0x00000000 }, - { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, + { CX2072X_DIGITAL_TEST16, 0x00000021 }, + { CX2072X_DIGITAL_TEST17, 0x00000018 }, + { CX2072X_DIGITAL_TEST18, 0x00000024 }, + { CX2072X_DIGITAL_TEST19, 0x00000001 }, + { CX2072X_DIGITAL_TEST20, 0x00000002 }, { CX2072X_CODEC_TEST2, 0x00000000 }, { CX2072X_CODEC_TEST9, 0x00000004 }, - { CX2072X_CODEC_TEST20, 0x00000600 }, - { CX2072X_CODEC_TEST26, 0x00000208 }, { CX2072X_ANALOG_TEST4, 0x00000000 }, { CX2072X_ANALOG_TEST5, 0x00000000 }, { CX2072X_ANALOG_TEST6, 0x0000059a }, @@ -215,11 +200,26 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_DIGITAL_TEST11, 0x00000000 }, { CX2072X_DIGITAL_TEST12, 0x00000084 }, { CX2072X_DIGITAL_TEST15, 0x00000077 }, - { CX2072X_DIGITAL_TEST16, 0x00000021 }, - { CX2072X_DIGITAL_TEST17, 0x00000018 }, - { CX2072X_DIGITAL_TEST18, 0x00000024 }, - { CX2072X_DIGITAL_TEST19, 0x00000001 }, - { CX2072X_DIGITAL_TEST20, 0x00000002 }, + { CX2072X_CODEC_TEST20, 0x00000600 }, + { CX2072X_CODEC_TEST26, 0x00000208 }, + { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTE_POWER_STATE, 0x00000433 }, + { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTF_POWER_STATE, 0x00000433 }, + { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTM_POWER_STATE, 0x00000433 }, + { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, }; /* From 5c4cf173b7eba9bd1e8824b75380412cae2e026b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:28:10 +0300 Subject: [PATCH 643/791] ASoC: max9860: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). MAX9860_PWRMAN (0x10) is listed as the first entry, before MAX9860_INTEN (0x02), which makes MAX9860_INTEN unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 3b2af7f79968 ("ASoC: max9860: new driver") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122811.13713-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/max9860.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 0d7ac37850bb..20e288e38669 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -48,7 +48,6 @@ static int max9860_dvddio_event(struct notifier_block *nb, } static const struct reg_default max9860_reg_defaults[] = { - { MAX9860_PWRMAN, 0x00 }, { MAX9860_INTEN, 0x00 }, { MAX9860_SYSCLK, 0x00 }, { MAX9860_AUDIOCLKHIGH, 0x00 }, @@ -62,6 +61,7 @@ static const struct reg_default max9860_reg_defaults[] = { { MAX9860_MICGAIN, 0x00 }, { MAX9860_MICADC, 0x00 }, { MAX9860_NOISEGATE, 0x00 }, + { MAX9860_PWRMAN, 0x00 }, }; static bool max9860_readable(struct device *dev, unsigned int reg) From bbd73fb224caa1badfe2aa338fe9c9dcf40a6e96 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 15:28:11 +0300 Subject: [PATCH 644/791] ASoC: sti-sas: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). STIH407_AUDIO_DAC_CTRL (0xa8) is listed before STIH407_AUDIO_GLUE_CTRL (0xa4), which makes the latter unreachable. regcache_reg_needs_sync() then cannot compare it against its default and reports that a sync is needed, so it is written to the device on every regcache_sync() even when it was never touched. Sort the table by register address. Fixes: 165a57a3df02 ("ASoC: sti-sas: clean legacy in sti-sas") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260805122811.13713-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/sti-sas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 4ab15be69f3a..b7eaf4795d8b 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -44,8 +44,8 @@ enum { }; static const struct reg_default stih407_sas_reg_defaults[] = { - { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, }; struct sti_dac_audio { From b8c7c3723e26ebd690b6dc6c022dcf3bbeda9a04 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Aug 2026 00:52:56 +0000 Subject: [PATCH 645/791] ASoC: codecs: ad1*: use .auto_selectable_formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can use .auto_selectable_formats. Let's adds it. Acked-by: Nuno Sá Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874ih6vjiw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/ad1836.c | 6 ++++++ sound/soc/codecs/ad193x.c | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 8afeadcaf8b0..dbea83bd49b6 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, return 0; } +static const u64 ad1836_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ad1836_dai_ops = { .hw_params = ad1836_hw_params, .set_fmt = ad1836_set_dai_fmt, + .auto_selectable_formats = &ad1836_selectable_formats, + .num_auto_selectable_formats = 1, }; #define AD183X_DAI(_name, num_dacs, num_adcs) \ diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index b93531c3a9a4..9841c779b175 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, &constr); } +static const u64 ad193x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + static const struct snd_soc_dai_ops ad193x_dai_ops = { .startup = ad193x_startup, .hw_params = ad193x_hw_params, @@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { .set_tdm_slot = ad193x_set_tdm_slot, .set_sysclk = ad193x_set_dai_sysclk, .set_fmt = ad193x_set_dai_fmt, + .auto_selectable_formats = &ad193x_selectable_formats, + .num_auto_selectable_formats = 1, .no_capture_mute = 1, }; From 1cea958237923501ec85f6818a6ee4995a36aa7c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Aug 2026 00:53:20 +0000 Subject: [PATCH 646/791] ASoC: codecs: adav80x: use .auto_selectable_formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can use .auto_selectable_formats. Let's adds it. Acked-by: Nuno Sá Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87zeyyu4xr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/adav80x.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 8a89187f9c78..e97c72ac9b2b 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, adav80x->rate = 0; } +static const u64 adav80x_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops adav80x_dai_ops = { .set_fmt = adav80x_set_dai_fmt, .hw_params = adav80x_hw_params, .startup = adav80x_dai_startup, .shutdown = adav80x_dai_shutdown, + .auto_selectable_formats = &adav80x_selectable_formats, + .num_auto_selectable_formats = 1, }; #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ From 31dbeb6d8869969870b56dee8a90acd82c6e1b7e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Aug 2026 00:54:04 +0000 Subject: [PATCH 647/791] ASoC: codecs: cros_ec_codec: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Reviewed-by: Tzung-Bi Shih Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzkau4wk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/cros_ec_codec.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index fd2d5d7f9276..1148e37e29ce 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -320,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) (uint8_t *)&p, sizeof(p), NULL, 0); } +static const u64 i2s_rx_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops i2s_rx_dai_ops = { .hw_params = i2s_rx_hw_params, .set_fmt = i2s_rx_set_fmt, .set_bclk_ratio = i2s_rx_set_bclk_ratio, + .auto_selectable_formats = &i2s_rx_selectable_formats, + .num_auto_selectable_formats = 1, }; static int i2s_rx_event(struct snd_soc_dapm_widget *w, From 8042806427c4c96f00f28d10b08c7916a46aa6d2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Aug 2026 00:55:04 +0000 Subject: [PATCH 648/791] ASoC: codecs: lochnagar-sc: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Charles Keepax Link: https://patch.msgid.link/87cxvuu4uv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/lochnagar-sc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c index a3d6318c9050..dadb4a6c287f 100644 --- a/sound/soc/codecs/lochnagar-sc.c +++ b/sound/soc/codecs/lochnagar-sc.c @@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); } +static const u64 lochnagar_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { .startup = lochnagar_sc_line_startup, .shutdown = lochnagar_sc_line_shutdown, .set_fmt = lochnagar_sc_set_line_fmt, + .auto_selectable_formats = &lochnagar_selectable_formats, + .num_auto_selectable_formats = 1, }; static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { .startup = lochnagar_sc_startup, .set_fmt = lochnagar_sc_set_usb_fmt, + .auto_selectable_formats = &lochnagar_selectable_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver lochnagar_sc_dai[] = { From 0aec3e83e3dce7f8eb94fa65d190d9c7e958848c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Aug 2026 00:55:09 +0000 Subject: [PATCH 649/791] ASoC: codecs: madera: use .auto_selectable_formats We can use .auto_selectable_formats. Let's adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Charles Keepax Link: https://patch.msgid.link/87bjbeu4uq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/madera.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index 55ea6950ca90..58255844b4df 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -3332,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, return 0; } +static const u64 madera_selectable_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + const struct snd_soc_dai_ops madera_dai_ops = { .startup = &madera_startup, .set_fmt = &madera_set_fmt, @@ -3339,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { .hw_params = &madera_hw_params, .set_sysclk = &madera_dai_set_sysclk, .set_tristate = &madera_set_tristate, + .auto_selectable_formats = &madera_selectable_formats, + .num_auto_selectable_formats = 1, }; EXPORT_SYMBOL_GPL(madera_dai_ops); From e04cf4dd5faac8bfe22a70b30fa40b07df853e25 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 6 Aug 2026 18:24:08 -0700 Subject: [PATCH 650/791] ASoC: amd: acp: return irq error directly platform_get_irq() returns multiple error codes. Return the irq directly instead of just -ENODEV. Signed-off-by: Rosen Penev Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260807012408.55272-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 6ad70aa0ea83..2b8848b6df2b 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1291,7 +1291,7 @@ static int acp_audio_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENODEV; + return irq; status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, 0, "ACP_IRQ", &pdev->dev); From e00a6a89900dfa4cda3669558d2f3581cd41a656 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 7 Aug 2026 20:58:54 +0800 Subject: [PATCH 651/791] ALSA: hda/realtek: Add quirk for Acer Nitro ANV16-42 headset mic The Acer Nitro ANV16-42 (subsystem 0x1025:0x1909, Realtek ALC245) does not detect the headset microphone jack. Adding the ALC2XX_FIXUP_HEADSET_MIC quirk resolves the issue and restores proper headset mic functionality. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221623 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260807125857.678297-1-zhangheng@kylinos.cn --- 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 ae572295550c..150a4db3fc45 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7061,6 +7061,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x1909, "Acer Nitro ANV16-42", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), From 3c6886dec94c7dbe0d8e83e4c7d66a037ae4f7da Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 7 Aug 2026 20:58:55 +0800 Subject: [PATCH 652/791] ALSA: hda/realtek: Add quirk for Acer Gadget E10 ETBook left speaker The Acer Gadget E10 ETBook (subsystem 0x1e50:0x7036, Realtek ALC233) has a left speaker that does not work by default. The BIOS fails to properly configure pin 0x1b, leaving it unconnected. Using hdajackretask to override pin 0x1b as "Internal Speaker" restores left speaker functionality. Add a quirk to apply this pin configuration automatically at probe time. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221435 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260807125857.678297-2-zhangheng@kylinos.cn --- 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 150a4db3fc45..a8a0bf8d72c1 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8183,6 +8183,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x1e50, 0x7007, "Positivo DN50E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1e50, 0x7036, "Acer Gadget E10 ETBook", ALC233_FIXUP_WUJIE_SPEAKERS), SND_PCI_QUIRK(0x1e50, 0x7038, "Positivo DN140", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1ee7, 0x2081, "HONOR MRB-XXX M1020", ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO), From f2d08f3651fb12634347fef2c41687a807a7dcba Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 7 Aug 2026 20:58:56 +0800 Subject: [PATCH 653/791] ALSA: hda/realtek: Fix headset mic on ASUS Vivobook S14 S5406SA On the ASUS Vivobook S14 S5406SA (subsystem 0x104310c4, Lunar Lake platform) with an ALC294 codec, the headset microphone (3.5mm jack) fails to capture any audio. Adding the ALC2XX_FIXUP_HEADSET_MIC quirk resolves the issue and restores proper headset mic recording. Link: https://github.com/thesofproject/linux/issues/5729 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260807125857.678297-3-zhangheng@kylinos.cn --- 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 a8a0bf8d72c1..a1abc47816e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7619,6 +7619,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x10c4, "ASUS Vivobook S14 S5406SA", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), From d152afd1cd7a69c752236b6e825c4e62a74788eb Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 7 Aug 2026 20:58:57 +0800 Subject: [PATCH 654/791] ALSA: hda/conexant: Add pin config quirk for Huawei Matebook The headphone jack is not detected on this Huawei Matebook (Conexant SN6140 codec). The BIOS incorrectly marks Pin 0x18 as [N/A], causing the driver to report hp_outs=0 and no "Headphones" output appears. Override the pin configuration for NID 0x18 to set it as a headphone jack, which restores proper detection and audio routing. Link: https://github.com/thesofproject/sof/issues/10687 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260807125857.678297-4-zhangheng@kylinos.cn --- sound/hda/codecs/conexant.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 5ea4c74715c5..c2f9409b76ef 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -292,6 +292,7 @@ enum { CXT_PINCFG_TOP_SPEAKER, CXT_FIXUP_HP_A_U, CXT_FIXUP_ACER_SWIFT_HP, + CXT_FIXUP_HUAWEI_MATEBOOK_HP, }; /* for hda_fixup_thinkpad_acpi() */ @@ -1035,6 +1036,13 @@ static const struct hda_fixup cxt_fixups[] = { { } }, }, + [CXT_FIXUP_HUAWEI_MATEBOOK_HP] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03211020 }, /* Headphone */ + { } + }, + }, }; static const struct hda_quirk cxt5045_fixups[] = { @@ -1135,6 +1143,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), + SND_PCI_QUIRK(0x19e5, 0x3289, "Huawei Matebook", CXT_FIXUP_HUAWEI_MATEBOOK_HP), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), SND_PCI_QUIRK(0x1d05, 0x3012, "MECHREVO Wujie 15X Pro", CXT_FIXUP_HEADSET_MIC), From 140fe610af607fd70da95c447943fc2690855519 Mon Sep 17 00:00:00 2001 From: Garrett Blackmon Date: Fri, 7 Aug 2026 10:07:08 -0500 Subject: [PATCH 655/791] ALSA: hda/realtek: Fix speakers on ASUS ROG Zephyrus G14 GA403UM The GA403UM (SSID 1043:1044) uses the same ALC285 codec + dual CS35L56 I2C amplifier topology as the GA403U and GA403W variants, which already have quirk entries (1043:1b13, 1043:1024). Without the quirk, the woofers sit on a separate DAC from the tweeters, so the hardware volume control only scales part of the speaker system and the headset microphone pins are not configured. Apply the existing ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC fixup to the GA403UM as well. Signed-off-by: Garrett Blackmon Link: https://patch.msgid.link/20260807150708.33785-1-garrett@blackmon.dev 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 a1abc47816e9..6b36c730ce78 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7612,6 +7612,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), + SND_PCI_QUIRK(0x1043, 0x1044, "ASUS GA403UM", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), From 82b6d37621b00ffbf32552b822d1a339fcf20d86 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 7 Aug 2026 15:22:02 +0800 Subject: [PATCH 656/791] ASoC: fsl_easrc: fix missing return on success in runtime_resume Commit 48d84310be60 ("ASoC: fsl_easrc: Use guard() for spin locks") refactored fsl_easrc_runtime_resume() but accidentally dropped the early return on the success path. The original code had a skip_load label followed by "return 0"; that label was removed during cleanup but the corresponding success return was lost too. As a result, every successful resume falls through into the disable_mem_clk error path and calls clk_disable_unprepare() on a clock that is still in use, leading to an unbalanced clock disable. Restore the missing "return 0" before the disable_mem_clk error label. Fixes: 48d84310be60 ("ASoC: fsl_easrc: Use guard() for spin locks") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260807072202.380021-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 1cece980c389..1b1b57fe0db6 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2364,6 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) goto disable_mem_clk; } + return 0; disable_mem_clk: clk_disable_unprepare(easrc->mem_clk); return ret; From 8a906c0b4f1ba123a95c166f644d2383bf30a420 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 8 Aug 2026 10:45:54 +0900 Subject: [PATCH 657/791] ALSA: ump: Fix corrupted data bytes at MIDI 1.0 SysEx to UMP conversion The cvt_legacy_sysex_to_ump() initialises only the first word of the output packet and ORs the data bytes into it. The second word is left alone, and the conversion context is kept across calls, so it still carries the previous packet's bytes. Those stale bits corrupt the new data. Any SysEx longer than six data bytes is affected. A SysEx with the twelve data bytes 01..0c comes out as: 30160102 03040506 30260708 0b0e0f0e The second packet declares six data bytes and four of them are wrong, inside the declared length. The sibling cvt_legacy_cmd_to_ump() already clears the second word. Do the same here. Fixes: 0b5288f5fe63 ("ALSA: ump: Add legacy raw MIDI support") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260808014554.3550153-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/core/ump_convert.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c index 0fe13d031656..85cc67de6330 100644 --- a/sound/core/ump_convert.c +++ b/sound/core/ump_convert.c @@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt, else status = UMP_SYSEX_STATUS_CONTINUE; *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len); + data[1] = 0; offset = 8; for (i = 0; i < cvt->len; i++) { *data |= cvt->buf[i] << offset; From 786f91da85355cc627ce83db9d81a52447aaa933 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Sun, 9 Aug 2026 01:27:26 +0800 Subject: [PATCH 658/791] ALSA: usb-audio: add QUIRK_FLAG_ALWAYS_SET_RATE for Mackie DLZ Creator XS set_sample_rate_v2v3() returns early when the clock already reports the requested rate: prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); if (prev_rate == rate) goto validation; A device advertising exactly one sample rate always takes this branch, so it never receives a SET_CUR for CS_SAM_FREQ_CONTROL at all. The Mackie DLZ Creator XS (0a73:003a, 14 in / 4 out, 48 kHz only) requires that write. Without it the device drops off the USB bus roughly 0.2-1.8 s into any stream, clearing its port CONNECTION bit; captured audio is byte-correct until the instant it vanishes. USBPcap traces of a cold-booted device on Windows show SET_CUR 48000 issued unconditionally on every stream start, followed by clean streaming. The device is otherwise driven with plain class-compliant UAC2 - it also works on iOS, which cannot load a vendor driver - so no vendor-specific initialization is involved. The device is self-powered, so the resulting state survives a USB replug: initializing it on any host that issues the write leaves it working on Linux until it is power-cycled, which made the failure look intermittent. Add a quirk flag rather than dropping the early exit, since the opposite requirement also exists in-tree: QUIRK_FLAG_FIXED_RATE suppresses rate setting for single-rate devices (JBL Quantum610/810). The two behaviors are device-dependent and cannot both be the default. A/B on identically cold-booted hardware, same kernel, same port, repeated twice: without the flag device dropped after 5-6 s, then again after 3-4 s with the flag 20 s playback followed by 20 s of 14-channel capture, 960000 frames, zero re-enumerations This change was developed with an AI coding assistant. The assistant did the trace analysis that located the bug and wrote the patch and this changelog; the hardware testing, the cold-boot cycles and the decision to submit were the author's. Several earlier hypotheses it proposed - URB queue depth, isochronous packet under-allocation, endpoint start ordering - were disproven by measurement before this one. The bug was located with usbmon on Linux and USBPcap on Windows, by diffing an enumeration capture of a cold-booted device on each host. Verified on physical hardware by the A/B above. Assisted-by: Claude-Code:claude-opus-5 Signed-off-by: JJ Macalinao Link: https://patch.msgid.link/20260808172726.1107550-1-jj@macalinao.org Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 11 +++++++---- sound/usb/quirks.c | 3 +++ sound/usb/usbaudio.h | 6 ++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2e0c18e35281..34832183a1f0 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -600,7 +600,7 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, static int set_sample_rate_v2v3(struct snd_usb_audio *chip, const struct audioformat *fmt, int rate) { - int cur_rate, prev_rate; + int cur_rate, prev_rate = 0; int clock; /* First, try to find a valid clock. This may trigger @@ -625,9 +625,12 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, return clock; } - prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); - if (prev_rate == rate) - goto validation; + if (!(chip->quirk_flags & QUIRK_FLAG_ALWAYS_SET_RATE)) { + prev_rate = get_sample_rate_v2v3(chip, fmt->iface, + fmt->altsetting, clock); + if (prev_rate == rate) + goto validation; + } cur_rate = snd_usb_set_sample_rate_v2v3(chip, fmt, clock, rate); if (cur_rate < 0) { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3330eb604911..d00d6a543a9f 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2333,6 +2333,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */ QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0a73, 0x003a, /* Mackie DLZ Creator XS */ + QUIRK_FLAG_ALWAYS_SET_RATE), DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */ QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */ @@ -2638,6 +2640,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), + QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), NULL }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 31e612500050..c49709d7ad25 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -260,6 +260,10 @@ extern bool snd_usb_skip_validation; * to insufficient buffer depth combined with xHCI scheduling variability. * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. + * QUIRK_FLAG_ALWAYS_SET_RATE: + * Issue SET_CUR for the sample rate even when the clock already reports the + * requested rate. A device advertising a single rate is otherwise never sent + * the request at all, and some require it before streaming will start. */ enum { @@ -295,6 +299,7 @@ enum { QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, + QUIRK_TYPE_ALWAYS_SET_RATE = 32, /* Please also edit snd_usb_audio_quirk_flag_names */ }; @@ -332,5 +337,6 @@ enum { #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) #define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) #define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) +#define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) #endif /* __USBAUDIO_H */ From 7097666b993b37f4e47982026b703b2379a364f8 Mon Sep 17 00:00:00 2001 From: Ajrat Makhmutov Date: Sat, 8 Aug 2026 21:55:00 +0300 Subject: [PATCH 659/791] ALSA: hda/realtek: Enable headset mic on F+ FLAPTOP r The BIOS of the F+ FLAPTOP r laptop (Realtek ALC897, SSID 1e63:6d9a) declares only pin 0x1b, the headphone output of the 3.5 mm combo jack. Every other external pin is left at 0x411111f0, so the headset mic pin 0x19 is never parsed and no headset mic input exists. The pin is wired on this board - retasking it makes the headset mic record. Reuse ALC897_FIXUP_HP_HSMIC_VERB, which already sets the pin config this machine needs: 0x19 as a headset mic without its own presence detect. Only 0x1b reports jack presence here, so a mic pin with presence detect would leave the driver in auto-mic mode waiting for an event that never arrives. Without the quirk the generic parser retasks the lone headphone pin as an input instead. That surfaces as a "Headphone Mic" input which records only the internal mic bleed, so the headset mic appears present but dead. Tested on ALT Linux, kernel 6.12, by recording a CTIA headset mic on the combo jack with the internal mic as a reference. ALSA info before the patch: https://alsa-project.org/db/?f=18363eddea933baee100c9bf461d0e5cf74c8de2 ALSA info after the patch: https://alsa-project.org/db/?f=48ae2cd7aaf1eb0f24639ce83cd38cfd93b25f76 Cc: stable@vger.kernel.org # 6.12.x Signed-off-by: Ajrat Makhmutov Link: https://patch.msgid.link/20260808185500.2564948-1-rauty@altlinux.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc662.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 2cf3664a35ff..a640292c4f10 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -852,6 +852,7 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB), #if 0 /* Below is a quirk table taken from the old code. From 918b8d231c571c50a00efe92ffc8404a537a0490 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 10 Aug 2026 03:36:01 +0930 Subject: [PATCH 660/791] ALSA: FCP: Use a private URB for the notification endpoint fcp_init_notify() used mixer->urb, which snd_usb_mixer_status_create() allocates for the optional UAC2 status interrupt endpoint and mixer.c kills, resubmits and frees. On a device with that endpoint, fcp_init_notify()'s "already set up" early return fires on the status URB and returns success without doing anything. No FCP notification URB is submitted, and cmd_done is left zeroed because it is initialised past that early return and nowhere else. fcp_init() then issues init1_opcode and wait_for_completion_timeout() would crash adding to the zeroed wait.head. fcp_cleanup_urb() would also kill and free mixer.c's status URB. Use a separate URB in fcp_data, and initialise cmd_done in fcp_init_private() where fcp_data is allocated. fcp_init_notify() is reached again after suspend via fcp_reinit(), and the URB kill path in fcp_notify() completes cmd_done, leaving a stale count that would satisfy the next command's wait before the device ACKs. Use reinit_completion() to clear it. Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/2cad281e6434024ca48a9ecc94fa19d6777e9be7.1786290885.git.g@b4.vu --- sound/usb/fcp.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 8f52a3dc9ec3..6bd659d47e8e 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -82,6 +82,7 @@ struct fcp_data { struct mutex mutex; /* serialise access to the device */ struct completion cmd_done; /* wait for command completion */ struct file *file; /* hwdep file */ + struct urb *urb; /* FCP notification endpoint */ struct fcp_notify notify; @@ -186,7 +187,7 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, const int max_retries = 5; int err; - if (!mixer->urb) + if (!private->urb) return -ENODEV; struct fcp_usb_packet *req __free(kfree) = NULL; @@ -301,7 +302,7 @@ static int fcp_reinit(struct usb_mixer_interface *mixer) { struct fcp_data *private = mixer->private_data; - if (mixer->urb) + if (private->urb) return 0; void *step0_resp __free(kfree) = @@ -893,13 +894,15 @@ static int fcp_hwdep_init(struct usb_mixer_interface *mixer) static void fcp_cleanup_urb(struct usb_mixer_interface *mixer) { - if (!mixer->urb) + struct fcp_data *private = mixer->private_data; + + if (!private->urb) return; - usb_kill_urb(mixer->urb); - kfree(mixer->urb->transfer_buffer); - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_kill_urb(private->urb); + kfree(private->urb->transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; } static void fcp_private_free(struct usb_mixer_interface *mixer) @@ -970,37 +973,37 @@ static int fcp_init_notify(struct usb_mixer_interface *mixer) int err; /* Already set up */ - if (mixer->urb) + if (private->urb) return 0; if (usb_pipe_type_check(dev, pipe)) return -EINVAL; - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) + private->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!private->urb) return -ENOMEM; transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); if (!transfer_buffer) { - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_free_urb(private->urb); + private->urb = NULL; return -ENOMEM; } - usb_fill_int_urb(mixer->urb, dev, pipe, + usb_fill_int_urb(private->urb, dev, pipe, transfer_buffer, private->wMaxPacketSize, fcp_notify, mixer, private->bInterval); - init_completion(&private->cmd_done); + reinit_completion(&private->cmd_done); - err = usb_submit_urb(mixer->urb, GFP_KERNEL); + err = usb_submit_urb(private->urb, GFP_KERNEL); if (err) { usb_audio_err(mixer->chip, "%s: usb_submit_urb failed: %d\n", __func__, err); kfree(transfer_buffer); - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_free_urb(private->urb); + private->urb = NULL; } return err; @@ -1053,6 +1056,7 @@ static int fcp_init_private(struct usb_mixer_interface *mixer) return -ENOMEM; mutex_init(&private->mutex); + init_completion(&private->cmd_done); init_waitqueue_head(&private->notify.queue); spin_lock_init(&private->notify.lock); From cd17d6ff7b7d2b1dd9bcc80ae7b4a83773f918c6 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 10 Aug 2026 03:36:11 +0930 Subject: [PATCH 661/791] ALSA: scarlett2: Use a private URB for the notification endpoint scarlett2_init_notify() used mixer->urb, which snd_usb_mixer_status_create() allocates for the UAC2 status interrupt endpoint and mixer.c manages. On a device with that endpoint, the "already in use" check fires on the status URB and returns 0 for success without doing anything. No notification URB is submitted, and cmd_done is left zeroed because it is initialised past that check and nowhere else. scarlett2_usb_init() then issues SCARLETT2_USB_INIT_1 and wait_for_completion_timeout() would crash adding to the zeroed wait.head. Use a separate URB in scarlett2_data, as done for FCP, and initialise cmd_done in scarlett2_init_private(). mixer.c was also freeing the URB in snd_usb_mixer_free() and resubmitting it in snd_usb_mixer_activate(), so scarlett2 must now do both: add scarlett2_cleanup_urb(), called from private_free and private_suspend, and a private_resume callback to re-establish the URB after resume. scarlett2_init_notify() is reached from there, and the URB kill path in scarlett2_notify() completes cmd_done, leaving a stale count that would satisfy the next command's wait before the device ACKs. Use reinit_completion() to clear it. Also free the URB if the transfer buffer allocation fails, and both if usb_submit_urb() fails. Move scarlett2_init_notify() up next to scarlett2_cleanup_urb() so scarlett2_init_private() can reference it without a forward declaration. Fixes: 1b65088958ca ("ALSA: scarlett2: Implement handling of the ACK notification") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ffb8ba37d5d605dfdfd8576949d67098651f9349.1786290885.git.g@b4.vu --- sound/usb/mixer.c | 6 +++ sound/usb/mixer.h | 2 + sound/usb/mixer_scarlett2.c | 98 ++++++++++++++++++++++++------------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 703c118f9d4e..5de182181ede 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3935,6 +3935,12 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) struct usb_mixer_elem_list *list; int id, err; + if (mixer->private_resume) { + err = mixer->private_resume(mixer); + if (err < 0) + return err; + } + /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { for_each_mixer_elem(list, mixer, id) { diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 3fa1bd96f858..037b446d8b6f 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -18,6 +18,7 @@ struct usb_mixer_interface { struct usb_host_interface *hostif; struct list_head list; unsigned int ignore_ctl_error; + /* UAC2 status interrupt endpoint; owned by mixer.c */ struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ struct usb_mixer_elem_list **id_elems; @@ -42,6 +43,7 @@ struct usb_mixer_interface { void *private_data; void (*private_free)(struct usb_mixer_interface *mixer); void (*private_suspend)(struct usb_mixer_interface *mixer); + int (*private_resume)(struct usb_mixer_interface *mixer); }; #define MAX_CHANNELS 64 /* max logical channels */ diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 78fb72e626ca..502854cc9f9f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -1403,6 +1403,7 @@ struct scarlett2_data { struct usb_mixer_interface *mixer; struct mutex usb_mutex; /* prevent sending concurrent USB requests */ struct completion cmd_done; + struct urb *urb; /* notification endpoint */ struct mutex data_mutex; /* lock access to this data */ u8 running; u8 hwdep_in_use; @@ -8565,13 +8566,70 @@ static void scarlett2_notify(struct urb *urb) } } -/*** Cleanup/Suspend Callbacks ***/ +/*** Notification URB and Cleanup/Suspend Callbacks ***/ + +/* Submit a URB to receive notifications from the device */ +static int scarlett2_init_notify(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct scarlett2_data *private = mixer->private_data; + unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); + void *transfer_buffer; + int err; + + /* Already set up */ + if (private->urb) + return 0; + + if (usb_pipe_type_check(dev, pipe)) + return -EINVAL; + + private->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!private->urb) + return -ENOMEM; + + transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); + if (!transfer_buffer) { + usb_free_urb(private->urb); + private->urb = NULL; + return -ENOMEM; + } + + usb_fill_int_urb(private->urb, dev, pipe, + transfer_buffer, private->wMaxPacketSize, + scarlett2_notify, mixer, private->bInterval); + + reinit_completion(&private->cmd_done); + + err = usb_submit_urb(private->urb, GFP_KERNEL); + if (err) { + kfree(transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; + } + + return err; +} + +static void scarlett2_cleanup_urb(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!private->urb) + return; + + usb_kill_urb(private->urb); + kfree(private->urb->transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; +} static void scarlett2_private_free(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; cancel_delayed_work_sync(&private->work); + scarlett2_cleanup_urb(mixer); kfree(private); mixer->private_data = NULL; } @@ -8582,6 +8640,8 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) if (cancel_delayed_work_sync(&private->work)) scarlett2_config_save(private->mixer); + + scarlett2_cleanup_urb(mixer); } /*** Initialisation ***/ @@ -8701,11 +8761,13 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mutex_init(&private->usb_mutex); mutex_init(&private->data_mutex); + init_completion(&private->cmd_done); INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); mixer->private_data = private; mixer->private_free = scarlett2_private_free; mixer->private_suspend = scarlett2_private_suspend; + mixer->private_resume = scarlett2_init_notify; private->info = entry->info; @@ -8722,40 +8784,6 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, return scarlett2_find_fc_interface(mixer->chip->dev, private); } -/* Submit a URB to receive notifications from the device */ -static int scarlett2_init_notify(struct usb_mixer_interface *mixer) -{ - struct usb_device *dev = mixer->chip->dev; - struct scarlett2_data *private = mixer->private_data; - unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); - void *transfer_buffer; - - if (mixer->urb) { - usb_audio_err(mixer->chip, - "%s: mixer urb already in use!\n", __func__); - return 0; - } - - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; - - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) - return -ENOMEM; - - transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; - - usb_fill_int_urb(mixer->urb, dev, pipe, - transfer_buffer, private->wMaxPacketSize, - scarlett2_notify, mixer, private->bInterval); - - init_completion(&private->cmd_done); - - return usb_submit_urb(mixer->urb, GFP_KERNEL); -} - /* Cargo cult proprietary initialisation sequence */ static int scarlett2_usb_init(struct usb_mixer_interface *mixer) { From fabae5548149ef5c8a056875d9ebdc38366db539 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 10 Aug 2026 07:57:28 +0200 Subject: [PATCH 662/791] ALSA: usx2y: Stop clearing urb->hcpriv before submission This is managed by USB core and drivers aren't expected to touch it. It should only be not NULL on a submitted URB, in which case clearing defeats the "submitted while active" sanity check in usb_submit_urb() and may crash the HCD handling the URB and panic the kernel. Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260810075728.483c827e.michal.pecio@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/usx2y/usbusx2yaudio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 3808df54727d..a3a0bc13632b 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -167,7 +167,6 @@ static int usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, i if (!urb) return -ENODEV; urb->start_frame = frame + NRURBS * nr_of_packs(); // let hcd do rollover sanity checks - urb->hcpriv = NULL; urb->dev = subs->usx2y->dev; /* we need to set this at each time */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { From 530e344e4b7ebaef88b845c3a2413a114f517d79 Mon Sep 17 00:00:00 2001 From: Le Qi Date: Mon, 10 Aug 2026 11:10:32 +0800 Subject: [PATCH 663/791] ASoC: qcom: sc8280xp: configure codec sysclk for QCS615 Continuous high-amplitude noise could occur in the DA7213 microphone capture path after a Bluetooth out-of-range/reset event followed by reconnection. The noise was present in both the raw ALSA capture and PipeWire input, confirming that it originated before Bluetooth encoding. The codec already obtains and enables MCLK through its DT clock and bias-level handling. However, the machine driver did not explicitly configure the codec sysclk during hw_params(). Enable codec_sysclk_set for QCS615 so that the DA7213 clock source and rate are configured before the codec power-up sequence. Verified on QCS615 Talos with repeated Bluetooth disconnect and reconnect cycles. The noise was no longer reproducible. Signed-off-by: Le Qi Link: https://patch.msgid.link/20260810031032.2001053-1-le.qi@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 597c0d887d2f..913ae81be424 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -462,6 +462,7 @@ static const struct qcom_snd_soc_common qcs615_priv_data = { .driver_name = "qcs615", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .codec_sysclk_set = true, }; static const struct qcom_snd_soc_common qcm6490_priv_data = { From 3eea69748a49b179642e4743dd0ea922ec0da354 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:33 +0200 Subject: [PATCH 664/791] ASoC: aw87390: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-2-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw87390.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 8150670fde2d..5555c45dffcc 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -248,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { static int aw87390_request_firmware_file(struct aw87390 *aw87390) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; int ret; aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; @@ -263,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw87390->aw_cfg) { - release_firmware(cont); + if (!aw87390->aw_cfg) return -ENOMEM; - } aw87390->aw_cfg->len = cont->size; memcpy(aw87390->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); if (ret) { From a26aa707bdf60d36d76cce03580e1bd744b03c69 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:34 +0200 Subject: [PATCH 665/791] ASoC: aw88081: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-3-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw88081.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index a3cc027de606..3247ba5c71b8 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -1133,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg static int aw88081_request_firmware_file(struct aw88081 *aw88081) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; int ret; @@ -1147,17 +1147,14 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) AW88081_ACF_FILE, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); aw88081->aw_cfg = aw_cfg; - release_firmware(cont); - ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); if (ret) return ret; From dba82cc766613856a2b214936720c2f62bc07758 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:35 +0200 Subject: [PATCH 666/791] ASoC: aw88166: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-4-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw88166.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index b72f87f677dd..d2a138211c59 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -1570,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg static int aw88166_request_firmware_file(struct aw88166 *aw88166) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; const char *fw_name; int ret; @@ -1590,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88166->aw_cfg) { - release_firmware(cont); + if (!aw88166->aw_cfg) return -ENOMEM; - } + aw88166->aw_cfg->len = (int)cont->size; memcpy(aw88166->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); if (ret) { From 037729f509fe0b0ad0b3f02d8eb4e95e961e0ef6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:36 +0200 Subject: [PATCH 667/791] ASoC: aw88261: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-5-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index acbd7de2e40e..b8d06534bc8f 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1149,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg static int aw88261_request_firmware_file(struct aw88261 *aw88261) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; const char *fw_name; int ret; @@ -1169,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) fw_name, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); - release_firmware(cont); aw88261->aw_cfg = aw_cfg; From d8e40355d9a3fe58b3e0c87e5d19a6ffd3710f65 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:37 +0200 Subject: [PATCH 668/791] ASoC: aw88395: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-6-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw88395/aw88395.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index e9ff2c79ac15..2f4108624d13 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -457,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) static int aw88395_request_firmware_file(struct aw88395 *aw88395) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; struct aw_container *aw_cfg; int ret; @@ -473,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) AW88395_ACF_FILE, cont ? cont->size : 0); aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw_cfg) { - release_firmware(cont); + if (!aw_cfg) return -ENOMEM; - } + aw_cfg->len = (int)cont->size; memcpy(aw_cfg->data, cont->data, cont->size); - release_firmware(cont); aw88395->aw_cfg = aw_cfg; From 1767b85125e2e3f8dc9623bc89015402fffcd341 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:38 +0200 Subject: [PATCH 669/791] ASoC: aw88399: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-7-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/aw88399-lib.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 5c7982891def..809c2faa0c0c 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1282,7 +1282,7 @@ static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg int aw88399_request_firmware_file(struct aw88399 *aw88399) { - const struct firmware *cont = NULL; + const struct firmware *cont __free(firmware) = NULL; int ret; aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; @@ -1298,13 +1298,11 @@ int aw88399_request_firmware_file(struct aw88399 *aw88399) aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88399->aw_cfg) { - release_firmware(cont); + if (!aw88399->aw_cfg) return -ENOMEM; - } + aw88399->aw_cfg->len = (int)cont->size; memcpy(aw88399->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); if (ret) { From aec8a1e14a80abbcf895b4780d68831d86cfcb88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:39 +0200 Subject: [PATCH 670/791] ASoC: fs-amp-lib: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Nick Li Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-8-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/fs-amp-lib.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c index c8f56617e370..a562c72fc524 100644 --- a/sound/soc/codecs/fs-amp-lib.c +++ b/sound/soc/codecs/fs-amp-lib.c @@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) { - const struct firmware *cont; + const struct firmware *cont __free(firmware) = NULL; struct fs_fwm_header *hdr; int ret; @@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); - release_firmware(cont); if (!hdr) return -ENOMEM; From a57beee8f816cc43fe0e78b794fa6cb54b3a2034 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:40 +0200 Subject: [PATCH 671/791] ASoC: hdac_hda: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-9-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 1ab5f8a26e03..1c06fdbf0e71 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) #ifdef CONFIG_SND_HDA_PATCH_LOADER if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; dev_info(&hdev->dev, "Applying patch firmware '%s'\n", loadable_patch[hda_pvt->dev_index]); @@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); goto error_no_pm; } - release_firmware(fw); } } #endif From b37d70e5bfc32be630b2b86943f0c9f49382eee3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:41 +0200 Subject: [PATCH 672/791] ASoC: max98390: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-10-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/max98390.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 66309e87fdbd..2295fc057c71 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) const char *vendor, *product; struct max98390_priv *max98390 = snd_soc_component_get_drvdata(component); - const struct firmware *fw; char *dsm_param; vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) snprintf(filename, sizeof(filename), "%s", max98390->dsm_param_name); } + + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, filename, component->dev); if (ret) { ret = request_firmware(&fw, "dsm_param.bin", component->dev); @@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) ret = request_firmware(&fw, "dsmparam.bin", component->dev); if (ret) - goto err; + return ret; } } @@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { dev_err(component->dev, "param fw is invalid.\n"); - ret = -EINVAL; - goto err_alloc; + return -EINVAL; } dsm_param = (char *)fw->data; param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; @@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { dev_err(component->dev, "param fw is invalid.\n"); - ret = -EINVAL; - goto err_alloc; + return -EINVAL; } regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; @@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) dsm_param, param_size); regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); -err_alloc: - release_firmware(fw); -err: - return ret; + return 0; } static void max98390_init_regs(struct snd_soc_component *component) From 5dd15c805eefd56baabdca3cbe8f64e6c5f02eba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:42 +0200 Subject: [PATCH 673/791] ASoC: ntpfw: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-11-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/ntpfw.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c index 5ced2e966ab7..b6443e24ae8e 100644 --- a/sound/soc/codecs/ntpfw.c +++ b/sound/soc/codecs/ntpfw.c @@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) { struct device *dev = &i2c->dev; const struct ntpfw_chunk *chunk; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; const u8 *data; size_t leftover; int ret; @@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) return ret; } - if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { - ret = -EINVAL; - goto done; - } + if (!ntpfw_verify(dev, fw->data, fw->size, magic)) + return -EINVAL; data = fw->data + sizeof(struct ntpfw_header); leftover = fw->size - sizeof(struct ntpfw_header); @@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) while (leftover) { chunk = (struct ntpfw_chunk *)data; - if (!ntpfw_verify_chunk(dev, chunk, leftover)) { - ret = -EINVAL; - goto done; - } + if (!ntpfw_verify_chunk(dev, chunk, leftover)) + return -EINVAL; ret = ntpfw_send_chunk(i2c, chunk); if (ret) - goto done; + return ret; data += be16_to_cpu(chunk->length) + sizeof(*chunk); leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); } -done: - release_firmware(fw); - - return ret; + return 0; } EXPORT_SYMBOL_GPL(ntpfw_load); From c0124e91a9f2f4b8a466f4685631ffb415d21501 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:43 +0200 Subject: [PATCH 674/791] ASoC: pcm6240: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Since the firmware release is cleaned up automatically, we can convert the mutex call with guard() gracefully, too. Only the code refactoring, no functional changes. Reviewed-by: Herve Codina Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-12-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/pcm6240.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index a2b66eae6ac4..db85ae2f8aed 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) { struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); struct i2c_adapter *adap = pcm_dev->client->adapter; - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; int ret, i, j; - mutex_lock(&pcm_dev->codec_lock); + guard(mutex)(&pcm_dev->codec_lock); pcm_dev->component = comp; @@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) for (j = 0; j < 2; j++) { ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); if (ret < 0) - goto out; + return ret; } } @@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) if (ret) { dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, pcm_dev->bin_name, ret); - goto out; + return ret; } ret = pcmdev_regbin_ready(fw_entry, pcm_dev); if (ret) { dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, pcm_dev->bin_name, ret); - goto out; + return ret; } - ret = pcmdev_profile_ctrl_add(pcm_dev); -out: - release_firmware(fw_entry); - mutex_unlock(&pcm_dev->codec_lock); - return ret; + return pcmdev_profile_ctrl_add(pcm_dev); } From 38a3855d0c378d3e414ea2eb4fd4e0e099ff95fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:44 +0200 Subject: [PATCH 675/791] ASoC: peb2466: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Reviewed-by: Herve Codina Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-13-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/peb2466.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 5a1ed02abb84..f1ded68c2d75 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -1538,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, fw_name, component->dev); if (ret) return ret; - ret = peb2466_fw_parse(component, fw->data, fw->size); - release_firmware(fw); - - return ret; + return peb2466_fw_parse(component, fw->data, fw->size); } static int peb2466_component_probe(struct snd_soc_component *component) From a372669b8dec902533603fcdca23e5aef69e98ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:45 +0200 Subject: [PATCH 676/791] ASoC: rt1320-sdw: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Oder Chiou Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-14-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 3a5eebcfefdd..8d2d6697a502 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1786,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) { struct sdw_slave *slave = rt1320->sdw_slave; - const struct firmware *patch; + const struct firmware *patch __free(firmware) = NULL; const char *filename; unsigned int addr, val, min_addr, max_addr; const unsigned char *ptr; @@ -1840,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) if (addr > max_addr || addr < min_addr) { dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); - goto _exit_; + return; } if (val > 0xff) { dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); - goto _exit_; + return; } regmap_write(rt1320->regmap, addr, val); } } -_exit_: - release_firmware(patch); } } @@ -1924,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; static const char func_tag[] = "FUNC"; static const char xu_tag[] = "XU"; - const struct firmware *rae_fw = NULL; + const struct firmware *rae_fw __free(firmware) = NULL; unsigned int fw_offset; unsigned char *fw_data; unsigned char *param_data; @@ -1977,7 +1975,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } if (!retry && !(value & 0x40)) { dev_err(dev, "%s: RAE is not ready to load\n", __func__); - release_firmware(rae_fw); return -ETIMEDOUT; } break; @@ -1998,7 +1995,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } if (!retry && !(value & 0x40)) { dev_err(dev, "%s: RAE is not ready to load\n", __func__); - release_firmware(rae_fw); return -ETIMEDOUT; } break; @@ -2057,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } regcache_cache_bypass(rt1320->regmap, false); - release_firmware(rae_fw); } else { dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); @@ -2124,7 +2119,7 @@ struct rt1320_dspfwheader { struct rt1320_dspfwheader *fwheader; struct rt1320_imageinfo *ptr_img; struct sdw_bpt_section sec[10]; - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; unsigned char *fw_data; bool dev_fw_match = false; static const char hdr_sig[] = "AFX"; @@ -2178,7 +2173,6 @@ struct rt1320_dspfwheader { if (fwheader->sync != 0x0a1c5679) { dev_err(dev, "%s: FW sync error\n", __func__); - release_firmware(fw); goto _exit_; } @@ -2256,7 +2250,6 @@ struct rt1320_dspfwheader { } regcache_cache_bypass(rt1320->regmap, false); - release_firmware(fw); if (!dev_fw_match) { dev_err(dev, "%s: FW file doesn't match to device\n", __func__); From c36d435b745582cb740cfa6f748f4535c322b0d2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:46 +0200 Subject: [PATCH 677/791] ASoC: rt5575: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Oder Chiou Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-15-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/rt5575-spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index d5b3a57c8866..750f1f7169e6 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t int rt5575_spi_fw_load(struct spi_device *spi) { struct device *dev = &spi->dev; - const struct firmware *firmware; int i, ret; static const char * const fw_path[] = { "realtek/rt5575/rt5575_fw1.bin", @@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { + const struct firmware *firmware __free(firmware) = NULL; ret = request_firmware(&firmware, fw_path[i], dev); if (ret) { dev_err(dev, "Request firmware failure: %d\n", ret); @@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) } rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); - release_firmware(firmware); } return 0; From 0582725c6b4252bf56afafbb19e26fd6c00f634e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:47 +0200 Subject: [PATCH 678/791] ASoC: rt5677: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Oder Chiou Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-16-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 3e4d1dbce740..4757017cc83d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -849,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) { - const struct firmware *fwp; struct device *dev = rt5677->component->dev; - int ret = 0; + int ret; /* Load dsp firmware from rt5677_elf_vad file */ + const struct firmware *fwp __free(firmware) = NULL; ret = request_firmware(&fwp, "rt5677_elf_vad", dev); if (ret) { dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); @@ -861,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) } dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); - ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); - release_firmware(fwp); - return ret; + return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); } static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) From d8c13497200faef7584d968b48abff073e1031fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:48 +0200 Subject: [PATCH 679/791] ASoC: rt722-sdca: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup, as well as the firmware file name being released with __free(kfree). Only the code refactoring, no functional changes. Cc: Oder Chiou Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-17-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 27bb0eb1ece7..4cbe9e909585 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) static const char func_tag[] = "FUNC"; static const char xu_tag[] = "XU"; const char *dmi_vendor, *dmi_product, *dmi_sku; - char *cae_filename; - const struct firmware *cae_fw = NULL; unsigned int cae_st_spk, cae_st_hp, cae_st_mic; unsigned int func, value; unsigned int combined_val; @@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) space = strchr(dmi_sku, ' '); s_len = space ? space - dmi_sku : strlen(dmi_sku); - cae_filename = kasprintf(GFP_KERNEL, + char *cae_filename __free(kfree) = + kasprintf(GFP_KERNEL, "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", v_len, dmi_vendor, p_len, dmi_product, @@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); usleep_range(50000, 60000); + const struct firmware *cae_fw __free(firmware) = NULL; request_firmware(&cae_fw, cae_filename, dev); - kfree(cae_filename); if (!cae_fw) { dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); return -ENOENT; @@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) regcache_cache_bypass(rt722->regmap, false); rt722->cae_update_done = 1; dev_dbg(dev, "%s: CAE FW update done.\n", __func__); - release_firmware(cae_fw); return 0; verify_abort: @@ -565,7 +563,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) out_release: rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, RT722_MISC_CTRL1, 0x8000, 0x0000); - release_firmware(cae_fw); dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); return ret; } From 4eea8eb99d6cd78a0246d4f7da27d945e36f29ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:49 +0200 Subject: [PATCH 680/791] ASoC: sigmadsp: se auto-cleanup for firmware loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Cc: Lars-Peter Clausen Cc: Nuno Sá Acked-by: Nuno Sá Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-18-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/sigmadsp.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index b7dbeb237447..4ecbbac93258 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -484,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) { const struct sigma_firmware_header *ssfw_head; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; u32 crc; @@ -492,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) ret = request_firmware(&fw, name, sigmadsp->dev); if (ret) { pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); - goto done; + return ret; } /* then verify the header */ @@ -506,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) */ if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); - goto done; + return -EINVAL; } ssfw_head = (void *)fw->data; if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); - goto done; + return -EINVAL; } crc = crc32(0, fw->data + sizeof(*ssfw_head), @@ -521,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) if (crc != le32_to_cpu(ssfw_head->crc)) { dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", le32_to_cpu(ssfw_head->crc), crc); - goto done; + return -EINVAL; } switch (ssfw_head->version) { @@ -542,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) if (ret) sigmadsp_firmware_release(sigmadsp); -done: - release_firmware(fw); - return ret; } From 2afbdeb48302ab076e302f11be79e4323607ec15 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:50 +0200 Subject: [PATCH 681/791] ASoC: sma1307: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Kiseok Jo Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-19-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/sma1307.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index c52fe95b30c6..adb369a29b9d 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int size, offset, num_mode; int ret; @@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil return; } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { dev_err(sma1307->dev, "%s: Invalid file\n", __func__); - release_firmware(fw); sma1307->set.status = false; return; } int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); if (!data) { - release_firmware(fw); sma1307->set.status = false; return; } size = fw->size >> 2; memcpy(data, fw->data, fw->size); - release_firmware(fw); - /* HEADER */ sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; sma1307->set.checksum = data[sma1307->set.header_size - 2]; From 37cb22498fa8d450064661c9f691f28ba1bae887 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:51 +0200 Subject: [PATCH 682/791] ASoC: tas2781: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Shenghao Ding Cc: Kevin Lu Cc: Baojun Xu Cc: Sen Wang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-20-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-fmwlib.c | 45 +++++++++++-------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 11d1c2ac865b..df30abfb3b6b 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2243,7 +2243,7 @@ int tas2781_load_calibration(void *context, char *file_name, { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; struct tasdevice_fw *tas_fmw; struct firmware fmw; int offset = 0; @@ -2253,60 +2253,50 @@ int tas2781_load_calibration(void *context, char *file_name, if (ret) { dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", __func__, file_name); - goto out; + return ret; } if (!fw_entry->size) { dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", __func__, (unsigned long)fw_entry->size); - ret = -EINVAL; - goto out; + return -EINVAL; } fmw.size = fw_entry->size; fmw.data = fw_entry->data; tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); - if (!tasdev->cali_data_fmw) { - ret = -ENOMEM; - goto out; - } + if (!tasdev->cali_data_fmw) + return -ENOMEM; + tas_fmw->dev = tas_priv->dev; offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); if (offset == -EINVAL) { dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); - ret = offset; - goto out; + return -EINVAL; } offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); if (offset == -EINVAL) { dev_err(tas_priv->dev, "%s: fw_parse_variable_header_cal EXIT!\n", __func__); - ret = offset; - goto out; + return -EINVAL; } offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); - ret = offset; - goto out; + return offset; } offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); - ret = offset; - goto out; + return offset; } offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); if (offset < 0) { dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); - ret = offset; - goto out; + return offset; } -out: - release_firmware(fw_entry); - - return ret; + return 0; } EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); @@ -2399,7 +2389,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, int tasdevice_dsp_parser(void *context) { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; - const struct firmware *fw_entry; + const struct firmware *fw_entry __free(firmware) = NULL; int ret; ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, @@ -2407,15 +2397,10 @@ int tasdevice_dsp_parser(void *context) if (ret) { dev_err(tas_priv->dev, "%s: load %s error\n", __func__, tas_priv->coef_binaryname); - goto out; + return ret; } - ret = tasdevice_dspfw_ready(fw_entry, tas_priv); - release_firmware(fw_entry); - fw_entry = NULL; - -out: - return ret; + return tasdevice_dspfw_ready(fw_entry, tas_priv); } EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); From 834c7c851179d86f04c18f0e69466e395128875f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:52 +0200 Subject: [PATCH 683/791] ASoC: tas5805m: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-21-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/tas5805m.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index f76e04b403b5..d32796a6fa75 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -457,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) struct tas5805m_priv *tas5805m; char filename[128]; const char *config_name; - const struct firmware *fw; int ret; regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); @@ -502,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", config_name); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, filename, dev); if (ret) return ret; if ((fw->size < 2) || (fw->size & 1)) { dev_err(dev, "firmware is invalid\n"); - release_firmware(fw); return -EINVAL; } tas5805m->dsp_cfg_len = fw->size; tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); - if (!tas5805m->dsp_cfg_data) { - release_firmware(fw); + if (!tas5805m->dsp_cfg_data) return -ENOMEM; - } - - release_firmware(fw); /* Do the first part of the power-on here, while we can expect * the I2S interface to be quiet. We must raise PDN# and then From e420e87ddc9abfc263c50acd77315486f3defbfa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:53 +0200 Subject: [PATCH 684/791] ASoC: tlv320aic31xx: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Shenghao Ding Cc: Kevin Lu Cc: Baojun Xu Cc: Sen Wang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-22-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 1d2e0ea6d4fe..43bcbc5449e1 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, const char *fw_name) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, fw_name, aic31xx->dev); if (ret) return ret; - ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); - - release_firmware(fw); - - return ret; + return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); } static int aic31xx_i2c_probe(struct i2c_client *i2c) From 148f54aecccc769dd17414ea2e2a2b68f7b4ba4e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:54 +0200 Subject: [PATCH 685/791] ASoC: wm0010: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-23-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 58 ++++++++++++++------------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 58c0c601ee6c..c44abffe9b56 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -333,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp struct wm0010_boot_xfer *xfer; int ret; DECLARE_COMPLETION_ONSTACK(done); - const struct firmware *fw; const struct dfw_binrec *rec; const struct dfw_inforec *inforec; u64 *img; @@ -342,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp INIT_LIST_HEAD(&xfer_list); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, name, component->dev); if (ret != 0) { dev_err(component->dev, "Failed to request application(%s): %d\n", @@ -360,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp /* First record should be INFO */ if (rec->command != DFW_CMD_INFO) { dev_err(component->dev, "First record not INFO\r\n"); - ret = -EINVAL; - goto abort; + return -EINVAL; } if (inforec->info_version != INFO_VERSION) { dev_err(component->dev, "Unsupported version (%02d) of INFO record\r\n", inforec->info_version); - ret = -EINVAL; - goto abort; + return -EINVAL; } dev_dbg(component->dev, "Version v%02d INFO record found\r\n", @@ -378,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp /* Check it's a DSP file */ if (dsp != DEVICE_ID_WM0010) { dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); - ret = -EINVAL; - goto abort; + return -EINVAL; } /* Skip the info record as we don't need to send it */ @@ -404,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { ret = -ENOMEM; - goto abort1; + goto abort; } xfer->t.rx_buf = out; img = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img) { ret = -ENOMEM; - goto abort1; + goto abort; } xfer->t.tx_buf = img; @@ -449,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp ret = spi_async(spi, &xfer->m); if (ret != 0) { dev_err(component->dev, "Write failed: %d\n", ret); - goto abort1; + goto abort; } if (wm0010->boot_failed) { dev_dbg(component->dev, "Boot fail!\n"); ret = -EINVAL; - goto abort1; + goto abort; } } @@ -463,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp ret = 0; -abort1: +abort: while (!list_empty(&xfer_list)) { xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, list); @@ -473,8 +470,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp kfree(xfer); } -abort: - release_firmware(fw); return ret; } @@ -482,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) { struct spi_device *spi = to_spi_device(component->dev); struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); - const struct firmware *fw; struct spi_message m; struct spi_transfer t; - u32 *img; - u8 *out; int i; int ret = 0; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); if (ret != 0) { dev_err(component->dev, "Failed to request stage2 loader: %d\n", @@ -500,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); /* Copy to local buffer first as vmalloc causes problems for dma */ - img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); - if (!img) { - ret = -ENOMEM; - goto abort2; - } + u32 *img __free(kfree) = + kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); + if (!img) + return -ENOMEM; - out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); - if (!out) { - ret = -ENOMEM; - goto abort1; - } + u8 *out __free(kfree) = + kzalloc(fw->size, GFP_KERNEL | GFP_DMA); + if (!out) + return -ENOMEM; spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -527,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) ret = spi_sync(spi, &m); if (ret != 0) { dev_err(component->dev, "Initial download failed: %d\n", ret); - goto abort; + return ret; } /* Look for errors from the boot ROM */ @@ -536,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) dev_err(component->dev, "Boot ROM error: %x in %d\n", out[i], i); wm0010_mark_boot_failure(wm0010); - ret = -EBUSY; - goto abort; + return -EBUSY; } } -abort: - kfree(out); -abort1: - kfree(img); -abort2: - release_firmware(fw); - return ret; + return 0; } static int wm0010_boot(struct snd_soc_component *component) From 2db054a5efa5f1a99aac4d714726b2b743b4720a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:55 +0200 Subject: [PATCH 686/791] ASoC: wm2000: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-24-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/wm2000.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 897b0acac5f3..41c8cfb346c6 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -796,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) struct wm2000_priv *wm2000; struct wm2000_platform_data *pdata; const char *filename; - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; int ret, i; unsigned int reg; u16 id; @@ -814,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) ret = PTR_ERR(wm2000->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto out; + return ret; } for (i = 0; i < WM2000_NUM_SUPPLIES; i++) @@ -908,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) err_supplies: regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); - -out: - release_firmware(fw); return ret; } From b6ba77dfeb2abb52945ea205a1865c1a60bdfebe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:56 +0200 Subject: [PATCH 687/791] ASoC: zl38060: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-25-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/zl38060.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index 7de4014e626d..894b8eb42e39 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) static int zl38_load_firmware(struct device *dev, struct regmap *regmap) { const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; u32 addr; u16 len; int err; @@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) return err; err = zl38_fw_enter_boot_mode(regmap); if (err) - goto out; + return err; rec = (const struct ihex_binrec *)fw->data; while (rec) { addr = be32_to_cpu(rec->addr); @@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) err = -EINVAL; } if (err) - goto out; + return err; /* next ! */ rec = ihex_next_binrec(rec); } - err = zl38_fw_go(regmap); -out: - release_firmware(fw); - return err; + return zl38_fw_go(regmap); } From 997261a59b9458bce2a38a93322309404470ee2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:57 +0200 Subject: [PATCH 688/791] ASoC: fsl: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Shengjiu Wang Cc: Xiubo Li Cc: Fabio Estevam Cc: Nicolin Chen Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-26-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 41d100500534..982827204351 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -921,10 +921,10 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) { struct device *dev = &xcvr->pdev->dev; - const struct firmware *fw; int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; u32 mask, val; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); if (ret) { dev_err(dev, "failed to request firmware.\n"); @@ -936,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ if (rem > 16384) { dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); - release_firmware(fw); return -ENOMEM; } @@ -947,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) if (ret < 0) { dev_err(dev, "FW: failed to set page %d, err=%d\n", page, ret); - goto err_firmware; + return ret; } off = page * size; @@ -968,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) } } -err_firmware: - release_firmware(fw); - if (ret < 0) - return ret; - /* configure watermarks */ mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); From 6a052cf54fa19940e3cddd1fd63e2fac6cdb374a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:58 +0200 Subject: [PATCH 689/791] ASoC: Intel: avs: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Cezary Rojewski Acked-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-27-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/intel/avs/topology.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 9033f683393c..673ac31f2fea 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -2222,7 +2222,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) int avs_load_topology(struct snd_soc_component *comp, const char *filename) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int ret; ret = request_firmware(&fw, filename, comp->dev); @@ -2235,7 +2235,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) if (ret < 0) dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); - release_firmware(fw); return ret; } From a0dae90ea9668a050c37cf8e0938a113872971f9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 15:59:59 +0200 Subject: [PATCH 690/791] ASoC: Intel: catpt: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Cezary Rojewski Acked-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-28-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/intel/catpt/loader.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index e7ba9e1e60ae..724cbe337db4 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -568,27 +568,24 @@ static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan * const char *name, bool restore) { struct catpt_fw_hdr *fw; - struct firmware *img; dma_addr_t paddr; void *vaddr; int ret; - ret = request_firmware((const struct firmware **)&img, name, cdev->dev); + const struct firmware *img __free(firmware) = NULL; + ret = request_firmware(&img, name, cdev->dev); if (ret) return ret; fw = (struct catpt_fw_hdr *)img->data; if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { dev_err(cdev->dev, "firmware signature mismatch\n"); - ret = -EINVAL; - goto release_fw; + return -EINVAL; } vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); - if (!vaddr) { - ret = -ENOMEM; - goto release_fw; - } + if (!vaddr) + return -ENOMEM; memcpy(vaddr, img->data, img->size); fw = (struct catpt_fw_hdr *)vaddr; @@ -598,8 +595,6 @@ static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan * ret = catpt_load_firmware(cdev, chan, paddr, fw); dma_free_coherent(cdev->dev, img->size, vaddr, paddr); -release_fw: - release_firmware(img); return ret; } From c5663770970c75b168e8dff9f2b2a334d4449865 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 16:00:00 +0200 Subject: [PATCH 691/791] ASoC: qcom: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Srinivas Kandagatla Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-29-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 54661bcb006c..faafbc5c0ad0 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -1416,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; struct device *dev = component->dev; - const struct firmware *fw; int ret; /* Inline with Qualcomm UCM configs and linux-firmware path */ @@ -1426,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) if (!tplg_fw_name) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_fw_name, dev); if (ret < 0) { dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); @@ -1438,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) dev_err(dev, "tplg component load failed: %d\n", ret); } - release_firmware(fw); - return ret; } EXPORT_SYMBOL_GPL(audioreach_tplg_init); From 81ab98716983e1dd43de6f3c0f5a3a78a8c66dc1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 16:00:01 +0200 Subject: [PATCH 692/791] ASoC: renesas: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-30-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/renesas/siu_dai.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c index 039b1264d90d..85dee9025710 100644 --- a/sound/soc/renesas/siu_dai.c +++ b/sound/soc/renesas/siu_dai.c @@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { static int siu_probe(struct platform_device *pdev) { - const struct firmware *fw_entry; struct resource *res, *region; struct siu_info *info; int ret; @@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) siu_i2s_data = info; info->dev = &pdev->dev; + const struct firmware *fw_entry __free(firmware) = NULL; ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); if (ret) return ret; @@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) */ memcpy(&info->fw, fw_entry->data, fw_entry->size); - release_firmware(fw_entry); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; From 73a5271e10da282705793b6e7f3f1197b1ea998a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 16:00:02 +0200 Subject: [PATCH 693/791] ASoC: SDCA: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) auto-cleanup. Only the code refactoring, no functional changes. Cc: Charles Keepax Cc: Maciej Strozek Cc: Bard Liao Cc: Pierre-Louis Bossart Reviewed-by: Charles Keepax Tested-by: Charles Keepax Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-31-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_fdl.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index dbe572336f8c..150e36ed24bc 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, { struct device *dev = interrupt->dev; struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; - const struct firmware *firmware = NULL; struct acpi_sw_file *swf = NULL, *tmp; struct sdca_fdl_file *fdl_file; char *disk_filename; @@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); + const struct firmware *firmware __free(firmware) = NULL; ret = firmware_request_nowarn(&firmware, disk_filename, dev); kfree(disk_filename); if (ret) { @@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, if (!swf) { dev_err(dev, "failed to locate SWF\n"); - ret = -ENOENT; - goto error; + return -ENOENT; } dev_info(dev, "loading SWF: %x-%x-%x\n", @@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, swf->file_length - offsetof(struct acpi_sw_file, data)); - -error: - release_firmware(firmware); return ret; } From 6d0a9e4df17979ef8acbf8d7d6145fb1375e45cd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Aug 2026 16:00:03 +0200 Subject: [PATCH 694/791] ASoC: SOF: Use auto-cleanup for firmware loading Simplify the code to manage the firmware loading with __free(firmware) and __free(kfree) auto-cleanups for the firmware data and the temporary string or array. Only the code refactoring, no functional changes. Cc: Liam Girdwood Cc: Bard Liao Cc: Daniel Baluta Cc: Pierre-Louis Bossart Cc: Vijendar Mukunda Acked-by: Peter Ujfalusi Tested-by: Peter Ujfalusi Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260806140006.1412298-32-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/sof/fw-file-profile.c | 19 +++++++------------ sound/soc/sof/topology.c | 20 ++++++-------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c index 76bde2e0be1d..fcd57f04ca36 100644 --- a/sound/soc/sof/fw-file-profile.c +++ b/sound/soc/sof/fw-file-profile.c @@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, enum sof_ipc_type *ipc_type_to_adjust) { enum sof_ipc_type fw_ipc_type; - const struct firmware *fw; - const char *fw_filename; const u32 *magic; int ret; - fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, - profile->fw_name); + const char *fw_filename __free(kfree) = + kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, + profile->fw_name); if (!fw_filename) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = firmware_request_nowarn(&fw, fw_filename, dev); if (ret < 0) { dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); - kfree(fw_filename); return ret; } @@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, break; default: dev_err(dev, "Invalid firmware magic: %#x\n", *magic); - ret = -EINVAL; - goto out; + return -EINVAL; } if (ipc_type_to_adjust) { @@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, dev_err(dev, "ipc type mismatch between %s and expected: %d vs %d\n", fw_filename, fw_ipc_type, profile->ipc_type); - ret = -EINVAL; + return -EINVAL; } -out: - release_firmware(fw); - kfree(fw_filename); - return ret; + return 0; } static int sof_test_topology_file(struct device *dev, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6fd69ba11c41..820513bb2577 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2506,13 +2506,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; - const struct firmware *fw; - const char **tplg_files; int tplg_cnt = 0; int ret; int i; - tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); + const char **tplg_files __free(kfree) = + kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); if (!tplg_files) return -ENOMEM; @@ -2538,10 +2537,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) tplg_filename_prefix, &tplg_files, no_fallback); - if (tplg_cnt < 0) { - kfree(tplg_files); + if (tplg_cnt < 0) return tplg_cnt; - } } /* @@ -2552,8 +2549,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (strstr(file, "dummy")) { dev_err(scomp->dev, "Function topology is required, please upgrade sof-firmware\n"); - - kfree(tplg_files); return -EINVAL; } tplg_files[0] = file; @@ -2568,6 +2563,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (tplg_files[0] != file) dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_files[i], scomp->dev); if (ret < 0) { /* @@ -2586,8 +2582,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) else ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - release_firmware(fw); - if (ret < 0) { dev_err(scomp->dev, "tplg %s component load failed %d\n", tplg_files[i], ret); @@ -2606,6 +2600,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) goto out; } dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); + + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, feature_topology, scomp->dev); if (ret < 0) { /* @@ -2630,8 +2626,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) else ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - release_firmware(fw); - if (ret < 0) { dev_err(scomp->dev, "feature tplg %s component load failed %d\n", feature_topologies[i], ret); @@ -2650,8 +2644,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); - kfree(tplg_files); - return ret; } EXPORT_SYMBOL(snd_sof_load_topology); From 564db8b8da8d5293424406114244c24ef947dd1c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 13:10:45 +0700 Subject: [PATCH 695/791] ASoC: dwc: Propagate -EPROBE_DEFER from IRQ lookup platform_get_irq_optional() never returns 0. It returns a positive IRQ number on success or a negative error code on failure. Return -EPROBE_DEFER from platform_get_irq_optional() so the driver is re-probed when the interrupt resource becomes available instead of continuing probe without an IRQ. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806061046.25323-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/dwc/dwc-i2s.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 74dfd39fd604..2e9fb79b1cd8 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) } irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { + if (irq == -EPROBE_DEFER) + return irq; + if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, pdev->name, dev); if (ret < 0) { From 2ce1d17dcbb38db4d44394c822b9d03c65a22931 Mon Sep 17 00:00:00 2001 From: Brian Koebbe Date: Mon, 10 Aug 2026 09:57:00 -0500 Subject: [PATCH 696/791] ALSA: hda/realtek: Fix quiet 3.5mm jacks on Beelink SER6 Both the front headphone jack and the rear line-out jack play back at a barely audible volume on the Beelink SER6 Max (ALC897, PCI subsystem ID 1f66:0202), even with all mixer controls at 0dB. GPIO2 on the codec gates an external headphone/line amplifier that the generic parser never enables. Add a fixup that asserts it. Verified fixed on both jacks. Signed-off-by: Brian Koebbe Link: https://patch.msgid.link/20260810145700.1206010-1-brian@koeb.be Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc662.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index a640292c4f10..eff7c83b9c59 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -325,6 +325,7 @@ enum { ALC897_FIXUP_UNIS_H3C_X500S, ALC897_FIXUP_HEADSET_MIC_PIN3, ALC662_FIXUP_CSL_GPIO, + ALC897_FIXUP_BEELINK_SER6_AMP, }; static const struct hda_fixup alc662_fixups[] = { @@ -782,6 +783,10 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc662_fixup_csl_amp, }, + [ALC897_FIXUP_BEELINK_SER6_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio4, + }, }; static const struct hda_quirk alc662_fixup_tbl[] = { @@ -853,6 +858,7 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1f66, 0x0202, "Beelink SER6 Max 6900", ALC897_FIXUP_BEELINK_SER6_AMP), #if 0 /* Below is a quirk table taken from the old code. From e7da28b820d12927de30abf554c727a319f80359 Mon Sep 17 00:00:00 2001 From: Denis Batishchev Date: Mon, 10 Aug 2026 17:14:41 +0200 Subject: [PATCH 697/791] ALSA: hda/realtek: Enable micmute LED on HP EliteBook 6 G1a p/n: AD3Q9ET#UUG The HP EliteBook 6 G1a (SSID 103c:8e0d) uses a Realtek ALC236 codec. Without a quirk no fixup is selected and the mic-mute LED stays off. It needs the same ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF quirk as the already-supported 14" variant (SSID 103c:8dfb), so add it. Signed-off-by: Denis Batishchev Cc: Link: https://patch.msgid.link/20260810151440.2306217-2-ii343hbka@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 6b36c730ce78..f92c044ea553 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7545,6 +7545,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), From a9760db775efb483e6e4cb571f5bda37532a75a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Aug 2026 15:37:02 +0200 Subject: [PATCH 698/791] ALSA: seq: Use RCU for the port subscriber list Each sequencer port keeps two subscriber groups (c_src and c_dest), each protected by both an rwlock (list_lock) and a rw_semaphore (list_mutex). The rwlock is taken read-side in the event delivery hot path (__deliver_to_subscribers()) for delivering every event to subscribers, while the mutex serializes subscribe/unsubscribe and covers the sleepable delivery and query walks. Subscriptions change rarely but delivery happens constantly, so this is a textbook read-mostly case. Convert the subscriber list traversal to RCU and drop the rwlock entirely while keeping the existing list_mutex for serializing the writers. The atomic delivery path now runs lock-free under rcu_read_lock() instead of contending on the shared rwlock. Along with the conversion to RCU, the subscriber lists are switched from list_head to hlist so that removal can use hlist_del_init_rcu(): it keeps the ->next pointer intact for concurrent readers while clearing ->pprev, which lets the double-deletion guard (added in commit 13d5e5d4725c) keep detecting an already-removed entry via hlist_unhashed(). Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Port lifetime handling (use_lock/closing drain in port_delete()) is orthogonal and unchanged. Note that the conversion to RCU has another merit: it automatically "fixes" the (rather false) lockdep warnings for the doubly read-locks of the same subscriber list, too. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260810133711.42483-2-tiwai@suse.de --- sound/core/seq/seq_clientmgr.c | 25 ++++++++-------- sound/core/seq/seq_ports.c | 52 ++++++++++++++++------------------ sound/core/seq/seq_ports.h | 8 +++--- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 23ec239640c3..77f5020f1873 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -717,10 +717,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, /* lock list */ if (atomic) - read_lock(&grp->list_lock); + rcu_read_lock(); else down_read_nested(&grp->list_mutex, hop); - list_for_each_entry(subs, &grp->list_head, src_list) { + hlist_for_each_entry_rcu(subs, &grp->list_head, src_list, + lockdep_is_held(&grp->list_mutex)) { /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; @@ -741,7 +742,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, memcpy(event, &event_saved, saved_size); } if (atomic) - read_unlock(&grp->list_lock); + rcu_read_unlock(); else up_read(&grp->list_mutex); memcpy(event, &event_saved, saved_size); @@ -1938,7 +1939,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { struct snd_seq_query_subs *subs = arg; struct snd_seq_port_subs_info *group; - struct list_head *p; + struct hlist_node *p; int i; struct snd_seq_client *cptr __free(snd_seq_client) = @@ -1965,15 +1966,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) /* search for the subscriber */ subs->num_subs = group->count; i = 0; - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (i++ == subs->index) { /* found! */ struct snd_seq_subscribers *s; if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) { - s = list_entry(p, struct snd_seq_subscribers, src_list); + s = hlist_entry(p, struct snd_seq_subscribers, src_list); subs->addr = s->info.dest; } else { - s = list_entry(p, struct snd_seq_subscribers, dest_list); + s = hlist_entry(p, struct snd_seq_subscribers, dest_list); subs->addr = s->info.sender; } subs->flags = s->info.flags; @@ -2529,19 +2530,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg) { - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int count = 0; guard(rwsem_read)(&group->list_mutex); - if (list_empty(&group->list_head)) + if (hlist_empty(&group->list_head)) return; snd_iprintf(buffer, msg); - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (is_src) - s = list_entry(p, struct snd_seq_subscribers, src_list); + s = hlist_entry(p, struct snd_seq_subscribers, src_list); else - s = list_entry(p, struct snd_seq_subscribers, dest_list); + s = hlist_entry(p, struct snd_seq_subscribers, dest_list); if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 6612e92d801f..357c72ed0d3b 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -98,10 +98,9 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl /* initialize snd_seq_port_subs_info */ static void port_subs_info_init(struct snd_seq_port_subs_info *grp) { - INIT_LIST_HEAD(&grp->list_head); + INIT_HLIST_HEAD(&grp->list_head); grp->count = 0; grp->exclusive = 0; - rwlock_init(&grp->list_lock); init_rwsem(&grp->list_mutex); grp->open = NULL; grp->close = NULL; @@ -202,12 +201,12 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, bool is_src, bool ack); static inline struct snd_seq_subscribers * -get_subscriber(struct list_head *p, bool is_src) +get_subscriber(struct hlist_node *p, bool is_src) { if (is_src) - return list_entry(p, struct snd_seq_subscribers, src_list); + return hlist_entry(p, struct snd_seq_subscribers, src_list); else - return list_entry(p, struct snd_seq_subscribers, dest_list); + return hlist_entry(p, struct snd_seq_subscribers, dest_list); } /* @@ -219,9 +218,9 @@ static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_port_subs_info *grp, int is_src) { - struct list_head *p, *n; + struct hlist_node *p, *n; - list_for_each_safe(p, n, &grp->list_head) { + hlist_for_each_safe(p, n, &grp->list_head) { struct snd_seq_subscribers *subs; subs = get_subscriber(p, is_src); @@ -238,13 +237,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, * remove the subscriber info */ if (atomic_dec_and_test(&subs->ref_count)) - kfree(subs); + kfree_rcu(subs, rcu); continue; } /* ok we got the connected port */ delete_and_unsubscribe_port(c, aport, subs, !is_src, true); - kfree(subs); + kfree_rcu(subs, rcu); } } @@ -499,20 +498,20 @@ static int check_and_subscribe_port(struct snd_seq_client *client, bool is_src, bool exclusive, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int err; grp = is_src ? &port->c_src : &port->c_dest; guard(rwsem_write)(&grp->list_mutex); if (exclusive) { - if (!list_empty(&grp->list_head)) + if (!hlist_empty(&grp->list_head)) return -EBUSY; } else { if (grp->exclusive) return -EBUSY; /* check whether already exists */ - list_for_each(p, &grp->list_head) { + hlist_for_each(p, &grp->list_head) { s = get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) return -EBUSY; @@ -526,11 +525,10 @@ static int check_and_subscribe_port(struct snd_seq_client *client, } /* add to list */ - guard(write_lock_irq)(&grp->list_lock); if (is_src) - list_add_tail(&subs->src_list, &grp->list_head); + hlist_add_tail_rcu(&subs->src_list, &grp->list_head); else - list_add_tail(&subs->dest_list, &grp->list_head); + hlist_add_tail_rcu(&subs->dest_list, &grp->list_head); grp->exclusive = exclusive; atomic_inc(&subs->ref_count); @@ -544,17 +542,15 @@ static void __delete_and_unsubscribe_port(struct snd_seq_client *client, bool is_src, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *list; + struct hlist_node *list; bool empty; grp = is_src ? &port->c_src : &port->c_dest; list = is_src ? &subs->src_list : &subs->dest_list; - scoped_guard(write_lock_irq, &grp->list_lock) { - empty = list_empty(list); - if (!empty) - list_del_init(list); - grp->exclusive = 0; - } + empty = hlist_unhashed(list); + if (!empty) + hlist_del_init_rcu(list); + grp->exclusive = 0; if (!empty) unsubscribe_port(client, port, grp, &subs->info, ack); @@ -590,8 +586,8 @@ int snd_seq_port_connect(struct snd_seq_client *connector, subs->info = *info; atomic_set(&subs->ref_count, 0); - INIT_LIST_HEAD(&subs->src_list); - INIT_LIST_HEAD(&subs->dest_list); + INIT_HLIST_NODE(&subs->src_list); + INIT_HLIST_NODE(&subs->dest_list); exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); @@ -612,7 +608,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector, delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); error: - kfree(subs); + kfree_rcu(subs, rcu); return err; } @@ -633,7 +629,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, */ scoped_guard(rwsem_write, &dest->list_mutex) { /* look for the connection */ - list_for_each_entry(subs, &dest->list_head, dest_list) { + hlist_for_each_entry(subs, &dest->list_head, dest_list) { if (match_subs_info(info, &subs->info)) { __delete_and_unsubscribe_port(dest_client, dest_port, subs, false, @@ -648,7 +644,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); - kfree(subs); + kfree_rcu(subs, rcu); return 0; } @@ -662,7 +658,7 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp, int err = -ENOENT; guard(rwsem_read)(&src_grp->list_mutex); - list_for_each_entry(s, &src_grp->list_head, src_list) { + hlist_for_each_entry(s, &src_grp->list_head, src_list) { if (addr_match(dest_addr, &s->info.dest)) { *subs = s->info; err = 0; diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h index b689c0f4867c..12ad86bf1489 100644 --- a/sound/core/seq/seq_ports.h +++ b/sound/core/seq/seq_ports.h @@ -28,17 +28,17 @@ struct snd_seq_subscribers { struct snd_seq_port_subscribe info; /* additional info */ - struct list_head src_list; /* link of sources */ - struct list_head dest_list; /* link of destinations */ + struct hlist_node src_list; /* link of sources */ + struct hlist_node dest_list; /* link of destinations */ atomic_t ref_count; + struct rcu_head rcu; /* for deferred free */ }; struct snd_seq_port_subs_info { - struct list_head list_head; /* list of subscribed ports */ + struct hlist_head list_head; /* list of subscribed ports */ unsigned int count; /* count of subscribers */ unsigned int exclusive: 1; /* exclusive mode */ struct rw_semaphore list_mutex; - rwlock_t list_lock; int (*open)(void *private_data, struct snd_seq_port_subscribe *info); int (*close)(void *private_data, struct snd_seq_port_subscribe *info); }; From 4c252fbc06d641d631ad55111a1bffc50c07f72c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Aug 2026 15:37:03 +0200 Subject: [PATCH 699/791] ALSA: seq: Use RCU for the client port list Each sequencer client keeps a list of its ports (ports_list_head) protected by both an rwlock (ports_lock) and a mutex (ports_mutex). The rwlock is taken read-side on the event delivery hot path: snd_seq_port_use_ptr() walks the list to resolve a port on every dispatched event, while the mutex serializes port creation/deletion. Ports change rarely but delivery happens constantly, so this is the another read-mostly case as the port subscriber list. Convert the port list traversal to RCU and drop the rwlock entirely; the existing ports_mutex keeps serializing the writers. The atomic delivery path (snd_seq_port_use_ptr(), snd_seq_port_query_nearest()) now runs lock-free under rcu_read_lock() instead of contending on the shared rwlock. The writers switch to list_add_tail_rcu()/list_del_rcu(). snd_seq_insert_port() now stores the port number and name before publishing the node so RCU readers only ever observe a fully initialized port. One drawback is that snd_seq_delete_all_ports() drops the O(1) splice trick and unlinks each port individually, though: the splice repointed the last port's ->next away from the list head, which would send a concurrent lockless reader off the end of the list. Unlike the subscriber objects, ports are not freed via kfree_rcu(): port_delete() must drain outstanding use_lock references (and run private_free()) synchronously. The rwlock previously guaranteed that no reader could take a new use_lock reference once the port was unlinked -- list_del under write_lock excluded snd_use_lock_use() under read_lock. list_del_rcu() offers no such exclusion, so a reader still traversing the list can grab a reference after the unlink. port_delete() therefore calls synchronize_rcu() after the port has been unlinked and before snd_use_lock_sync(): once the grace period elapses no new reference can appear, and the existing drain then frees the port safely. Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260810133711.42483-3-tiwai@suse.de --- sound/core/seq/seq_clientmgr.c | 1 - sound/core/seq/seq_clientmgr.h | 1 - sound/core/seq/seq_ports.c | 51 +++++++++++++++------------------- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 77f5020f1873..b7cf14e3ddb3 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -212,7 +212,6 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) } client->type = NO_CLIENT; snd_use_lock_init(&client->use_lock); - rwlock_init(&client->ports_lock); mutex_init(&client->ports_mutex); INIT_LIST_HEAD(&client->ports_list_head); mutex_init(&client->ioctl_mutex); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index feea8bb7d987..d7ffc5c1ed61 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -49,7 +49,6 @@ struct snd_seq_client { /* ports */ int num_ports; /* number of ports */ struct list_head ports_list_head; - rwlock_t ports_lock; struct mutex ports_mutex; struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 357c72ed0d3b..eb67eb0eeb14 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -48,8 +48,8 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client, if (client == NULL) return NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if (port->addr.port == num) { if (port->closing) break; /* deleting now */ @@ -71,8 +71,8 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl num = pinfo->addr.port; found = NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) && !check_inactive) continue; /* skip inactive ports */ @@ -153,7 +153,6 @@ int snd_seq_insert_port(struct snd_seq_client *client, int port, num = max(port, 0); guard(mutex)(&client->ports_mutex); - guard(write_lock_irq)(&client->ports_lock); struct list_head *insert_before = &client->ports_list_head; list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) @@ -165,12 +164,13 @@ int snd_seq_insert_port(struct snd_seq_client *client, int port, if (port < 0) /* auto-probe mode */ num = p->addr.port + 1; } - /* insert the new port */ - list_add_tail(&new_port->list, insert_before); - client->num_ports++; + /* finish initializing the port before publishing it to RCU readers */ new_port->addr.port = num; /* store the port number in the port */ if (!new_port->name[0]) sprintf(new_port->name, "port-%d", num); + /* insert the new port */ + list_add_tail_rcu(&new_port->list, insert_before); + client->num_ports++; return num; } @@ -253,7 +253,13 @@ static int port_delete(struct snd_seq_client *client, { /* set closing flag and wait for all port access are gone */ port->closing = 1; - snd_use_lock_sync(&port->use_lock); + /* the port has already been unlinked from the client's port list; + * wait for a grace period so that RCU readers still traversing the + * list can no longer take a new use_lock reference, then drain the + * outstanding references before freeing + */ + synchronize_rcu(); + snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, true); @@ -276,11 +282,10 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) struct snd_seq_client_port *found = NULL, *p; scoped_guard(mutex, &client->ports_mutex) { - guard(write_lock_irq)(&client->ports_lock); list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) { /* ok found. delete from the list at first */ - list_del(&p->list); + list_del_rcu(&p->list); client->num_ports--; found = p; break; @@ -296,26 +301,16 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) /* delete the all ports belonging to the given client */ int snd_seq_delete_all_ports(struct snd_seq_client *client) { - struct list_head deleted_list; struct snd_seq_client_port *port, *tmp; - - /* move the port list to deleted_list, and - * clear the port list in the client data. + + /* unlink and delete each port; port_delete() waits for an RCU grace + * period before draining the port, so concurrent lockless readers can + * no longer take a new use_lock reference on it */ guard(mutex)(&client->ports_mutex); - scoped_guard(write_lock_irq, &client->ports_lock) { - if (!list_empty(&client->ports_list_head)) { - list_add(&deleted_list, &client->ports_list_head); - list_del_init(&client->ports_list_head); - } else { - INIT_LIST_HEAD(&deleted_list); - } - client->num_ports = 0; - } - - /* remove each port in deleted_list */ - list_for_each_entry_safe(port, tmp, &deleted_list, list) { - list_del(&port->list); + list_for_each_entry_safe(port, tmp, &client->ports_list_head, list) { + list_del_rcu(&port->list); + client->num_ports--; snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } From 7a287e4615d623fade44bec48077263c7564abf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Aug 2026 15:37:04 +0200 Subject: [PATCH 700/791] ALSA: seq: Use RCU for the client table The sequencer keeps a global table of clients (clienttab[]) indexed by client id, protected by the global clients_lock spinlock. The lookup snd_seq_client_use_ptr() reads a slot and takes a use_lock reference on the client, and this runs on the event delivery hot path: every dispatched event resolves its destination (and often source) client through it. The spinlock's only job on the read side is to make the "pointer is non-NULL" test and the reference increment indivisible with respect to the writer that nulls the slot and then drains the refcount. Clients come and go rarely but delivery happens constantly, so this is yet another read-mostly case as the port and subscriber lists. Convert the table to RCU: the read side now runs lock-free under rcu_read_lock() and takes the use_lock reference via rcu_dereference(), removing contention on the single global spinlock from the delivery path. The writers keep clients_lock (still needed to serialize slot allocation) and publish / unpublish via rcu_assign_pointer(); creation and destruction remain serialized at a higher level by register_mutex. As with the ports, the client is not freed via kfree_rcu(): its lifetime is governed by the use_lock refcount drained in seq_free_client1(). list_del under the old spinlock excluded a concurrent lookup from taking a new reference once the slot was nulled; rcu_assign_pointer(NULL) offers no such exclusion, so a reader still holding the old pointer can grab a reference after the unpublish. seq_free_client1() therefore calls synchronize_rcu() after nulling the slot and before snd_use_lock_sync(): once the grace period elapses no new reference can appear, and the existing drain then frees the client safely. clienttablock[] keeps its slot-reservation role (create/free are serialized by register_mutex); its read on the lookup path only gates module autoload, so a lockless read is harmless. Dropping the spinlock from the read path is safe: clients_lock is now taken only by the process-context writers, and the sole atomic reader uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260810133711.42483-4-tiwai@suse.de --- sound/core/seq/seq_clientmgr.c | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index b7cf14e3ddb3..d4cac594bc8f 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -59,7 +59,7 @@ static DEFINE_MUTEX(register_mutex); * client table */ static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; -static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static struct snd_seq_client __rcu *clienttab[SNDRV_SEQ_MAX_CLIENTS]; static struct snd_seq_usage client_usage; /* @@ -95,15 +95,23 @@ static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client) return snd_seq_total_cells(client->pool) > 0; } -/* return pointer to client structure for specified id */ -static struct snd_seq_client *clientptr(int clientid) +/* return pointer to client structure for specified id; call under RCU read-lock */ +static struct snd_seq_client *__clientptr(int clientid) { if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n", clientid); return NULL; } - return clienttab[clientid]; + return rcu_dereference_check(clienttab[clientid], + lockdep_is_held(&clients_lock)); +} + +/* return pointer to client structure for specified id */ +static struct snd_seq_client *clientptr(int clientid) +{ + guard(rcu)(); + return __clientptr(clientid); } static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) @@ -115,8 +123,8 @@ static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) clientid); return NULL; } - scoped_guard(spinlock_irqsave, &clients_lock) { - client = clientptr(clientid); + scoped_guard(rcu) { + client = __clientptr(clientid); if (client) return snd_seq_client_ref(client); if (clienttablock[clientid]) @@ -150,8 +158,8 @@ static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) snd_seq_device_load_drivers(); } } - scoped_guard(spinlock_irqsave, &clients_lock) { - client = clientptr(clientid); + scoped_guard(rcu) { + client = __clientptr(clientid); if (client) return snd_seq_client_ref(client); } @@ -223,14 +231,17 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN; c < SNDRV_SEQ_MAX_CLIENTS; c++) { - if (clienttab[c] || clienttablock[c]) + if (rcu_access_pointer(clienttab[c]) || clienttablock[c]) continue; - clienttab[client->number = c] = client; + client->number = c; + rcu_assign_pointer(clienttab[c], client); return client; } } else { - if (clienttab[client_index] == NULL && !clienttablock[client_index]) { - clienttab[client->number = client_index] = client; + if (rcu_access_pointer(clienttab[client_index]) == NULL && + !clienttablock[client_index]) { + client->number = client_index; + rcu_assign_pointer(clienttab[client_index], client); return client; } } @@ -248,10 +259,16 @@ static int seq_free_client1(struct snd_seq_client *client) return 0; scoped_guard(spinlock_irq, &clients_lock) { clienttablock[client->number] = 1; - clienttab[client->number] = NULL; + rcu_assign_pointer(clienttab[client->number], NULL); } snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); + /* the client has been unpublished from the table; wait for a grace + * period so that lockless readers (snd_seq_client_use_ptr()) that + * observed the old pointer can no longer take a new use_lock + * reference, then drain the outstanding references before freeing + */ + synchronize_rcu(); snd_use_lock_sync(&client->use_lock); if (client->pool) snd_seq_pool_delete(&client->pool); From 7ffa5d2462dcf307746a79e959ceac6cd5828bfe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Aug 2026 15:37:05 +0200 Subject: [PATCH 701/791] ALSA: seq: Use RCU for the virmidi file list Each virmidi device keeps a list of its opened input files (filelist) protected by both an rwlock (filelist_lock) and a rw_semaphore (filelist_sem). snd_virmidi_dev_receive_event() walks the list on the sequencer event input path -- read_lock() when the event is delivered in atomic context, down_read() otherwise -- decoding each incoming event into the file's rawmidi buffer. The writers (input open/close) take both locks to add/remove entries. This is another typical dual-lock read-mostly pattern as the port subscriber list: files are opened/closed rarely while the receive callback runs per event. Let's convert the traversal to RCU and drop the rwlock; the existing filelist_sem keeps serializing the writers. The atomic input path now runs lock-free under rcu_read_lock(), and both readers share a single list_for_each_entry_rcu() (valid under the rwsem via lockdep_is_held()). The writers switch to list_add_tail_rcu() / list_del_rcu(). snd_virmidi_input_close() freed the entry (parser and struct) immediately after list_del. A concurrent lockless reader in the atomic path may still be dereferencing it, so the close path now waits for an RCU grace period after list_del_rcu() before freeing; synchronize_rcu() is used rather than kfree_rcu() because the parser must also be released after the grace period, not just the struct. Non-atomic readers are already excluded by the down_write, so only the atomic RCU readers need the grace period. Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260810133711.42483-5-tiwai@suse.de --- include/sound/seq_virmidi.h | 1 - sound/core/seq/seq_virmidi.c | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h index 56a3f38df8c3..359cb363369d 100644 --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -46,7 +46,6 @@ struct snd_virmidi_dev { int client; /* created/attached client */ int port; /* created/attached port */ unsigned int flags; /* SNDRV_VIRMIDI_* */ - rwlock_t filelist_lock; struct rw_semaphore filelist_sem; struct list_head filelist; }; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 982828650d41..6208bf7f57bf 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -78,10 +78,11 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, int len; if (atomic) - read_lock(&rdev->filelist_lock); + rcu_read_lock(); else down_read(&rdev->filelist_sem); - list_for_each_entry(vmidi, &rdev->filelist, list) { + list_for_each_entry_rcu(vmidi, &rdev->filelist, list, + lockdep_is_held(&rdev->filelist_sem)) { if (!READ_ONCE(vmidi->trigger)) continue; if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { @@ -96,7 +97,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, } } if (atomic) - read_unlock(&rdev->filelist_lock); + rcu_read_unlock(); else up_read(&rdev->filelist_sem); @@ -200,8 +201,7 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) vmidi->port = rdev->port; runtime->private_data = vmidi; scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_add_tail(&vmidi->list, &rdev->filelist); + list_add_tail_rcu(&vmidi->list, &rdev->filelist); } vmidi->rdev = rdev; return 0; @@ -243,9 +243,13 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream) struct snd_virmidi *vmidi = substream->runtime->private_data; scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_del(&vmidi->list); + list_del_rcu(&vmidi->list); } + /* wait for a grace period so that lockless readers in the atomic + * delivery path (snd_virmidi_dev_receive_event()) are no longer + * traversing this entry before its parser and memory are freed + */ + synchronize_rcu(); snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; kfree(vmidi); @@ -508,7 +512,6 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi rdev->device = device; rdev->client = -1; init_rwsem(&rdev->filelist_sem); - rwlock_init(&rdev->filelist_lock); INIT_LIST_HEAD(&rdev->filelist); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; rmidi->private_data = rdev; From 0252ad169c5eb8d9cfd2ee646a7cd055865e1f12 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Aug 2026 15:37:06 +0200 Subject: [PATCH 702/791] ALSA: seq: Use RCU for the UMP client output substream The UMP sequencer client protects its output rawmidi file (out_rfile) with an rwlock (output_lock). seq_ump_process_event(), the port's event_input callback, reads out_rfile.output under read_lock on every delivered UMP event, while the open/close paths (serialized by ump->open_mutex) publish and clear out_rfile under write_lock. Output is opened/closed only on the subscribe/use lifecycle while delivery happens per event, so this is another read-mostly hot path. Convert it to RCU and drop the rwlock. out_rfile is an embedded struct rather than a pointer, so instead of restructuring it, add an RCU-protected shadow of the substream (out_substream) for the reader; out_rfile itself becomes writer-only state accessed solely under open_mutex. The reader now runs lock-free under rcu_read_lock() via rcu_dereference(), and open publishes the substream with rcu_assign_pointer(). On close the substream is cleared with rcu_assign_pointer(NULL) and the rawmidi is released only after synchronize_rcu(), so no reader in the delivery path can still be writing to the substream when snd_rawmidi_kernel_release() runs. Dropping write_lock_irqsave() from the writers is safe: they run in process context under open_mutex, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260810133711.42483-6-tiwai@suse.de --- sound/core/seq/seq_ump_client.c | 38 +++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index ccd93599b493..4c1e81db376f 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -37,8 +37,11 @@ struct seq_ump_client { struct snd_ump_endpoint *ump; /* assigned endpoint */ int seq_client; /* sequencer client id */ int opened[2]; /* current opens for each direction */ - rwlock_t output_lock; /* protects out_rfile output access */ struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + /* RCU-protected shadow of out_rfile.output for the delivery hot path; + * out_rfile itself is only touched by open/close under open_mutex + */ + struct snd_rawmidi_substream __rcu *out_substream; struct seq_ump_input_buffer input; /* input parser context */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ struct work_struct group_notify_work; /* FB change notification */ @@ -89,8 +92,8 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, unsigned char type; int len; - guard(read_lock_irqsave)(&client->output_lock); - substream = client->out_rfile.output; + guard(rcu)(); + substream = rcu_dereference(client->out_substream); if (!substream) return -ENODEV; if (!snd_seq_ev_is_ump(ev)) @@ -108,19 +111,22 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, static int seq_ump_client_open(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - struct snd_rawmidi_file rfile = {}; int err; guard(mutex)(&ump->open_mutex); if (dir == STR_OUT && !client->opened[dir]) { + /* out_rfile is only accessed under open_mutex; the delivery + * path reads out_substream via RCU, so open into out_rfile + * directly and publish the substream afterwards + */ err = snd_rawmidi_kernel_open(&ump->core, 0, SNDRV_RAWMIDI_LFLG_OUTPUT | SNDRV_RAWMIDI_LFLG_APPEND, - &rfile); + &client->out_rfile); if (err < 0) return err; - scoped_guard(write_lock_irqsave, &client->output_lock) - client->out_rfile = rfile; + rcu_assign_pointer(client->out_substream, + client->out_rfile.output); } client->opened[dir]++; return 0; @@ -130,17 +136,18 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) static int seq_ump_client_close(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - struct snd_rawmidi_file rfile = {}; guard(mutex)(&ump->open_mutex); if (!--client->opened[dir]) { - if (dir == STR_OUT) { - scoped_guard(write_lock_irqsave, &client->output_lock) { - rfile = client->out_rfile; - client->out_rfile = (struct snd_rawmidi_file){}; - } - if (rfile.rmidi) - snd_rawmidi_kernel_release(&rfile); + if (dir == STR_OUT && client->out_rfile.rmidi) { + rcu_assign_pointer(client->out_substream, NULL); + /* wait for a grace period so that no reader in the + * delivery path is still writing to the substream + * before it is released + */ + synchronize_rcu(); + snd_rawmidi_kernel_release(&client->out_rfile); + client->out_rfile = (struct snd_rawmidi_file){}; } } return 0; @@ -480,7 +487,6 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) INIT_WORK(&client->group_notify_work, handle_group_notify); client->ump = ump; - rwlock_init(&client->output_lock); client->seq_client = snd_seq_create_kernel_client(card, ump->core.device, From 04a8e286bb2e3a0dc37980a8b7da76da339d1a88 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 11 Aug 2026 10:49:00 +0800 Subject: [PATCH 703/791] ALSA: hda/realtek: Add quirk for Acer Predator PH16-71 The Acer Predator PH16-71 (subsystem 0x1025:0x166c) with Realtek ALC245 codec has a non-functional headset microphone. Adding the ALC2XX_FIXUP_HEADSET_MIC quirk resolves the issue. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221641 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260811024902.134457-2-zhangheng@kylinos.cn --- 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 f92c044ea553..c86983fea13b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7053,6 +7053,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x166c, "Acer Predator PH16-71", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), From 9976513eb6422e7c3a1f35df49ad4ba2e1b9ac8b Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 11 Aug 2026 10:49:01 +0800 Subject: [PATCH 704/791] ALSA: hda/realtek: Add quirk for Lenovo Legion Pro 5 16ADR10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo Legion Pro 5 16ADR10 (codec SSID 0x17aa:0x3926) suffers from distorted/crackling speaker output, as only one speaker pin is driven without proper COEF/amp initialization. Add HDA_CODEC_QUIRK applying ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN to enable both speaker pins and proper amp initialization, restoring clean audio output at all volume levels. Tested: Both internal speaker pairs are now driven correctly and distortion is gone; headphone output remains unaffected. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221595 Signed-off-by: Zhang Heng Tested-by: Efe Yılmaz Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260811024902.134457-3-zhangheng@kylinos.cn --- 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 c86983fea13b..236ae507bca6 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8064,6 +8064,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x17aa, 0x3926, "Legion Pro 5 16ADR10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), /* Legion R9000P ADR10 shares PCI SSID 17aa:38bb with Yoga S780-14.5 Air AMD quad AAC; * use codec SSID to distinguish them */ From 9508f9f122d4af799cf4a422eb31eedd888b76d2 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 11 Aug 2026 10:49:02 +0800 Subject: [PATCH 705/791] ALSA: usb-audio: Fix popping noise on Valeton GP-200 The Valeton GP-200 guitar multi-effects processor exhibits a continuous popping noise (~6Hz) during playback and recording. Force implicit feedback to resolve the issue. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221662 Signed-off-by: Zhang Heng Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260811024902.134457-4-zhangheng@kylinos.cn --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d00d6a543a9f..eb0d012cf856 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2526,6 +2526,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x84ef, 0x002a, /* Valeton GP-200 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x84ef, 0x0082, /* Hotone Audio Pulze Mini */ QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL), From a9ac75b664d917220dfe2e3c1005402d7a18e84b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 10 Aug 2026 21:21:22 -0700 Subject: [PATCH 706/791] ALSA: pci: asihpi: use pcim_iomap for managed PCI memory mapping Replace manual ioremap() calls with pcim_iomap() which uses devres for automatic cleanup. This eliminates the need for manual iounmap() in both the error path of asihpi_adapter_probe() and the asihpi_adapter_remove() function. The pcim_iomap() helper is cleaner and less error-prone since it handles unmapping automatically when the PCI device is released. Assisted-by: opencode/big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260811042122.44923-1-rosenp@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpioctl.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 9de9ae7032b8..ec2da792e1c8 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -385,8 +385,7 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { memlen = pci_resource_len(pci_dev, idx); pci.ap_mem_base[idx] = - ioremap(pci_resource_start(pci_dev, idx), - memlen); + pcim_iomap(pci_dev, idx, memlen); if (!pci.ap_mem_base[idx]) { HPI_DEBUG_LOG(ERROR, "ioremap failed, aborting\n"); @@ -509,13 +508,6 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, return 0; err: - while (--idx >= 0) { - if (pci.ap_mem_base[idx]) { - iounmap(pci.ap_mem_base[idx]); - pci.ap_mem_base[idx] = NULL; - } - } - if (adapter.p_buffer) { adapter.buffer_size = 0; vfree(adapter.p_buffer); @@ -527,14 +519,11 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, void asihpi_adapter_remove(struct pci_dev *pci_dev) { - int idx; struct hpi_message hm; struct hpi_response hr; struct hpi_adapter *pa; - struct hpi_pci pci; pa = pci_get_drvdata(pci_dev); - pci = pa->adapter->pci; /* Disable IRQ generation on DSP side */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, @@ -550,10 +539,6 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) hm.adapter_index = pa->adapter->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - /* unmap PCI memory space, mapped during device init. */ - for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx) - iounmap(pci.ap_mem_base[idx]); - if (pa->irq) free_irq(pa->irq, pa); From 108704eecab9563a457ef6f32c7eef06e7703a5e Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 11 Aug 2026 14:27:34 +0800 Subject: [PATCH 707/791] ALSA: hda/realtek: Rename Line Out control to Headphone on ThinkPad X1 Carbon 6th The ThinkPad X1 Carbon 6th Gen (ALC285, SSID 17aa:225c) has no physical Line Out jack. The 3.5mm headphone jack is wired to the headphone DAC, but the ALSA HDA driver names the corresponding control as "Line Out Playback Volume" (node 0x02). PipeWire's ALSA Card Profile (ACP) silences "Line Out" when headphones are activated, which incorrectly mutes the headphone output. Add a quirk to rename the control to "Headphone Playback Volume" via alc285_lenovo_dac_rename(). Tested on openSUSE Tumbleweed (kernel 7.1.5): - Control renamed successfully, no name collision with "Headphone Playback Switch" - Headphone output works across multiple PipeWire/WirePlumber restarts and port switches Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221522 Signed-off-by: Zhang Heng Tested-by: Branislav Klocok Link: https://patch.msgid.link/20260811062734.400512-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 236ae507bca6..87d59a9dc55f 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -2572,6 +2572,13 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec, } } +static void alc285_lenovo_dac_rename(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_BUILD) + rename_ctl(codec, "Line Out Playback Volume", + "Headphone Playback Volume"); +} static void alc290_fixup_mono_speakers(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4289,6 +4296,7 @@ enum { ALC287_FIXUP_AW88399_I2C_2, ALC287_FIXUP_LENOVO_LEGION_AW88399, ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6998,6 +7006,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, }, + [ALC285_LENOVO_DAC_RENAME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_lenovo_dac_rename, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7958,6 +7970,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x225c, "Lenovo ThinkPad X1 Carbon 6th Gen", ALC285_LENOVO_DAC_RENAME), SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2288, "Thinkpad X390", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), From 6f14f6a24f110f665023211bbfb6abc2ce12c948 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Tue, 11 Aug 2026 20:14:46 +0800 Subject: [PATCH 708/791] ASoC: tas2781: Fix compiling warning for tasdevice_set_capture_profile_id() Correct the mismatched function description, parameter names and return value documentation in the comment block. No functional code change, only comment and documentation update. Fixes: 431c15610d01 ("ASoC: tas2781: add capture_profile_id field and update the tuning_switch function") Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260811121446.1805-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 70229e8279a3..c029345b4644 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1001,18 +1001,25 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, } /** - * tasdevice_get_capture_profile_id - Report current active capture profile - * ID to user space - * @kcontrol: ALSA kcontrol structure passed from ALSA core - * @ucontrol: User-space control element value buffer to write the result back + * tasdevice_set_capture_profile_id - Set runtime capture profile index via + * ALSA control + * @kcontrol: ALSA kcontrol handle that triggers this operation + * @ucontrol: User space control value carrying the new profile index * - * This function ensures the returned profile ID is always clamped inside the - * valid range advertised by the info callback, preventing accidental invalid - * values from being exposed to applications even if internal driver state is - * temporarily inconsistent. + * This mixer control handler validates the user-provided capture profile ID + * against the maximum valid index parsed from the loaded DSP firmware, + * then updates the runtime stored capture profile ID only if the new value + * differs from the current active one. It will immediately return -EINVAL + * if the submitted profile ID falls outside the valid range, including the + * edge case that no valid configuration blocks are detected in firmware. * - * Returns 0 on successful fill of the control value, no error conditions - * are defined for this getter callback. + * No actual DSP register write is performed in this handler. The updated + * profile ID will be applied to the hardware when the next ALSA capture + * stream starts up. Caller does not need to take extra codec lock here, + * as the ALSA control core already guarantees serialized execution. + * + * Return: 1 if profile ID value was changed, 0 if no modification needed, + * -EINVAL if the input profile ID is out of valid range */ static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) From ce76c44c34ad1d4e0b1671ba376db814afbb3e83 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:59:37 +0700 Subject: [PATCH 709/791] ASoC: Intel: KMB: Propagate -EPROBE_DEFER from IRQ lookup Return -EPROBE_DEFER from platform_get_irq_optional() so the driver is re-probed when the interrupt resource becomes available instead of continuing probe without an IRQ. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806055937.24600-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/keembay/kmb_platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index 6659e8060ef3..48f132f01878 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) if (kmb_i2s->use_pio) { irq = platform_get_irq_optional(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; if (irq > 0) { ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, pdev->name, kmb_i2s); From 9e0698b77684c38f12eb86f828e8a49cc5624304 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 5 Aug 2026 11:45:56 +0700 Subject: [PATCH 710/791] ASoC: ti: omap-twl4030: Check for missing card name after parsing Return any error from snd_soc_of_parse_card_name() directly. If the helper returns successfully but card->name remains unset, report the missing card name explicitly before returning -ENODEV. Suggested-by: Andreas Kemnade Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260805044556.38183-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-twl4030.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 8a3a792a21c6..a3ab52b2b64e 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -216,7 +216,11 @@ static int omap_twl4030_probe(struct platform_device *pdev) if (priv == NULL) return -ENOMEM; - if (snd_soc_of_parse_card_name(card, "ti,model")) { + ret = snd_soc_of_parse_card_name(card, "ti,model"); + if (ret) + return ret; + + if (!card->name) { dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } From daa7ffd765ae67a83e77dae32c66ce2d6d995d19 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sun, 26 Jul 2026 23:12:26 +0200 Subject: [PATCH 711/791] ASoC: qcom: q6apm: keep the graph start count in sync with the DSP q6apm_graph_start() increments start_count even when APM_CMD_GRAPH_START fails, leaving the graph counted as running while the DSP never started it. A later start - a retried prepare, or a resume after a failed start - then finds a non-zero count, skips the command and returns success with no data flowing. Count the graph only once the DSP has accepted the start. The count then stays at zero for a graph that never started, so also stop decrementing below zero in q6apm_graph_stop(): the compressed free path stops unconditionally, and a negative count would make the next start skip the command in the same way. Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Assisted-by: Claude:claude-opus-5 Signed-off-by: Jorijn van der Graaf Link: https://patch.msgid.link/20260726211226.94059-1-jorijnvdgraaf@catcrafts.net Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 641d6d243229..f167b9dae3fa 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -802,14 +802,17 @@ EXPORT_SYMBOL_GPL(q6apm_graph_prepare); int q6apm_graph_start(struct q6apm_graph *graph) { struct audioreach_graph *ar_graph = graph->ar_graph; - int ret = 0; + int ret; - if (ar_graph->start_count == 0) + if (ar_graph->start_count == 0) { ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START); + if (ret) + return ret; + } ar_graph->start_count++; - return ret; + return 0; } EXPORT_SYMBOL_GPL(q6apm_graph_start); @@ -817,6 +820,9 @@ int q6apm_graph_stop(struct q6apm_graph *graph) { struct audioreach_graph *ar_graph = graph->ar_graph; + if (ar_graph->start_count == 0) + return 0; + if (--ar_graph->start_count > 0) return 0; From 59e1592d3c270ff4642d5d6dc55c545306eb0693 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 11 Aug 2026 22:18:35 +0900 Subject: [PATCH 712/791] ALSA: seq: Don't leak the extension cell pointer in the bounce payload The bounce_error_event() embeds the failed event in the bounce payload by pointing data.ext.ptr at it. When that event is a queued variable-length event, its own data.ext.ptr holds the address of its first extension cell, put there by snd_seq_event_dup(). The payload goes out verbatim through snd_seq_expand_var_event(), so the address reaches userspace. That is the same address commit 705dd6dcbc0e ("ALSA: seq: Clear variable event pointer on read") removed from the event header. The read path still clears it there, just above the call that expands the payload. Embed a sanitised copy instead, treated exactly as snd_seq_read() treats the header. A stack copy is enough because delivery is synchronous and snd_seq_event_dup() copies before returning. An unprivileged client reaches this by setting SNDRV_SEQ_FILTER_BOUNCE, queueing a variable-length event to a port that does not exist and reading the bounce back. Eight bytes on 64-bit, from its own pool. Fixes: efc86691e4d8 ("ALSA: seq: Fix kernel heap address leak in bounce_error_event()") Assisted-by: Claude:claude-opus-5 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260811131835.3837024-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_clientmgr.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index d4cac594bc8f..11fa7e825819 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -541,7 +541,7 @@ static int bounce_error_event(struct snd_seq_client *client, struct snd_seq_event *event, int err, int atomic, int hop) { - struct snd_seq_event bounce_ev; + struct snd_seq_event bounce_ev, quoted; int result; if (client == NULL || @@ -561,15 +561,19 @@ static int bounce_error_event(struct snd_seq_client *client, * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the * original event embedded as variable-length data. This * avoids exposing data.quote.event (a kernel pointer) to - * userspace. The variable-length path in snd_seq_event_dup() - * copies the event data from data.ext.ptr into chained cells, - * and snd_seq_expand_var_event() copies only the data content - * -- never the pointer -- to userspace. + * userspace. Sanitise the embedded copy too - a queued + * variable-length event carries the address of its own + * extension cell, and the payload goes out verbatim. */ + quoted = *event; + if (snd_seq_ev_is_variable("ed)) { + quoted.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + quoted.data.ext.ptr = NULL; + } bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; bounce_ev.data.ext.len = sizeof(struct snd_seq_event); - bounce_ev.data.ext.ptr = (char *)event; + bounce_ev.data.ext.ptr = (char *)"ed; } else { /* * For kernel clients, quote the event pointer directly. From 6ec64d757af9b75a3c64f9f7dad76bdc1efc06ca Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Tue, 11 Aug 2026 17:09:49 +0700 Subject: [PATCH 713/791] ASoC: pxa: Use devm_clk_get_optional() for extclk clock The Device Tree binding defines the extclk clock as an optional property, but the driver currently uses devm_clk_get() and manually handles the absence of the clock. Use devm_clk_get_optional() to match the binding and simplify the optional clock handling. This also propagates errors other than the absence of the optional clock, including -EPROBE_DEFER. This changes the existing behavior for errors other than -EPROBE_DEFER. RFC to discuss whether these errors should cause probe to fail rather than being treated as an unavailable optional clock. Fixes: 90eb6b59d311 ("ASoC: pxa-ssp: add support for an external clock in devicetree") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260811100949.61142-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/pxa/pxa-ssp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index f8054c1c59fa..3a0abcb0bfbd 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) goto err_priv; } - priv->extclk = devm_clk_get(dev, "extclk"); + priv->extclk = devm_clk_get_optional(dev, "extclk"); if (IS_ERR(priv->extclk)) { ret = PTR_ERR(priv->extclk); - if (ret == -EPROBE_DEFER) - goto err_priv; - - priv->extclk = NULL; + goto err_priv; } } else { priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); From 0286324da660875dc504fd65b3be87e9c8b9a547 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Wed, 5 Aug 2026 15:55:43 +0800 Subject: [PATCH 714/791] ASoC: fsl-asoc-card: defer probe when the CPU DAI device is not ready fsl_asoc_card_probe() hard-fails with -EINVAL when the CPU DAI (SAI) platform device is not found. Like the codec, the CPU DAI may just be probed later than the machine driver; the order is not guaranteed and varies across kernel versions, so a permanent -EINVAL leaves the card unregistered with no analog playback or capture. Defer probe instead, mirroring commit e396dec46c56 ("ASoC: fsl-asoc-card: Defer probe when fail to find codec device"). Tested on i.MX8MP with an ALC5672 on SAI3: the card that failed to register on v6.18 now comes up during boot. Fixes: 708b4351f08c ("ASoC: fsl: Add Freescale Generic ASoC Sound Card with ASRC support") Signed-off-by: LiangCheng Wang Link: https://patch.msgid.link/20260805-fsl-asoc-defer-cpu-dai-v1-1-43f7f538e384@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 709543308fe9..eff46666c8ad 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -728,8 +728,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) cpu_pdev = of_find_device_by_node(cpu_np); if (!cpu_pdev) { - dev_err(&pdev->dev, "failed to find CPU DAI device\n"); - ret = -EINVAL; + ret = dev_err_probe(&pdev->dev, -EPROBE_DEFER, + "failed to find CPU DAI device\n"); goto fail; } From 4d788788e36652cc965853d9f84f11759a259fb4 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 11 Aug 2026 14:16:04 +0530 Subject: [PATCH 715/791] ASoC: dt-bindings: qcom,sm8250: Add Maili sound card Add the Maili sound card compatible to the SM8450-family sound card bindings. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260811084605.1820056-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 3e7216d61a5e..1536fcd96d68 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -26,6 +26,7 @@ properties: - qcom,eliza-sndcard - qcom,hawi-sndcard - qcom,kaanapali-sndcard + - qcom,maili-sndcard - qcom,sm8475-sndcard - qcom,sm8550-sndcard - qcom,sm8650-sndcard From 19b7493a71625bcc8f29e1b858e99c7e42790156 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 11 Aug 2026 14:16:05 +0530 Subject: [PATCH 716/791] ASoC: qcom: sc8280xp: Add Maili sound card support Add the Maili sound card compatible. Maili can reuse the Hawi sound card data. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20260811084605.1820056-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 913ae81be424..1322601659d4 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -554,6 +554,7 @@ static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, From 8393e0bf593be4fe51ee8d8e3aff53d076e02baf Mon Sep 17 00:00:00 2001 From: Ville Saarinen Date: Sun, 9 Aug 2026 10:15:04 +0000 Subject: [PATCH 717/791] ASoC: amd: acp-config: force SoundWire probe on HP OmniBook X Flip 14 The BIOS on the HP OmniBook X Flip 14-kc0xxx (board 8EA1, Strix Point, ACP 7.2) reports acp-audio-config-flag = FLAG_AMD_LEGACY_ONLY_DMIC. That binds the legacy ACP driver and registers a PDM-only card, so the SoundWire links are never scanned: the two TAS2783 speaker amplifiers on link 0 and the RT712-VB jack codec on link 1 do not enumerate and the machine ends up with no usable playback path at all. Add a DMI entry for the board so the flag is overridden to 0 and snd_pci_ps probes instead. Developed with AI assistance. The assistant read the board's ACP configuration flag out of the running system, identified the flag override as the fix and drafted the DMI entry. All hardware measurements quoted above were run by the submitter on the affected machine. The submitter has reviewed the change, understands it and takes responsibility for it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Ville Saarinen Link: https://patch.msgid.link/20260809101439.4798-2-wiza@saarinenkoti.fi Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 88e4230d66c3..4ce8573d4ca0 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), }, }, + { + /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8EA1"), + }, + }, {} }; From b48466e4be18822d58a62ae619ffb062220ad572 Mon Sep 17 00:00:00 2001 From: Ville Saarinen Date: Sun, 9 Aug 2026 10:15:09 +0000 Subject: [PATCH 718/791] ASoC: amd: acp70: add HP OmniBook X Flip 14 SoundWire machine Describe the SoundWire topology of the HP OmniBook X Flip 14-kc0xxx (board 8EA1): two TAS2783 smart amplifiers aggregated on link 0 (unique IDs 0xC and 0x9, group_position 0 and 1, name prefixes tas2783-1 and tas2783-2) driving the left and right internal speakers, and an RT712-VB on link 1 providing the headset jack on AIF1 and the internal DMIC array on AIF3. The RT712 amplifier path (AIF2) is left unused because the speakers are driven by the external TAS2783 pair, so the existing jack_dmic_endpoints array describes it exactly. The entry is gated on snd_soc_acpi_amd_sdca_is_device_rt712_vb() so it does not capture a board carrying a different link 1 codec. Developed with AI assistance. The assistant derived the link topology from the enumerated peripherals and drafted the table entry. The order of the two amplifier entries, which is what assigns the physical sides, was corrected after a listening test by the submitter. All hardware measurements quoted above were run by the submitter on the affected machine. The submitter has reviewed the change, understands it and takes responsibility for it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Ville Saarinen Link: https://patch.msgid.link/20260809101439.4798-3-wiza@saarinenkoti.fi Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index ccd01152c87d..815e088b437d 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { {} }; +static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { + { + .adr = 0x000130025D071201ull, + /* + * On this platform speakers are provided by two TAS2783 amps, + * so the AIF2 amp path is left unused: jack + DMIC only. + */ + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt712" + } +}; + +/* + * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the + * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() + * appends them to the dailink in array order and sdw_compute_slave_ports() + * hands out payload block offsets in that same order, so the first entry is the + * one that receives channel 0. + */ +static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { + { + .adr = 0x00003C0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-1", + }, + { + .adr = 0x0000390102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-2", + }, +}; + +/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ +static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), + .adr_d = tas2783x2_l0u9c_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), + .adr_d = rt712_vb_l1u0_adr, + }, + {} +}; + static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { { /* Jack Playback/Capture Endpoint (AIF1) */ .num = 0, @@ -704,6 +754,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_tas2783_2, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, + .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(0) | BIT(1), .links = acp70_rt1320_l0_rt722_l1, From b992511180e126150c6ad3580a6fd568c385f4c6 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 11 Aug 2026 11:51:40 -0700 Subject: [PATCH 719/791] ASoC: xilinx: formatter_pcm: fix stream_data leak on open error In xlnx_formatter_pcm_open(), stream_data is allocated and adata->play_stream or adata->capture_stream is assigned early. If a later step, such as snd_pcm_hw_constraint_step() or snd_pcm_hw_constraint_integer(), fails, the function returns the error immediately. ALSA does not call the close callback when open fails, so stream_data is leaked and the stream pointer is left dangling, pointing to a substream that ALSA frees. A later interrupt would then call snd_pcm_period_elapsed() on the freed substream. Free stream_data and clear the stream pointer on the error paths. Fixes: 6f6c3c36f091 ("ASoC: xlnx: add pcm formatter platform driver") Assisted-by: opencode:deepseek-v4-flash-free Signed-off-by: Rosen Penev Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260811185140.27149-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 55b5b473d85f..fce9c0a02ea3 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -384,7 +384,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err) { dev_err(component->dev, "Unable to set constraint on period bytes\n"); - return err; + goto error; } /* Resize the buffer bytes as divisible by 64 */ @@ -394,7 +394,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err) { dev_err(component->dev, "Unable to set constraint on buffer bytes\n"); - return err; + goto error; } /* Set periods as integer multiple */ @@ -403,7 +403,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, if (err < 0) { dev_err(component->dev, "Unable to set constraint on periods to be integer\n"); - return err; + goto error; } /* enable DMA IOC irq */ @@ -412,6 +412,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, writel(val, stream_data->mmio + XLNX_AUD_CTRL); return 0; + +error: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = NULL; + else + adata->capture_stream = NULL; + kfree(stream_data); + return err; } static int xlnx_formatter_pcm_close(struct snd_soc_component *component, From 21e958c4fd92d63139039430c246613505480689 Mon Sep 17 00:00:00 2001 From: Trevor Vorhees Date: Tue, 11 Aug 2026 20:44:10 -0400 Subject: [PATCH 720/791] ALSA: usb-audio: Fix sample rates for PreSonus AudioBox USB The fixed audio formats for the PreSonus AudioBox USB specify a discrete rate mask but leave nr_rates at zero and rate_table unset. find_format() therefore rejects every requested rate, preventing the playback and capture streams from being opened. Add the advertised 44100 and 48000 Hz rates to both streams and report their 24 significant bits. Fixes: 34fe4a9df247 ("ALSA: usb-audio: Add quirk for PreSonus AudioBox USB") Cc: stable@vger.kernel.org Signed-off-by: Trevor Vorhees Link: https://patch.msgid.link/20260811-audiobox-usb-fix-v1-1-13c8b7f071ea@proton.me Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 938908671d93..0a3d39b8385b 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2692,6 +2692,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), { QUIRK_DATA_AUDIOFORMAT(2) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 2, .altsetting = 1, @@ -2703,11 +2704,16 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, { QUIRK_DATA_AUDIOFORMAT(3) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 3, .altsetting = 1, @@ -2719,6 +2725,10 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, QUIRK_COMPOSITE_END From 67300656f6a690c0146b2bb375f3d30eb05a7bea Mon Sep 17 00:00:00 2001 From: Bob Song Date: Wed, 12 Aug 2026 11:30:07 +0800 Subject: [PATCH 721/791] ALSA: hda: simplify match functions and remove unreachable return hda_bus_match() has an unreachable 'return 1' after an if/else that covers both branches. Remove the superfluous return and simplify the control flow by dropping the else branch. hdac_codec_match() uses a redundant if/else to return 1 or 0. Simplify to a single return statement. Signed-off-by: Bob Song Link: https://patch.msgid.link/20260812033007.633564-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/core/hda_bus_type.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/hda/core/hda_bus_type.c b/sound/hda/core/hda_bus_type.c index a4afd41b6f84..e1e986a8b5b5 100644 --- a/sound/hda/core/hda_bus_type.c +++ b/sound/hda/core/hda_bus_type.c @@ -39,10 +39,7 @@ EXPORT_SYMBOL_GPL(hdac_get_device_id); static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv) { - if (hdac_get_device_id(dev, drv)) - return 1; - else - return 0; + return !!hdac_get_device_id(dev, drv); } static int hda_bus_match(struct device *dev, const struct device_driver *drv) @@ -59,9 +56,7 @@ static int hda_bus_match(struct device *dev, const struct device_driver *drv) */ if (hdrv->match) return hdrv->match(hdev, hdrv); - else - return hdac_codec_match(hdev, hdrv); - return 1; + return hdac_codec_match(hdev, hdrv); } static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env) From 3cd6cb2dc9598278235b9c76bc188cd4fbc320e5 Mon Sep 17 00:00:00 2001 From: Bob Song Date: Wed, 12 Aug 2026 11:30:19 +0800 Subject: [PATCH 722/791] ALSA: hda/ca0132: set codec->spec to NULL after freeing ca0132_free() and dbpro_free() call kfree(codec->spec) without setting codec->spec to NULL afterward, leaving a dangling pointer. Set it to NULL. Signed-off-by: Bob Song Link: https://patch.msgid.link/20260812033019.635010-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/ca0132.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index 61c9fb42b1de..aa2635d8ab92 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -9628,6 +9628,7 @@ static void ca0132_free(struct hda_codec *codec) #endif kfree(spec->spec_init_verbs); kfree(codec->spec); + codec->spec = NULL; } static void dbpro_free(struct hda_codec *codec) @@ -9638,6 +9639,7 @@ static void dbpro_free(struct hda_codec *codec) kfree(spec->spec_init_verbs); kfree(codec->spec); + codec->spec = NULL; } static void ca0132_config(struct hda_codec *codec) From 59a2cd69b44007fa59566bc52da107fc2b814cf0 Mon Sep 17 00:00:00 2001 From: Bob Song Date: Wed, 12 Aug 2026 11:30:30 +0800 Subject: [PATCH 723/791] ALSA: hda/ca0132: replace sprintf() with snprintf() Replace six sprintf() calls that write to SNDRV_CTL_ELEM_ID_NAME_MAXLEN-sized buffers with snprintf() to avoid potential buffer overflows. Signed-off-by: Bob Song Link: https://patch.msgid.link/20260812033030.635417-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/ca0132.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index aa2635d8ab92..0f8fb62f021d 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -5788,7 +5788,8 @@ static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS; if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; - sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); + snprintf(namestr, sizeof(namestr), "%d %s", + (uinfo->value.enumerated.item * 10), sfx); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5840,9 +5841,9 @@ static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; - sprintf(namestr, "%s %s", - ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, - sfx); + snprintf(namestr, sizeof(namestr), "%s %s", + ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, + sfx); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5894,8 +5895,8 @@ static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; - sprintf(namestr, "%s", - ae5_filter_presets[uinfo->value.enumerated.item].name); + snprintf(namestr, sizeof(namestr), "%s", + ae5_filter_presets[uinfo->value.enumerated.item].name); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -6632,7 +6633,7 @@ static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); - sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "FX: %s %s Volume", pfx, dirstr[dir]); knew.tlv.c = NULL; @@ -6671,9 +6672,9 @@ static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, * prefix to OutFX or InFX enable controls. */ if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) - sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "FX: %s %s Switch", pfx, dirstr[dir]); else - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "%s %s Switch", pfx, dirstr[dir]); return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } From e9966d450b4612423ad2f60827c47cd7657a14f9 Mon Sep 17 00:00:00 2001 From: Hongyang Zhao Date: Wed, 12 Aug 2026 17:35:43 +0800 Subject: [PATCH 724/791] ASoC: dt-bindings: es8316: Add regulator supplies The ES8316 has separate AVDD, CPVDD, DVDD and PVDD supply inputs for its analog, charge pump, digital core and digital I/O domains. Describe all four inputs so boards can model the codec power topology. The binding also covers ES8311 and ES8323, whose supply inputs differ, so restrict these properties to the ES8316 compatible. Keep them optional for compatibility with existing descriptions. Signed-off-by: Hongyang Zhao Link: https://patch.msgid.link/20260812-es8316-regulator-next-20260722-v2-1-e7078bc9bc9c@thundersoft.com Signed-off-by: Mark Brown --- .../bindings/sound/everest,es8316.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml index fe5d938ca310..276c73bb4790 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml @@ -30,6 +30,17 @@ description: | allOf: - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + const: everest,es8316 + else: + properties: + avdd-supply: false + cpvdd-supply: false + dvdd-supply: false + pvdd-supply: false properties: compatible: @@ -49,6 +60,18 @@ properties: items: - const: mclk + avdd-supply: + description: Regulator providing the analog supply, from 2.0 V to 3.6 V + + cpvdd-supply: + description: Regulator providing the charge pump supply, from 1.6 V to 2.0 V + + dvdd-supply: + description: Regulator providing the digital core supply, from 1.6 V to 3.6 V + + pvdd-supply: + description: Regulator providing the digital I/O supply, from 1.6 V to 3.6 V + interrupts: maxItems: 1 description: Headphone detect interrupt @@ -77,6 +100,10 @@ examples: reg = <0x11>; clocks = <&clks 10>; clock-names = "mclk"; + avdd-supply = <®_3p3v>; + cpvdd-supply = <®_1p8v>; + dvdd-supply = <®_1p8v>; + pvdd-supply = <®_1p8v>; #sound-dai-cells = <0>; }; }; From c60279912ef005fe3ebaf2c139c19a14ccb42b5e Mon Sep 17 00:00:00 2001 From: Hongyang Zhao Date: Wed, 12 Aug 2026 17:35:44 +0800 Subject: [PATCH 725/791] ASoC: codecs: es8316: Add regulator support ES8316 has separate AVDD, CPVDD, DVDD and PVDD supply inputs. Request and enable the supplies during I2C probe, before initializing the regmap. Keep them enabled for the lifetime of the I2C device so the regmap cannot access an unpowered device and its cache remains synchronized if the ASoC component is unbound and rebound. Signed-off-by: Hongyang Zhao Link: https://patch.msgid.link/20260812-es8316-regulator-next-20260722-v2-2-e7078bc9bc9c@thundersoft.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8316.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 3abe77423f29..983fa0bca419 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { 256, 384, 400, 500, 512, 768, 1024 }; +static const char * const es8316_supply_names[] = { + "avdd", + "cpvdd", + "dvdd", + "pvdd", +}; + struct es8316_priv { struct mutex lock; struct clk *mclk; @@ -871,6 +879,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) i2c_set_clientdata(i2c_client, es8316); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), + es8316_supply_names); + if (ret) + return dev_err_probe(dev, ret, "unable to enable supplies\n"); + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); if (IS_ERR(es8316->regmap)) return PTR_ERR(es8316->regmap); From 191fe151eb85ff6cb24189fc88c970745497c057 Mon Sep 17 00:00:00 2001 From: Dmytro Moroziuk Date: Wed, 12 Aug 2026 15:18:56 +0300 Subject: [PATCH 726/791] ALSA: hda/conexant: Add mute LED quirk for HP ProBook 440 G5 The HP ProBook 440 G5 requires the CXT_FIXUP_MUTE_LED_GPIO quirk to properly toggle the physical mute and mic-mute LEDs via the CX8200 codec. Without this quirk, the LEDs remain permanently dark. Signed-off-by: Dmytro Moroziuk Link: https://patch.msgid.link/20260812121856.30353-1-dmorozyk1@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index c2f9409b76ef..c517334b56fd 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1107,6 +1107,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x837b, "HP ProBook 440 G5", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), From 0414dd7b6f8b8619c8f1595ba04edb43d405581e Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 12 Aug 2026 23:15:06 +0900 Subject: [PATCH 727/791] ALSA: seq: Drop the dead struct snd_seq_event_bounce The struct describes a bounce payload of an error code followed by the original event and its external data. No kernel has ever sent that. Before commit efc86691e4d8 ("ALSA: seq: Fix kernel heap address leak in bounce_error_event()") the kernel emitted no SNDRV_SEQ_EVENT_BOUNCE at all, and since then it sends the event record alone. Nothing has ever read it either. Its only accessor, snd_seq_event_bounce_ext_data(), has had no caller for the whole git history, and it did not even compile until commit c7e0b5bf9fff ("[ALSA] Remove xxx_t typedefs: Sequencer") incidentally repaired the type name it referred to, three years after the git import. Drop the accessor along with the struct. This removes a definition from a UAPI header. Since no kernel ever produced the layout, nothing can have parsed it, but a program that merely names the type will need to stop. Suggested-by: Takashi Iwai Assisted-by: Claude:claude-opus-5 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260812141506.4016387-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- include/sound/asequencer.h | 3 --- include/uapi/sound/asequencer.h | 10 ---------- sound/core/seq/seq_clientmgr.c | 5 ++--- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/include/sound/asequencer.h b/include/sound/asequencer.h index ddbb6bf801bb..efad366736a4 100644 --- a/include/sound/asequencer.h +++ b/include/sound/asequencer.h @@ -11,9 +11,6 @@ #include #include -/* helper macro */ -#define snd_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(struct snd_seq_event_bounce))) - /* * type check macros */ diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index a5c41f771e05..3deba3965ca5 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -308,16 +308,6 @@ struct snd_seq_ump_event { }; }; -/* - * bounce event - stored as variable size data - */ -struct snd_seq_event_bounce { - int err; - struct snd_seq_event event; - /* external data follows here. */ -}; - - /* system information */ struct snd_seq_system_info { int queues; /* maximum queues count */ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 11fa7e825819..5b86e75c2658 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -530,9 +530,8 @@ static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event) * Return the error event. * * If the receiver client is a user client, the original event is - * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If - * the original event is also variable length, the external data is - * copied after the event record. + * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. The + * external data of a variable length event is not copied along. * If the receiver client is a kernel client, the original event is * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra * kmalloc. From 3d7fb01b36aa85b2fc912b51305fbe011d1c3290 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 10 Aug 2026 21:24:24 -0700 Subject: [PATCH 728/791] ASoC: mediatek: mt8365: use devm_platform_ioremap_resource helpers Simplify the probe function by using devm_platform_ioremap_resource() for the base address and devm_platform_get_and_ioremap_resource() for the SRAM, dropping the manual platform_get_resource() calls. Assisted-by: opencode:deepseek-v4-flash-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260811042424.66882-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8365/mt8365-afe-pcm.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c index 5966ca18c7c9..e9497246f37c 100644 --- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c @@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) spin_lock_init(&afe_priv->afe_ctrl_lock); mutex_init(&afe_priv->afe_clk_mutex); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - afe_priv->afe_sram_vir_addr = - devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { - afe_priv->afe_sram_phy_addr = res->start; - afe_priv->afe_sram_size = resource_size(res); - } + afe_priv->afe_sram_vir_addr = + devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { + afe_priv->afe_sram_phy_addr = res->start; + afe_priv->afe_sram_size = resource_size(res); } /* initial audio related clock */ From 4fc945d8fdfcbe8d484a7ca840ea891b56145eda Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 12 Aug 2026 09:44:37 +0200 Subject: [PATCH 729/791] ASoC: samsung: i2s: drop secondary DAI for i2sv7 hardware variant Commit 9167f260477b ("ASoC: soc-generic-dmaengine: Handle DMA channel request failures correctly") started reporting DMA channel request failures during probe instead of silently ignoring them. This exposed a bug in the Samsung I2S driver: it always registered a second DAI and its associated "tx-sec" DMA channel, even for hardware variants that don't actually support it, such as i2sv7 used on Exynos5433. As a result, sound card probing on Exynos5433-based boards started failing, whereas previously it worked only because the channel request failure was ignored. Drop the QUIRK_SEC_DAI flag from i2sv7, since this variant does not have a secondary DAI and register "Secondary Playback" DAPM route only for variants with such interface. Signed-off-by: Marek Szyprowski Link: https://patch.msgid.link/20260812074438.3225001-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 81d5dd36a246..3e76cb81462f 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1116,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { { "Playback Mixer", NULL, "Primary Playback" }, - { "Playback Mixer", NULL, "Secondary Playback" }, - { "Mixer DAI TX", NULL, "Playback Mixer" }, { "Primary Capture", NULL, "Mixer DAI RX" }, }; +static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { + { "Playback Mixer", NULL, "Secondary Playback" }, +}; + +static int samsung_i2s_component_probe(struct snd_soc_component *component) +{ + struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); + + if (priv->quirks & QUIRK_SEC_DAI) + snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), + samsung_i2s_dapm_routes_sec_play, + ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); + + return 0; +} + static const struct snd_soc_component_driver samsung_i2s_component = { .name = "samsung-i2s", + .probe = samsung_i2s_component_probe, + .dapm_widgets = samsung_i2s_widgets, .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), @@ -1650,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { }; static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { - .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | - QUIRK_SUPPORTS_TDM, + .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, .pcm_rates = SNDRV_PCM_RATE_8000_192000, .i2s_variant_regs = &i2sv7_regs, }; From 79a883d53960fb2eaf02f72a37182078b4fc938e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 12 Aug 2026 15:43:11 +0700 Subject: [PATCH 730/791] ASoC: pxa: Drop redundant probe error messages devm_platform_ioremap_resource() does not report the error itself, but the error is already reported deeper in the call chain, so the dev_err() calls are redundant and can be removed. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260812084311.29188-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/pxa/pxa2xx-ac97-lib.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c index d9c3935636da..d7f720431afb 100644 --- a/sound/soc/pxa/pxa2xx-ac97-lib.c +++ b/sound/soc/pxa/pxa2xx-ac97-lib.c @@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) int irq; ac97_reg_base = devm_platform_ioremap_resource(dev, 0); - if (IS_ERR(ac97_reg_base)) { - dev_err(&dev->dev, "Missing MMIO resource\n"); + if (IS_ERR(ac97_reg_base)) return PTR_ERR(ac97_reg_base); - } if (cpu_is_pxa27x()) { /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ From b215caca714ed1c3b41c4dc6a055454e28e9e6a1 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 12 Aug 2026 17:14:16 +0700 Subject: [PATCH 731/791] ASoC: mxs-saif: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() to prevent log spam when probe returns -EPROBE_DEFER. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260812101418.37966-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index a01a680ad4d7..b877c978a04c 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) mxs_saif[saif->id] = saif; saif->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(saif->clk)) { - ret = PTR_ERR(saif->clk); - dev_err(&pdev->dev, "Cannot get the clock: %d\n", - ret); - return ret; - } + if (IS_ERR(saif->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), + "Cannot get the clock\n"); saif->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(saif->base)) From f0701e5fc299e4ff2cb80c1f00bf2f23b94d6b8a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 12 Aug 2026 17:14:17 +0700 Subject: [PATCH 732/791] ASoC: mxs-saif: Drop redundant probe error messages The functions called here don't log the error themselves, but the error is already reported deeper in the call chain, so the dev_err() calls are redundant and can be removed. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260812101418.37966-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b877c978a04c..a77cd516a1bf 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -841,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) saif->dev = &pdev->dev; ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, dev_name(&pdev->dev), saif); - if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, saif); @@ -857,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, &mxs_saif_dai, 1); - if (ret) { - dev_err(&pdev->dev, "register DAI failed\n"); + if (ret) return ret; - } ret = mxs_pcm_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + if (ret) return ret; - } return 0; } From e84c06775d4a3508b8068bc84986b4a29e27b1d3 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 12 Aug 2026 17:14:18 +0700 Subject: [PATCH 733/791] ASoC: mxs-sgtl5000: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Link: https://patch.msgid.link/20260812101418.37966-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index f1c0e612313d..a253a48ca59c 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -155,8 +155,6 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret) { - dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", - ret); mxs_saif_put_mclk(0); return ret; } From 0a91bb72980e4aa5f0c1be33d691e7767d7db9ed Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 5 Aug 2026 13:43:48 +0700 Subject: [PATCH 734/791] ASoC: spacemit: advertise only DMA-backed DAI streams The static DAI template initializes both playback and capture stream capabilities before dma-names is examined. As a result, snd_soc_dai_stream_valid() considers both directions valid even when the device only provides a single DMA channel. Move the playback and capture capability initialization into spacemit_i2s_init_dai(), where it is performed only for the stream directions backed by a corresponding DMA channel. This preserves the existing capabilities for devices with both "tx" and "rx" DMA channels, while preventing unsupported stream directions from being advertised. Initialize rate_min and rate_max together with the other stream capabilities to preserve the existing rate constraints. Fixes: fce217449075 ("ASoC: spacemit: add i2s support for K1 SoC") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260805064348.44283-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/spacemit/k1_i2s.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 2d5ea1fd5d49..28a762769253 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -354,22 +354,6 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { static struct snd_soc_dai_driver spacemit_i2s_dai = { .ops = &spacemit_i2s_dai_ops, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SPACEMIT_PCM_RATES, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, - .formats = SPACEMIT_PCM_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SPACEMIT_PCM_RATES, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, - .formats = SPACEMIT_PCM_FORMATS, - }, .symmetric_rate = 1, }; @@ -399,6 +383,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, dai->playback.channels_min = 1; dai->playback.channels_max = 2; dai->playback.rates = SPACEMIT_PCM_RATES; + dai->playback.rate_min = SNDRV_PCM_RATE_8000; + dai->playback.rate_max = SNDRV_PCM_RATE_48000; dai->playback.formats = SPACEMIT_PCM_FORMATS; i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; @@ -411,6 +397,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, dai->capture.channels_min = 1; dai->capture.channels_max = 2; dai->capture.rates = SPACEMIT_PCM_RATES; + dai->capture.rate_min = SNDRV_PCM_RATE_8000; + dai->capture.rate_max = SNDRV_PCM_RATE_48000; dai->capture.formats = SPACEMIT_PCM_FORMATS; i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; From 36aa66de481d29edd63cbad9b5c4dc18c340fdf6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 13 Aug 2026 14:55:24 +0800 Subject: [PATCH 735/791] ALSA: hda/ext: preserve PPLCCTL bits when clearing reset snd_hdac_ext_stream_reset() polls PPLCCTL for STRST by masking the register value with AZX_PPLCCTL_STRST: val = readl(...) & AZX_PPLCCTL_STRST; The same masked value is then used when clearing STRST. Since val contains no bits other than STRST, clearing STRST from it always produces zero. The subsequent writel() therefore writes zero to the entire PPLCCTL register instead of clearing only the reset bit. PPLCCTL contains other stream control fields, including the stream tag in AZX_PPLCCTL_STRM_MASK. Those fields must not be modified as a side effect of clearing stream reset. Use snd_hdac_updatel() to clear STRST, matching the existing set-reset path and preserving all unrelated PPLCCTL bits. Fixes: df203a4e46f4 ("ALSA: hdac_ext: add extended stream capabilities") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/43BB7930B0F07C09+20260813065524.1955696-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/core/ext/stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c index 4c7506d49f55..517bd151fcc3 100644 --- a/sound/hda/core/ext/stream.c +++ b/sound/hda/core/ext/stream.c @@ -210,8 +210,8 @@ void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream) break; udelay(3); } while (--timeout); - val &= ~AZX_PPLCCTL_STRST; - writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, + AZX_PPLCCTL_STRST, 0); udelay(3); timeout = 50; From bfdadd1656b6a21d30738b63909494e98ad7e3e7 Mon Sep 17 00:00:00 2001 From: Bramwel Barack Date: Wed, 12 Aug 2026 22:28:32 +0300 Subject: [PATCH 736/791] ALSA: hda/realtek: Add mute LED quirk for HP 250 G8 (0x85f3) The HP 250 G8 Laptop PC (subsystem 103c:85f3) using the Realtek ALC236 codec requires a specific quirk to enable the mute button LED. Currently, the audio mutes in software, but the physical indicator light remains unlit. Adding a quirk entry to the alc236_fixup_tbl with the ALC236_FIXUP_HP_MUTE_LED_COEFBIT2 fixup correctly maps the LED to the mute state via COEF index 0x07. Signed-off-by: Bramwel Barack Link: https://patch.msgid.link/20260812192832.69240-1-bramwelbarack89@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 87d59a9dc55f..be7287ea24ec 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7259,6 +7259,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x85f3, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), From 583e44f2828b9628535c099ebdeb8758c9b2d90f Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 12 Aug 2026 22:50:34 +0200 Subject: [PATCH 737/791] ASoC: meson: aiu: make aiu_formatter_i2s_drv static aiu_formatter_i2s_drv is indeed used only in the aiu and it is not meant to be exported. It should be static. Cc: Valerio Setti Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202608080634.hf6aJGPB-lkp@intel.com/ Fixes: 83b83024cdbf ("ASoC: meson: aiu: use aiu-formatter-i2s to format I2S output data") Signed-off-by: Jerome Brunet Reviewed-by: Valerio Setti Link: https://patch.msgid.link/20260812-aiu-formatter-static-v1-1-67936d57ba05@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index 2668646e3597..535fbf7c1639 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -182,7 +182,7 @@ static const struct regmap_config aiu_regmap_cfg = { .max_register = 0x2ac, }; -const struct gx_formatter_driver aiu_formatter_i2s_drv = { +static const struct gx_formatter_driver aiu_formatter_i2s_drv = { .regmap_cfg = &aiu_regmap_cfg, .ops = &aiu_formatter_i2s_ops, }; From 91415225fbfb38c4b210e656e11a527a3760f6e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:33 +0200 Subject: [PATCH 738/791] ALSA: uapi: Drop __bitwise and __force prefix We've used __bitwise and __force for some integer parameters for sanity-checks via sparse, with a hope that it'll reduce the misuse or incorrect assignments. This worked in principle, but OTOH, it's been quite a PITA, making the code much uglier than its gain, too, because one had to cast with __force everywhere. Also, Rust-binding would skip those defines because of __force usage, which will become more pains in near future. So let's drop __bitwise and __force prefix usages. In this patch, we start cleaning up the UAPI headers at first. The former bit-wised typedefs are still kept for compatibility for now. As it's only markers for sparse, the changes are absolutely safe, per se. Only that we'll need to watch out more carefully about the variable usage for PCM format type, etc. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-2-tiwai@suse.de --- include/uapi/sound/asequencer.h | 8 +- include/uapi/sound/asound.h | 178 ++++++++++++++++---------------- 2 files changed, 93 insertions(+), 93 deletions(-) diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index 3deba3965ca5..c2e3d079c51e 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -338,10 +338,10 @@ struct snd_seq_running_info { /* client types */ -typedef int __bitwise snd_seq_client_type_t; -#define NO_CLIENT ((__force snd_seq_client_type_t) 0) -#define USER_CLIENT ((__force snd_seq_client_type_t) 1) -#define KERNEL_CLIENT ((__force snd_seq_client_type_t) 2) +typedef int snd_seq_client_type_t; +#define NO_CLIENT 0 +#define USER_CLIENT 1 +#define KERNEL_CLIENT 2 /* event filter flags */ #define SNDRV_SEQ_FILTER_BROADCAST (1U<<0) /* accept broadcast messages */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 500599213f93..c11da9656e38 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -169,72 +169,72 @@ enum { SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, }; -typedef int __bitwise snd_pcm_access_t; -#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ((__force snd_pcm_access_t) 0) /* interleaved mmap */ -#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ((__force snd_pcm_access_t) 1) /* noninterleaved mmap */ -#define SNDRV_PCM_ACCESS_MMAP_COMPLEX ((__force snd_pcm_access_t) 2) /* complex mmap */ -#define SNDRV_PCM_ACCESS_RW_INTERLEAVED ((__force snd_pcm_access_t) 3) /* readi/writei */ -#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ((__force snd_pcm_access_t) 4) /* readn/writen */ +typedef int snd_pcm_access_t; +#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED 0 /* interleaved mmap */ +#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED 1 /* noninterleaved mmap */ +#define SNDRV_PCM_ACCESS_MMAP_COMPLEX 2 /* complex mmap */ +#define SNDRV_PCM_ACCESS_RW_INTERLEAVED 3 /* readi/writei */ +#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED 4 /* readn/writen */ #define SNDRV_PCM_ACCESS_LAST SNDRV_PCM_ACCESS_RW_NONINTERLEAVED -typedef int __bitwise snd_pcm_format_t; -#define SNDRV_PCM_FORMAT_S8 ((__force snd_pcm_format_t) 0) -#define SNDRV_PCM_FORMAT_U8 ((__force snd_pcm_format_t) 1) -#define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2) -#define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3) -#define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4) -#define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5) -#define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6) /* low three bytes */ -#define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */ -#define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */ -#define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */ +typedef int snd_pcm_format_t; +#define SNDRV_PCM_FORMAT_S8 0 +#define SNDRV_PCM_FORMAT_U8 1 +#define SNDRV_PCM_FORMAT_S16_LE 2 +#define SNDRV_PCM_FORMAT_S16_BE 3 +#define SNDRV_PCM_FORMAT_U16_LE 4 +#define SNDRV_PCM_FORMAT_U16_BE 5 +#define SNDRV_PCM_FORMAT_S24_LE 6 /* low three bytes */ +#define SNDRV_PCM_FORMAT_S24_BE 7 /* low three bytes */ +#define SNDRV_PCM_FORMAT_U24_LE 8 /* low three bytes */ +#define SNDRV_PCM_FORMAT_U24_BE 9 /* low three bytes */ /* * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the * available bit count in most significant bit. It's for the case of so-called 'left-justified' or * `right-padding` sample which has less width than 32 bit. */ -#define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) -#define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) -#define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) -#define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13) -#define SNDRV_PCM_FORMAT_FLOAT_LE ((__force snd_pcm_format_t) 14) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT_BE ((__force snd_pcm_format_t) 15) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT64_LE ((__force snd_pcm_format_t) 16) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT64_BE ((__force snd_pcm_format_t) 17) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18) /* IEC-958 subframe, Little Endian */ -#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19) /* IEC-958 subframe, Big Endian */ -#define SNDRV_PCM_FORMAT_MU_LAW ((__force snd_pcm_format_t) 20) -#define SNDRV_PCM_FORMAT_A_LAW ((__force snd_pcm_format_t) 21) -#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22) -#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23) -#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24) -#define SNDRV_PCM_FORMAT_S20_LE ((__force snd_pcm_format_t) 25) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_S20_BE ((__force snd_pcm_format_t) 26) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_U20_LE ((__force snd_pcm_format_t) 27) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_U20_BE ((__force snd_pcm_format_t) 28) /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_S32_LE 10 +#define SNDRV_PCM_FORMAT_S32_BE 11 +#define SNDRV_PCM_FORMAT_U32_LE 12 +#define SNDRV_PCM_FORMAT_U32_BE 13 +#define SNDRV_PCM_FORMAT_FLOAT_LE 14 /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT_BE 15 /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT64_LE 16 /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT64_BE 17 /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE 18 /* IEC-958 subframe, Little Endian */ +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE 19 /* IEC-958 subframe, Big Endian */ +#define SNDRV_PCM_FORMAT_MU_LAW 20 +#define SNDRV_PCM_FORMAT_A_LAW 21 +#define SNDRV_PCM_FORMAT_IMA_ADPCM 22 +#define SNDRV_PCM_FORMAT_MPEG 23 +#define SNDRV_PCM_FORMAT_GSM 24 +#define SNDRV_PCM_FORMAT_S20_LE 25 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_S20_BE 26 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_U20_LE 27 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_U20_BE 28 /* in four bytes, LSB justified */ /* gap in the numbering for a future standard linear format */ -#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31) -#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U24_3LE ((__force snd_pcm_format_t) 34) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U24_3BE ((__force snd_pcm_format_t) 35) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S20_3LE ((__force snd_pcm_format_t) 36) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S20_3BE ((__force snd_pcm_format_t) 37) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U20_3LE ((__force snd_pcm_format_t) 38) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U20_3BE ((__force snd_pcm_format_t) 39) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S18_3LE ((__force snd_pcm_format_t) 40) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S18_3BE ((__force snd_pcm_format_t) 41) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U18_3LE ((__force snd_pcm_format_t) 42) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U18_3BE ((__force snd_pcm_format_t) 43) /* in three bytes */ -#define SNDRV_PCM_FORMAT_G723_24 ((__force snd_pcm_format_t) 44) /* 8 samples in 3 bytes */ -#define SNDRV_PCM_FORMAT_G723_24_1B ((__force snd_pcm_format_t) 45) /* 1 sample in 1 byte */ -#define SNDRV_PCM_FORMAT_G723_40 ((__force snd_pcm_format_t) 46) /* 8 Samples in 5 bytes */ -#define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */ -#define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */ -#define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */ -#define SNDRV_PCM_FORMAT_DSD_U32_LE ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */ -#define SNDRV_PCM_FORMAT_DSD_U16_BE ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */ -#define SNDRV_PCM_FORMAT_DSD_U32_BE ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */ +#define SNDRV_PCM_FORMAT_SPECIAL 31 +#define SNDRV_PCM_FORMAT_S24_3LE 32 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S24_3BE 33 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U24_3LE 34 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U24_3BE 35 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S20_3LE 36 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S20_3BE 37 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U20_3LE 38 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U20_3BE 39 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S18_3LE 40 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S18_3BE 41 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U18_3LE 42 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U18_3BE 43 /* in three bytes */ +#define SNDRV_PCM_FORMAT_G723_24 44 /* 8 samples in 3 bytes */ +#define SNDRV_PCM_FORMAT_G723_24_1B 45 /* 1 sample in 1 byte */ +#define SNDRV_PCM_FORMAT_G723_40 46 /* 8 Samples in 5 bytes */ +#define SNDRV_PCM_FORMAT_G723_40_1B 47 /* 1 sample in 1 byte */ +#define SNDRV_PCM_FORMAT_DSD_U8 48 /* DSD, 1-byte samples DSD (x8) */ +#define SNDRV_PCM_FORMAT_DSD_U16_LE 49 /* DSD, 2-byte samples DSD (x16), little endian */ +#define SNDRV_PCM_FORMAT_DSD_U32_LE 50 /* DSD, 4-byte samples DSD (x32), little endian */ +#define SNDRV_PCM_FORMAT_DSD_U16_BE 51 /* DSD, 2-byte samples DSD (x16), big endian */ +#define SNDRV_PCM_FORMAT_DSD_U32_BE 52 /* DSD, 4-byte samples DSD (x32), big endian */ #define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_BE #define SNDRV_PCM_FORMAT_FIRST SNDRV_PCM_FORMAT_S8 @@ -265,11 +265,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_U20 SNDRV_PCM_FORMAT_U20_BE #endif -typedef int __bitwise snd_pcm_subformat_t; -#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX ((__force snd_pcm_subformat_t) 1) -#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 2) -#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 3) +typedef int snd_pcm_subformat_t; +#define SNDRV_PCM_SUBFORMAT_STD 0 +#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX 1 +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 2 +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 3 #define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ @@ -303,16 +303,16 @@ typedef int __bitwise snd_pcm_subformat_t; #define __SND_STRUCT_TIME64 #endif -typedef int __bitwise snd_pcm_state_t; -#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ -#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ -#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2) /* stream is ready to start */ -#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3) /* stream is running */ -#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4) /* stream reached an xrun */ -#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5) /* stream is draining */ -#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6) /* stream is paused */ -#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7) /* hardware is suspended */ -#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8) /* hardware is disconnected */ +typedef int snd_pcm_state_t; +#define SNDRV_PCM_STATE_OPEN 0 /* stream is open */ +#define SNDRV_PCM_STATE_SETUP 1 /* stream has a setup */ +#define SNDRV_PCM_STATE_PREPARED 2 /* stream is ready to start */ +#define SNDRV_PCM_STATE_RUNNING 3 /* stream is running */ +#define SNDRV_PCM_STATE_XRUN 4 /* stream reached an xrun */ +#define SNDRV_PCM_STATE_DRAINING 5 /* stream is draining */ +#define SNDRV_PCM_STATE_PAUSED 6 /* stream is paused */ +#define SNDRV_PCM_STATE_SUSPENDED 7 /* hardware is suspended */ +#define SNDRV_PCM_STATE_DISCONNECTED 8 /* hardware is disconnected */ #define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED enum { @@ -1091,24 +1091,24 @@ struct snd_ctl_card_bytes { __u64 data; /* user buffer (pointer stored as __u64) */ }; -typedef int __bitwise snd_ctl_elem_type_t; -#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) /* invalid */ -#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) /* boolean type */ -#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2) /* integer type */ -#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3) /* enumerated type */ -#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4) /* byte array */ -#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5) /* IEC958 (S/PDIF) setup */ -#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6) /* 64-bit integer type */ +typedef int snd_ctl_elem_type_t; +#define SNDRV_CTL_ELEM_TYPE_NONE 0 /* invalid */ +#define SNDRV_CTL_ELEM_TYPE_BOOLEAN 1 /* boolean type */ +#define SNDRV_CTL_ELEM_TYPE_INTEGER 2 /* integer type */ +#define SNDRV_CTL_ELEM_TYPE_ENUMERATED 3 /* enumerated type */ +#define SNDRV_CTL_ELEM_TYPE_BYTES 4 /* byte array */ +#define SNDRV_CTL_ELEM_TYPE_IEC958 5 /* IEC958 (S/PDIF) setup */ +#define SNDRV_CTL_ELEM_TYPE_INTEGER64 6 /* 64-bit integer type */ #define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64 -typedef int __bitwise snd_ctl_elem_iface_t; -#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0) /* global control */ -#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1) /* hardware dependent device */ -#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2) /* virtual mixer device */ -#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3) /* PCM device */ -#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4) /* RawMidi device */ -#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5) /* timer device */ -#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6) /* sequencer client */ +typedef int snd_ctl_elem_iface_t; +#define SNDRV_CTL_ELEM_IFACE_CARD 0 /* global control */ +#define SNDRV_CTL_ELEM_IFACE_HWDEP 1 /* hardware dependent device */ +#define SNDRV_CTL_ELEM_IFACE_MIXER 2 /* virtual mixer device */ +#define SNDRV_CTL_ELEM_IFACE_PCM 3 /* PCM device */ +#define SNDRV_CTL_ELEM_IFACE_RAWMIDI 4 /* RawMidi device */ +#define SNDRV_CTL_ELEM_IFACE_TIMER 5 /* timer device */ +#define SNDRV_CTL_ELEM_IFACE_SEQUENCER 6 /* sequencer client */ #define SNDRV_CTL_ELEM_IFACE_LAST SNDRV_CTL_ELEM_IFACE_SEQUENCER #define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) From 3850dce65e2595feae868f9c4b1395e4bc1dfe6b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:34 +0200 Subject: [PATCH 739/791] ALSA: pcm: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-3-tiwai@suse.de --- include/sound/pcm.h | 10 ++++------ include/sound/pcm_params.h | 13 +++++-------- sound/core/pcm.c | 12 +++++------- sound/core/pcm_misc.c | 25 +++++++++++-------------- sound/core/pcm_native.c | 28 ++++++++++++++-------------- 5 files changed, 39 insertions(+), 49 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 76fc33dce537..ab96396a7444 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -145,7 +145,7 @@ struct snd_pcm_ops { #define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|\ SNDRV_PCM_RATE_705600|\ SNDRV_PCM_RATE_768000) -#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) +#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << SNDRV_PCM_FORMAT_##fmt) #define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) #define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) #define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) @@ -228,7 +228,7 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE #endif -#define _SNDRV_PCM_SUBFMTBIT(fmt) BIT((__force int)SNDRV_PCM_SUBFORMAT_##fmt) +#define _SNDRV_PCM_SUBFMTBIT(fmt) BIT(SNDRV_PCM_SUBFORMAT_##fmt) #define SNDRV_PCM_SUBFMTBIT_STD _SNDRV_PCM_SUBFMTBIT(STD) #define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX _SNDRV_PCM_SUBFMTBIT(MSBITS_MAX) #define SNDRV_PCM_SUBFMTBIT_MSBITS_20 _SNDRV_PCM_SUBFMTBIT(MSBITS_20) @@ -1515,7 +1515,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, */ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) { - return 1ULL << (__force int) pcm_format; + return 1ULL << pcm_format; } /** @@ -1523,9 +1523,7 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) * @f: the iterator variable in snd_pcm_format_t type */ #define pcm_for_each_format(f) \ - for ((f) = SNDRV_PCM_FORMAT_FIRST; \ - (__force int)(f) <= (__force int)SNDRV_PCM_FORMAT_LAST; \ - (f) = (__force snd_pcm_format_t)((__force int)(f) + 1)) + for ((f) = SNDRV_PCM_FORMAT_FIRST; (f) <= SNDRV_PCM_FORMAT_LAST; (f)++) /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index fbf35df6e5cf..bc43955fcb76 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -71,7 +71,7 @@ static inline void snd_mask_set(struct snd_mask *mask, unsigned int val) static inline void snd_mask_set_format(struct snd_mask *mask, snd_pcm_format_t format) { - snd_mask_set(mask, (__force unsigned int)format); + snd_mask_set(mask, format); } static inline void snd_mask_reset(struct snd_mask *mask, unsigned int val) @@ -132,7 +132,7 @@ static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val) static inline int snd_mask_test_format(const struct snd_mask *mask, snd_pcm_format_t format) { - return snd_mask_test(mask, (__force unsigned int)format); + return snd_mask_test(mask, format); } static inline int snd_mask_single(const struct snd_mask *mask) @@ -302,8 +302,7 @@ static inline int snd_interval_eq(const struct snd_interval *i1, const struct sn */ static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_access_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_ACCESS)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_ACCESS)); } /** @@ -312,8 +311,7 @@ static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) */ static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_format_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_FORMAT)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_FORMAT)); } /** @@ -323,8 +321,7 @@ static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) static inline snd_pcm_subformat_t params_subformat(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_subformat_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_SUBFORMAT)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_SUBFORMAT)); } /** diff --git a/sound/core/pcm.c b/sound/core/pcm.c index bfedf571e021..41c2cab7a52c 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -211,11 +211,9 @@ static const char * const snd_pcm_format_names[] = { */ const char *snd_pcm_format_name(snd_pcm_format_t format) { - unsigned int format_num = (__force unsigned int)format; - - if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num]) + if (format >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format]) return "Unknown"; - return snd_pcm_format_names[format_num]; + return snd_pcm_format_names[format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -275,12 +273,12 @@ static const char *snd_pcm_stream_name(int stream) static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[(__force int)access]; + return snd_pcm_access_names[access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[(__force int)subformat]; + return snd_pcm_subformat_names[subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) @@ -290,7 +288,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode) static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[(__force int)state]; + return snd_pcm_state_names[state]; } #if IS_ENABLED(CONFIG_SND_PCM_OSS) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 180b6b64a448..13de3b02aa34 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -24,15 +24,12 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ -#define INT __force int - static bool valid_format(snd_pcm_format_t format) { - return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST; + return format >= 0 && format <= SNDRV_PCM_FORMAT_LAST; } -static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { +static const struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -251,7 +248,7 @@ int snd_pcm_format_signed(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].signd; + val = pcm_formats[format].signd; if (val < 0) return -EINVAL; return val; @@ -300,7 +297,7 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].le; + val = pcm_formats[format].le; if (val < 0) return -EINVAL; return val; @@ -337,7 +334,7 @@ int snd_pcm_format_width(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].width; + val = pcm_formats[format].width; if (!val) return -EINVAL; return val; @@ -356,7 +353,7 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].phys; + val = pcm_formats[format].phys; if (!val) return -EINVAL; return val; @@ -390,9 +387,9 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { if (!valid_format(format)) return NULL; - if (! pcm_formats[(INT)format].phys) + if (! pcm_formats[format].phys) return NULL; - return pcm_formats[(INT)format].silence; + return pcm_formats[format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -416,12 +413,12 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[(INT)format].phys; /* physical width */ + width = pcm_formats[format].phys; /* physical width */ if (!width) return -EINVAL; - pat = pcm_formats[(INT)format].silence; + pat = pcm_formats[format].silence; /* signed or 1 byte data */ - if (pcm_formats[(INT)format].signd == 1 || width <= 8) { + if (pcm_formats[format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index f44dc334aac6..4a5057e7629d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -257,7 +257,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, } /* macro for simplified cast */ -#define PARAM_MASK_BIT(b) (1U << (__force int)(b)) +#define PARAM_MASK_BIT(b) (1U << (b)) static bool hw_support_mmap(struct snd_pcm_substream *substream) { @@ -489,7 +489,7 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, params->msbits = snd_interval_value(i); m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); if (snd_mask_single(m)) { - snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m); + snd_pcm_format_t format = snd_mask_min(m); params->msbits = snd_pcm_format_width(format); } } @@ -497,13 +497,13 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, if (params->msbits) { m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); if (snd_mask_single(m)) { - snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m); + snd_pcm_format_t format = snd_mask_min(m); if (snd_pcm_format_linear(format) && snd_pcm_format_width(format) != params->msbits) { m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT); snd_mask_reset(m_rw, - (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + SNDRV_PCM_SUBFORMAT_MSBITS_MAX); if (snd_mask_empty(m_rw)) return -EINVAL; } @@ -1252,7 +1252,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) runtime->trigger_master = NULL; } -#define ACTION_ARG_IGNORE (__force snd_pcm_state_t)0 +#define ACTION_ARG_IGNORE 0 struct action_ops { int (*pre_action)(struct snd_pcm_substream *substream, @@ -1635,7 +1635,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun); /* * pause callbacks: pass boolean (to start pause or resume) as state argument */ -#define pause_pushed(state) (__force bool)(state) +#define pause_pushed(state) (bool)(state) static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, snd_pcm_state_t state) @@ -1707,14 +1707,14 @@ static const struct action_ops snd_pcm_action_pause = { static int snd_pcm_pause(struct snd_pcm_substream *substream, bool push) { return snd_pcm_action(&snd_pcm_action_pause, substream, - (__force snd_pcm_state_t)push); + (snd_pcm_state_t)push); } static int snd_pcm_pause_lock_irq(struct snd_pcm_substream *substream, bool push) { return snd_pcm_action_lock_irq(&snd_pcm_action_pause, substream, - (__force snd_pcm_state_t)push); + (snd_pcm_state_t)push); } #ifdef CONFIG_PM @@ -1982,7 +1982,7 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, snd_pcm_state_t state) { snd_pcm_state_t cur_state = snd_pcm_get_state(substream); - int f_flags = (__force int)state; + int f_flags = state; if (cur_state == SNDRV_PCM_STATE_OPEN || cur_state == SNDRV_PCM_STATE_DISCONNECTED) @@ -2050,7 +2050,7 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, - (__force snd_pcm_state_t)f_flags); + (snd_pcm_state_t)f_flags); } /* @@ -2461,7 +2461,7 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, if (bits <= 0) continue; /* ignore invalid formats */ if ((unsigned)bits < i->min || (unsigned)bits > i->max) - snd_mask_reset(&m, (__force unsigned)k); + snd_mask_reset(&m, k); } return snd_mask_refine(mask, &m); } @@ -2543,16 +2543,16 @@ static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params, snd_mask_none(&m); /* All PCMs support at least the default STD subformat. */ - snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD); + snd_mask_set(&m, SNDRV_PCM_SUBFORMAT_STD); pcm_for_each_format(f) { - if (!snd_mask_test(fmask, (__force unsigned)f)) + if (!snd_mask_test(fmask, f)) continue; if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats) m.bits[0] |= *subformats; else if (snd_pcm_format_linear(f)) - snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + snd_mask_set(&m, SNDRV_PCM_SUBFORMAT_MSBITS_MAX); } return snd_mask_refine(sfmask, &m); From de8131f2f1ba763b68ded06c3b4a63fb70d5dd81 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:35 +0200 Subject: [PATCH 740/791] ALSA: pcm: Avoid macros for SNDRV_PCM_FMTBIT and SNDRV_PCM_SUBFMTBIT Avoid macros to define SNDRV_PCM_FMTBIT_* and SNDRV_PCM_SUBFMTBIT_* contants but use plain bit shifts, instead. This allows bindgen and other tools aware of those definitions. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-4-tiwai@suse.de --- include/sound/pcm.h | 112 ++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index ab96396a7444..02d8689354c4 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -145,61 +145,60 @@ struct snd_pcm_ops { #define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|\ SNDRV_PCM_RATE_705600|\ SNDRV_PCM_RATE_768000) -#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << SNDRV_PCM_FORMAT_##fmt) -#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) -#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) -#define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) -#define SNDRV_PCM_FMTBIT_S16_BE _SNDRV_PCM_FMTBIT(S16_BE) -#define SNDRV_PCM_FMTBIT_U16_LE _SNDRV_PCM_FMTBIT(U16_LE) -#define SNDRV_PCM_FMTBIT_U16_BE _SNDRV_PCM_FMTBIT(U16_BE) -#define SNDRV_PCM_FMTBIT_S24_LE _SNDRV_PCM_FMTBIT(S24_LE) -#define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) -#define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) -#define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) // For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the // available bit count in most significant bit. It's for the case of so-called 'left-justified' or // `right-padding` sample which has less width than 32 bit. -#define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) -#define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) -#define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) -#define SNDRV_PCM_FMTBIT_U32_BE _SNDRV_PCM_FMTBIT(U32_BE) -#define SNDRV_PCM_FMTBIT_FLOAT_LE _SNDRV_PCM_FMTBIT(FLOAT_LE) -#define SNDRV_PCM_FMTBIT_FLOAT_BE _SNDRV_PCM_FMTBIT(FLOAT_BE) -#define SNDRV_PCM_FMTBIT_FLOAT64_LE _SNDRV_PCM_FMTBIT(FLOAT64_LE) -#define SNDRV_PCM_FMTBIT_FLOAT64_BE _SNDRV_PCM_FMTBIT(FLOAT64_BE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE) -#define SNDRV_PCM_FMTBIT_MU_LAW _SNDRV_PCM_FMTBIT(MU_LAW) -#define SNDRV_PCM_FMTBIT_A_LAW _SNDRV_PCM_FMTBIT(A_LAW) -#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) -#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) -#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) -#define SNDRV_PCM_FMTBIT_S20_LE _SNDRV_PCM_FMTBIT(S20_LE) -#define SNDRV_PCM_FMTBIT_U20_LE _SNDRV_PCM_FMTBIT(U20_LE) -#define SNDRV_PCM_FMTBIT_S20_BE _SNDRV_PCM_FMTBIT(S20_BE) -#define SNDRV_PCM_FMTBIT_U20_BE _SNDRV_PCM_FMTBIT(U20_BE) -#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) -#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) -#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) -#define SNDRV_PCM_FMTBIT_S24_3BE _SNDRV_PCM_FMTBIT(S24_3BE) -#define SNDRV_PCM_FMTBIT_U24_3BE _SNDRV_PCM_FMTBIT(U24_3BE) -#define SNDRV_PCM_FMTBIT_S20_3LE _SNDRV_PCM_FMTBIT(S20_3LE) -#define SNDRV_PCM_FMTBIT_U20_3LE _SNDRV_PCM_FMTBIT(U20_3LE) -#define SNDRV_PCM_FMTBIT_S20_3BE _SNDRV_PCM_FMTBIT(S20_3BE) -#define SNDRV_PCM_FMTBIT_U20_3BE _SNDRV_PCM_FMTBIT(U20_3BE) -#define SNDRV_PCM_FMTBIT_S18_3LE _SNDRV_PCM_FMTBIT(S18_3LE) -#define SNDRV_PCM_FMTBIT_U18_3LE _SNDRV_PCM_FMTBIT(U18_3LE) -#define SNDRV_PCM_FMTBIT_S18_3BE _SNDRV_PCM_FMTBIT(S18_3BE) -#define SNDRV_PCM_FMTBIT_U18_3BE _SNDRV_PCM_FMTBIT(U18_3BE) -#define SNDRV_PCM_FMTBIT_G723_24 _SNDRV_PCM_FMTBIT(G723_24) -#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) -#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) -#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) -#define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8) -#define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE) -#define SNDRV_PCM_FMTBIT_DSD_U32_LE _SNDRV_PCM_FMTBIT(DSD_U32_LE) -#define SNDRV_PCM_FMTBIT_DSD_U16_BE _SNDRV_PCM_FMTBIT(DSD_U16_BE) -#define SNDRV_PCM_FMTBIT_DSD_U32_BE _SNDRV_PCM_FMTBIT(DSD_U32_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_S20_LE (1ULL << SNDRV_PCM_FORMAT_S20_LE) +#define SNDRV_PCM_FMTBIT_U20_LE (1ULL << SNDRV_PCM_FORMAT_U20_LE) +#define SNDRV_PCM_FMTBIT_S20_BE (1ULL << SNDRV_PCM_FORMAT_S20_BE) +#define SNDRV_PCM_FMTBIT_U20_BE (1ULL << SNDRV_PCM_FORMAT_U20_BE) +#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) +#define SNDRV_PCM_FMTBIT_G723_24 (1ULL << SNDRV_PCM_FORMAT_G723_24) +#define SNDRV_PCM_FMTBIT_G723_24_1B (1ULL << SNDRV_PCM_FORMAT_G723_24_1B) +#define SNDRV_PCM_FMTBIT_G723_40 (1ULL << SNDRV_PCM_FORMAT_G723_40) +#define SNDRV_PCM_FMTBIT_G723_40_1B (1ULL << SNDRV_PCM_FORMAT_G723_40_1B) +#define SNDRV_PCM_FMTBIT_DSD_U8 (1ULL << SNDRV_PCM_FORMAT_DSD_U8) +#define SNDRV_PCM_FMTBIT_DSD_U16_LE (1ULL << SNDRV_PCM_FORMAT_DSD_U16_LE) +#define SNDRV_PCM_FMTBIT_DSD_U32_LE (1ULL << SNDRV_PCM_FORMAT_DSD_U32_LE) +#define SNDRV_PCM_FMTBIT_DSD_U16_BE (1ULL << SNDRV_PCM_FORMAT_DSD_U16_BE) +#define SNDRV_PCM_FMTBIT_DSD_U32_BE (1ULL << SNDRV_PCM_FORMAT_DSD_U32_BE) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE @@ -228,11 +227,10 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE #endif -#define _SNDRV_PCM_SUBFMTBIT(fmt) BIT(SNDRV_PCM_SUBFORMAT_##fmt) -#define SNDRV_PCM_SUBFMTBIT_STD _SNDRV_PCM_SUBFMTBIT(STD) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX _SNDRV_PCM_SUBFMTBIT(MSBITS_MAX) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 _SNDRV_PCM_SUBFMTBIT(MSBITS_20) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 _SNDRV_PCM_SUBFMTBIT(MSBITS_24) +#define SNDRV_PCM_SUBFMTBIT_STD (1U << SNDRV_PCM_SUBFORMAT_STD) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX (1U << SNDRV_PCM_SUBFORMAT_MSBITS_MAX) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 (1U << SNDRV_PCM_SUBFORMAT_MSBITS_20) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 (1U << SNDRV_PCM_SUBFORMAT_MSBITS_24) struct snd_pcm_file { struct snd_pcm_substream *substream; From cfea0fbbdb28566be414c1db2496d807ef5daa74 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:36 +0200 Subject: [PATCH 741/791] ALSA: control: Drop __force casts Now that the bitwise parameter definitions are gone for control parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-5-tiwai@suse.de --- sound/core/control_compat.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index f14d9f5e94be..87f7ad5ab739 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -226,8 +226,8 @@ static int copy_ctl_value_from_user(struct snd_card *card, if (type < 0) return type; - if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN || - type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { s32 __user *intp = valuep; int val; @@ -236,7 +236,7 @@ static int copy_ctl_value_from_user(struct snd_card *card, data->value.integer.value[i] = val; } } else { - size = get_elem_size((__force snd_ctl_elem_type_t)type, count); + size = get_elem_size(type, count); if (size < 0) { dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); return -EINVAL; @@ -259,8 +259,8 @@ static int copy_ctl_value_to_user(void __user *userdata, struct snd_ctl_elem_value32 __user *data32 = userdata; int i, size; - if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN || - type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { s32 __user *intp = valuep; int val; @@ -269,7 +269,7 @@ static int copy_ctl_value_to_user(void __user *userdata, return -EFAULT; } } else { - size = get_elem_size((__force snd_ctl_elem_type_t)type, count); + size = get_elem_size(type, count); if (copy_to_user(valuep, data->value.bytes.data, size)) return -EFAULT; } From 01edf81ac8577278ecbd9371078bc85f0baa65ac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:37 +0200 Subject: [PATCH 742/791] ALSA: oss: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-6-tiwai@suse.de --- sound/core/oss/pcm_oss.c | 16 ++++++++-------- sound/core/oss/pcm_plugin.c | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 0924f1ff1ae7..bd19328dca9e 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -879,11 +879,11 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { @@ -909,7 +909,7 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, sformat_mask); - if ((__force int)sformat < 0 || + if (sformat < 0 || !snd_mask_test_format(sformat_mask, sformat)) { pcm_for_each_format(sformat) { if (snd_mask_test_format(sformat_mask, sformat) && @@ -921,7 +921,7 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) goto failure; } format_found: - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); if (err < 0) goto failure; @@ -930,9 +930,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); + snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -1875,7 +1875,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); for (fmt = 0; fmt < 32; ++fmt) { if (snd_mask_test(format_mask, fmt)) { - int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt); + int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f; } diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 5f4d6945a7df..acf5ca663ba8 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -272,13 +272,13 @@ static int snd_pcm_plug_formats(const struct snd_mask *mask, SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & lower_32_bits(linfmts)) formats.bits[0] |= lower_32_bits(linfmts); if (formats.bits[1] & upper_32_bits(linfmts)) formats.bits[1] |= upper_32_bits(linfmts); - return snd_mask_test(&formats, (__force int)format); + return snd_mask_test(&formats, format); } static const snd_pcm_format_t preferred_formats[] = { @@ -307,20 +307,20 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, { int i; - if (snd_mask_test(format_mask, (__force int)format)) + if (snd_mask_test(format_mask, format)) return format; if (!snd_pcm_plug_formats(format_mask, format)) - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; + snd_pcm_format_t best_format = -1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, (__force int)f)) + if (!snd_mask_test(format_mask, f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -334,21 +334,21 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, best = badness; } } - if ((__force int)best_format >= 0) + if (best_format >= 0) return best_format; else - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { snd_pcm_format_t format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, (__force int)format1)) + if (snd_mask_test(format_mask, format1)) return format1; } fallthrough; default: - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; } } } From a47bc1c7c473af0f5ca80a79b255c7f9ebf74898 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:38 +0200 Subject: [PATCH 743/791] ALSA: kunit: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-7-tiwai@suse.de --- sound/core/sound_kunit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index 84e337ecbddd..0376112cc67e 100644 --- a/sound/core/sound_kunit.c +++ b/sound/core/sound_kunit.c @@ -17,8 +17,8 @@ .name = #fmt, \ } -#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1) -#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1 +#define WRONG_FORMAT_1 (SNDRV_PCM_FORMAT_LAST + 1) +#define WRONG_FORMAT_2 -1 #define VALID_NAME "ValidName" #define NAME_W_SPEC_CHARS "In%v@1id name" From 93aa34ef917738fbb1eb649881152ed521bf8855 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:39 +0200 Subject: [PATCH 744/791] ALSA: aloop: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-8-tiwai@suse.de --- sound/drivers/aloop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 92ef821ddbeb..4e3ea23ca913 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1603,7 +1603,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST; + uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; uinfo->value.integer.step = 1; return 0; } @@ -1614,7 +1614,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol, struct loopback *loopback = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = - (__force int)loopback->setup[kcontrol->id.subdevice] + loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].format; return 0; } From ba4239b6d8e1b4e3b702b9f69d39806b1ca2a4a9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:40 +0200 Subject: [PATCH 745/791] ALSA: hda: Drop __force casts Now that the bitwise parameter definitions are gone for PCM and control parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-9-tiwai@suse.de --- sound/hda/common/codec.c | 2 +- sound/hda/core/device.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 641083c2376f..7d17d773cfbf 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -3379,7 +3379,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, for (; knew->name; knew++) { struct snd_kcontrol *kctl; int addr = 0, idx = 0; - if (knew->iface == (__force snd_ctl_elem_iface_t)-1) + if (knew->iface == -1) continue; /* skip this codec private value */ for (;;) { kctl = snd_ctl_new1(knew, codec); diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c index 160c8d0453b0..832494035f0a 100644 --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c @@ -765,7 +765,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor params_set_format(¶ms, snd_hdac_format_normalize(format)); snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT), - (__force unsigned int)subformat); + subformat); bits = snd_pcm_hw_params_bits(¶ms); if (maxbits) From 68edc3ded96bc3e1b8df639b5dc154e4a0e9e496 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:41 +0200 Subject: [PATCH 746/791] ALSA: asihpi: Drop __force cast Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop a superfluous cast. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-10-tiwai@suse.de --- sound/pci/asihpi/asihpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 4dbc79899c09..b78e96caef9d 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -280,7 +280,7 @@ static void print_hwparams(struct snd_pcm_substream *substream, snd_pcm_format_width(params_format(p)) / 8); } -#define INVALID_FORMAT (__force snd_pcm_format_t)(-1) +#define INVALID_FORMAT -1 static const snd_pcm_format_t hpi_to_alsa_formats[] = { INVALID_FORMAT, /* INVALID */ From 920b514c4328fb6deaab0c6beeb126fe3dc03fc5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:42 +0200 Subject: [PATCH 747/791] ALSA: emu10k1: Drop __force casts Now that the bitwise parameter definitions are gone for control parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-11-tiwai@suse.de --- sound/pci/emu10k1/emufx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 08e0556bf161..49cabb2eb2b7 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -990,7 +990,7 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, i < icode->gpr_list_control_count) { memset(gctl, 0, sizeof(*gctl)); id = &ctl->kcontrol->id; - gctl->id.iface = (__force int)id->iface; + gctl->id.iface = id->iface; strscpy(gctl->id.name, id->name, sizeof(gctl->id.name)); gctl->id.index = id->index; gctl->id.device = id->device; @@ -1156,7 +1156,7 @@ static void snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; if (high_res_gpr_volume) { @@ -1180,7 +1180,7 @@ static void snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; if (high_res_gpr_volume) { @@ -1205,7 +1205,7 @@ static void snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; @@ -1218,7 +1218,7 @@ static void snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; @@ -1542,7 +1542,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) * Process tone control */ ctl = &controls[nctl + 0]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; @@ -1551,7 +1551,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ctl->value[0] = ctl->value[1] = 20; ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[nctl + 1]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; @@ -2137,7 +2137,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) * Process tone control */ ctl = &controls[i + 0]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; @@ -2147,7 +2147,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->tlv = snd_emu10k1_bass_treble_db_scale; ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; From fde65373cb861edf560af50782b08a52b39e7585 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:43 +0200 Subject: [PATCH 748/791] ASoC: fsl: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Reviewed-by: Shengjiu Wang Acked-by: Mark Brown Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-12-tiwai@suse.de --- sound/soc/fsl/fsl-asoc-card.c | 2 +- sound/soc/fsl/fsl_asrc.c | 2 +- sound/soc/fsl/fsl_asrc_m2m.c | 10 +++++----- sound/soc/fsl/fsl_easrc.c | 2 +- sound/soc/fsl/fsl_qmc_audio.c | 8 ++++---- sound/soc/fsl/imx-card.c | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 709543308fe9..9dc46ab1d3e1 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -1118,7 +1118,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &asrc_fmt); - priv->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + priv->asrc_format = asrc_fmt; if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index f23c21032287..11748c65d5cc 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1358,7 +1358,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); - asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + asrc->asrc_format = asrc_fmt; if (ret) { ret = of_property_read_u32(np, "fsl,asrc-width", &width); if (ret) { diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c index 7d39378c0622..4bc40f328f58 100644 --- a/sound/soc/fsl/fsl_asrc_m2m.c +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -367,13 +367,13 @@ static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream, if (ret) return -EINVAL; - if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in) - pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format; + if (pcm_format_to_bits(params->codec.format) & cap.fmt_in) + pair->sample_format[IN] = params->codec.format; else return -EINVAL; - if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out) - pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format; + if (pcm_format_to_bits(params->codec.pcm_format) & cap.fmt_out) + pair->sample_format[OUT] = params->codec.pcm_format; else return -EINVAL; @@ -600,7 +600,7 @@ static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc, cap.rate_in, cap.rate_in_count * sizeof(__u32)); codec->descriptor[j].num_sample_rates = cap.rate_in_count; - codec->descriptor[j].formats = (__force __u32)k; + codec->descriptor[j].formats = k; codec->descriptor[j].pcm_formats = cap.fmt_out; codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0]; codec->descriptor[j].src.out_sample_rate_max = diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 8535ef844ce0..79eb2391058d 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2227,7 +2227,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); - easrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + easrc->asrc_format = asrc_fmt; if (ret) { dev_err(dev, "failed to asrc format\n"); return ret; diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index d0f644573f49..f27934cf49da 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -503,8 +503,8 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, return ret; } - access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | - 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED; + access = 1ULL << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | + 1ULL << SNDRV_PCM_ACCESS_RW_INTERLEAVED; ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { @@ -532,8 +532,8 @@ static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substrea return ret; } - access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | - 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + access = 1ULL << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | + 1ULL << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 43438af1e1c6..e3cb1438e837 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -531,7 +531,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_none(mask); - snd_mask_set(mask, (__force unsigned int)data->asrc_format); + snd_mask_set(mask, data->asrc_format); return 0; } @@ -684,7 +684,7 @@ static int imx_card_parse_of(struct imx_card_data *data) } ret = of_property_read_u32(args.np, "fsl,asrc-format", &asrc_fmt); - data->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + data->asrc_format = asrc_fmt; if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width); From 4effed8fe6d592aa0fb33ea913aaa437d902db85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:44 +0200 Subject: [PATCH 749/791] ASoC: Intel: avs: Drop __force cast Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop a superfluous cast. Acked-by: Cezary Rojewski Acked-by: Mark Brown Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-13-tiwai@suse.de --- sound/soc/intel/avs/probes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 099119ad28b3..74096236984a 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -144,7 +144,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); if (ret < 0) return ret; - bps = snd_pcm_format_physical_width((__force snd_pcm_format_t)params->codec.format); + bps = snd_pcm_format_physical_width(params->codec.format); if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); From 9543539d7ce26e246f15f9744372aaf27b0207b4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:45 +0200 Subject: [PATCH 750/791] ASoC: mediatek: Drop __force casts Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop those superfluous casts. Acked-by: Mark Brown Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-14-tiwai@suse.de --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 4 ++-- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 4 ++-- sound/soc/mediatek/mt8186/mt8186-mt6366.c | 2 +- sound/soc/mediatek/mt8188/mt8188-mt6359.c | 2 +- sound/soc/mediatek/mt8189/mt8189-nau8825.c | 2 +- sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 2 +- sound/soc/mediatek/mt8195/mt8195-mt6359.c | 4 ++-- sound/soc/mediatek/mt8196/mt8196-nau8825.c | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 983f3b91119a..aa3d1a588479 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -172,7 +172,7 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); @@ -184,7 +184,7 @@ static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 0bc1f11e17aa..dea7ad167a49 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -99,7 +99,7 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; @@ -112,7 +112,7 @@ static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c index 22123b087c3c..b68f474b63f4 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c @@ -387,7 +387,7 @@ static int mt8186_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, fmt); diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 55ebac0c3cef..75c90d1d165f 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -623,7 +623,7 @@ static int mt8188_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c index e849e7a649bc..5d652d0cf01e 100644 --- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c +++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c @@ -174,7 +174,7 @@ static int mt8189_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 91c57765ab57..8af8b0a366d5 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -382,7 +382,7 @@ static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 4d62bc654a58..fc293ca71502 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -387,7 +387,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -650,7 +650,7 @@ static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c index c9424786c53d..1d1dad86365b 100644 --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c @@ -180,7 +180,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; From 0c4ef23e66a715019e27e679502e2be3bc358f81 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Aug 2026 08:04:46 +0200 Subject: [PATCH 751/791] ASoC: meson: Drop __force cast Now that the bitwise parameter definitions are gone for PCM parameters, we don't have to cast with ugly __force prefix. Simply drop a superfluous cast. Acked-by: Mark Brown Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260812060557.80445-15-tiwai@suse.de --- sound/soc/meson/meson-codec-glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c index 2ff6066e1b6c..8773bf06e154 100644 --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -74,7 +74,7 @@ int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); data->params.rate_min = params_rate(params); data->params.rate_max = params_rate(params); - data->params.formats = 1ULL << (__force int) params_format(params); + data->params.formats = 1ULL << params_format(params); data->params.channels_min = params_channels(params); data->params.channels_max = params_channels(params); data->params.sig_bits = dai->driver->playback.sig_bits; From 931f4a1223e154fdea63071074cbaa633ebf394b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:23 +0700 Subject: [PATCH 752/791] ASoC: rockchip: rk3288_hdmi_analog: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rk3288_hdmi_analog.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index cf642a23c38a..d88d5ccfcdb2 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(card->dev, "SoC parse card name failed %d\n", ret); + if (ret) return ret; - } rk_dailink.codecs[0].of_node = of_parse_phandle(np, "rockchip,audio-codec", @@ -223,11 +221,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); - if (ret) { - dev_err(&pdev->dev, - "Unable to parse 'rockchip,routing' property\n"); + if (ret) return ret; - } snd_soc_card_set_drvdata(card, machine); From 06f5854050f56775977f9972b78a21806de001f0 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:24 +0700 Subject: [PATCH 753/791] ASoC: rockchip: rk3288_hdmi_analog: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() to prevent log spam when probe returns -EPROBE_DEFER. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rk3288_hdmi_analog.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index d88d5ccfcdb2..541163ed56fc 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -205,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) } ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); - if (ret) { - dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to get codec_dai_name\n"); rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", 0); From b5e4b1159743e72564030256949ab37108184ed8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:25 +0700 Subject: [PATCH 754/791] ASoC: rockchip: rockchip_i2s: Use dev_err_probe() for error handling Replace dev_err() with dev_err_probe() to prevent log spam when probe returns -EPROBE_DEFER. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 64c90316fa02..3e8d07b3fc1e 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) /* try to prepare related clocks */ i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); - if (IS_ERR(i2s->hclk)) { - dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); - return PTR_ERR(i2s->hclk); - } + if (IS_ERR(i2s->hclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), + "Can't retrieve i2s bus clock\n"); i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); - if (IS_ERR(i2s->mclk)) { - dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); - return PTR_ERR(i2s->mclk); - } + if (IS_ERR(i2s->mclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), + "Can't retrieve i2s master clock\n"); regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) From c1bf7ae0c87a42cb81608f40aa99535119171915 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:26 +0700 Subject: [PATCH 755/791] ASoC: rockchip: rockchip_i2s: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 3e8d07b3fc1e..354430f916f9 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -829,16 +829,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) &rockchip_i2s_component, dai, 1); - if (ret) { - dev_err(&pdev->dev, "Could not register DAI\n"); + if (ret) return ret; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Could not register PCM\n"); + if (ret) return ret; - } return 0; } From be4f82d15235ad4341c6529ff6cca048c26eb5a4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:27 +0700 Subject: [PATCH 756/791] ASoC: rockchip: rockchip_i2s: Propagate -EPROBE_DEFER from devm_pinctrl_get() Return -EPROBE_DEFER from devm_pinctrl_get() instead of ignoring it and continuing probe. This allows the driver to be reprobed once the pinctrl provider becomes available. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 354430f916f9..261f36d4c2fd 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -790,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->bclk_ratio = 64; i2s->pinctrl = devm_pinctrl_get(&pdev->dev); - if (!IS_ERR(i2s->pinctrl)) { + if (IS_ERR(i2s->pinctrl)) { + if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); + } else { i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); if (!IS_ERR_OR_NULL(i2s->bclk_on)) { i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); @@ -799,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) return -EINVAL; } } - } else { - dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); } i2s_pinctrl_select_bclk_off(i2s); From 334d5ea7582b2e8bfc4cc3917941e88f2204a86d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:28 +0700 Subject: [PATCH 757/791] ASoC: rockchip: i2s-tdm: Inline PTR_ERR() in dev_err_probe() Pass PTR_ERR() directly to dev_err_probe() and avoid assigning it to the local variable first. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s_tdm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index e6229a325ffe..c9ad769a4072 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "tx-m"); if (IS_ERR(i2s_tdm->tx_reset)) { - ret = PTR_ERR(i2s_tdm->tx_reset); - return dev_err_probe(i2s_tdm->dev, ret, + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), "Error in tx-m reset control\n"); } i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "rx-m"); if (IS_ERR(i2s_tdm->rx_reset)) { - ret = PTR_ERR(i2s_tdm->rx_reset); - return dev_err_probe(i2s_tdm->dev, ret, + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), "Error in rx-m reset control\n"); } From 9fea4805ed95c0ebabd57e02b29cf06a1593b728 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:29 +0700 Subject: [PATCH 758/791] ASoC: rockchip: i2s-tdm: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s_tdm.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index c9ad769a4072..5ef7f109706d 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1361,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_tdm_component, i2s_tdm->dai, 1); - - if (ret) { - dev_err(&pdev->dev, "Could not register DAI\n"); + if (ret) goto err_suspend; - } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Could not register PCM\n"); + if (ret) goto err_suspend; - } return 0; From de983524dcbec5eeaae7fde001fbe5ab9c02f28c Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:30 +0700 Subject: [PATCH 759/791] ASoC: rockchip: rockchip_max98090: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_max98090.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 075d0990a126..426506a8e18e 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) /* Parse card name. */ ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(&pdev->dev, - "Soc parse card name failed %d\n", ret); + if (ret) return ret; - } /* register the soc card */ ret = devm_snd_soc_register_card(&pdev->dev, card); From 7d3f7eb890f131e5222a319958576d3271dfe0bd Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:31 +0700 Subject: [PATCH 760/791] ASoC: rockchip: rockchip_pdm: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_pdm.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 115e90d3bbfe..343fda478901 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) &rockchip_pdm_component, &rockchip_pdm_dai, 1); - if (ret) { - dev_err(&pdev->dev, "could not register dai: %d\n", ret); + if (ret) goto err_suspend; - } rockchip_pdm_rxctrl(pdm, 0); @@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) goto err_suspend; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "could not register pcm: %d\n", ret); + if (ret) goto err_suspend; - } return 0; From b300f1313b00b69315d6a016beb4ec227c10a2c3 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:32 +0700 Subject: [PATCH 761/791] ASoC: rockchip: rockchip_rt5645: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_rt5645.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 590b64b362f6..0432eeabc64e 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; ret = snd_soc_of_parse_card_name(card, "rockchip,model"); - if (ret) { - dev_err(&pdev->dev, - "Soc parse card name failed %d\n", ret); + if (ret) goto put_cpu_of_node; - } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { From c97bdbf862c6ddd201a58ccf26b3a84519e20431 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:33 +0700 Subject: [PATCH 762/791] ASoC: rockchip: rockchip_sai: Propagate -EPROBE_DEFER from IRQ lookup Return -EPROBE_DEFER from platform_get_irq_optional() so the driver is re-probed when the interrupt resource becomes available instead of continuing probe without an IRQ. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-12-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 585e89f61f0d..522b4f4f6a7a 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1428,6 +1428,8 @@ static int rockchip_sai_probe(struct platform_device *pdev) "Failed to initialize regmap\n"); irq = platform_get_irq_optional(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); From 7a2229ef7cdb9e8e7dd15cdbc2974910f54c343d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:34 +0700 Subject: [PATCH 763/791] ASoC: rockchip: rockchip_sai: Return the original error code Return the original error code directly and drop the redundant error message since the called function already reports the failure. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-13-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 522b4f4f6a7a..1fdec12a0d4e 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1434,8 +1434,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to request irq %d\n", irq); + return ret; } else { dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); } @@ -1458,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) ret = rockchip_sai_parse_paths(sai, node); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); + return ret; /* * From here on, all register accesses need to be wrapped in From 26c9632c33f72735e7a9c88707a2014b50e7e281 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:35 +0700 Subject: [PATCH 764/791] ASoC: rockchip: rockchip_sai: Drop redundant probe error messages Remove the probe error messages to avoid duplicate error reporting, since the error is already reported by the called functions. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-14-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 1fdec12a0d4e..30b5e71d0937 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1472,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); + if (ret) goto err_runtime_suspend; - } ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_sai_component, dai, 1); - if (ret) { - dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + if (ret) goto err_runtime_suspend; - } pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_put(&pdev->dev); From 5784ef446847cc01e43f3bb2ce63d3a72a9301ce Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 6 Aug 2026 12:21:36 +0700 Subject: [PATCH 765/791] ASoC: rockchip: spdif: Return the original error code Return the original error code directly and drop the redundant error message since the called function already reports the failure. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260806052136.21034-15-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 7f15bc7f8f35..2d53efd5dd2e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); + return ret; ret = devm_snd_soc_register_component(&pdev->dev, &rk_spdif_component, &rk_spdif_dai, 1); if (ret) - return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); + return ret; return 0; } From 4c51a83a4fe85dd58a56bed491740ecefcf8a110 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 10 Aug 2026 18:35:29 +0900 Subject: [PATCH 766/791] ASoC: fsl-asoc-card: Drop mclk management for nau8822 commit 93f12a7568269 ("ASoC: nau8822: add MCLK support") added MCLK handling directly in the nau8822 codec driver. The machine driver no longer needs to acquire and enable the codec MCLK on its behalf. Remove MCLK management in this machine driver that was introduced by commit 1075df4bdeb32 ("ASoC: fsl-asoc-card: add nau8822 support"). This avoids a potential double-enable and removes clock resource management from the machine driver where it does not belong. Additionally, the sound card may be unbound and rebound multiple times during its lifetime. Managing a codec clock resource in the machine driver would require careful cleanup in the card remove path to avoid reference count leaks. Leaving clock management to the codec driver, which has the same lifetime as the codec device, is the correct ownership model. The nau8822 compatible entry, DAI name, and PLL/FLL clock ID configuration are kept unchanged. Assisted-by: VeroCoder:claude-sonnet-4-6 Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260810093834.1511749-2-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 709543308fe9..4c245aaf2847 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { /** * struct codec_priv - CODEC private data - * @mclk: Main clock of the CODEC * @mclk_freq: Clock rate of MCLK * @free_freq: Clock rate of MCLK for hw_free() * @mclk_id: MCLK (or main clock) id for set_sysclk() @@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { * to stay within PLL frequency limits */ struct codec_priv { - struct clk *mclk; unsigned long mclk_freq; unsigned long free_freq; u32 mclk_id; @@ -680,9 +678,6 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) dev_err(dev, "failed to set sysclk in %s\n", __func__); return ret; } - - if (!IS_ERR_OR_NULL(codec_priv->mclk)) - clk_prepare_enable(codec_priv->mclk); } return 0; @@ -933,8 +928,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - if (codec_dev[0]) - priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { codec_dai_name[0] = "wm8904-hifi"; priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; From 76408e27e60f8deb087f7d84e866d7f03ac750f1 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 10 Aug 2026 18:35:30 +0900 Subject: [PATCH 767/791] ASoC: fsl-asoc-card: Move static compatible data to platform data Replace the large if/else chain of of_device_is_compatible() calls in probe() with a table-driven approach. Each compatible string now has a corresponding static const struct fsl_asoc_card_pdata descriptor stored in the of_device_id .data field. probe() calls of_device_get_match_data() once and reads all per-compatible configuration from the returned pointer: - DAI format - CPU SYSCLK direction and ratio overrides - TDM slot width - Codec DAI name, MCLK id, FLL/PLL ids, PLL S24 ratio - playback_only / capture_only direction restrictions - Default DAPM route table - Excluded PCM format mask (for SAI + WM8960/WM8962) - Optional probe_init callback (SPDIF multi-codec discovery) - Optional codec_init callback (codec-specific post-probe logic) This patch is a pure refactoring, no functional change is intended. Assisted-by: VeroCoder:claude-sonnet-4-6 Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260810093834.1511749-3-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 423 +++++++++++++++++++++++----------- 1 file changed, 287 insertions(+), 136 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 4c245aaf2847..10e432c916b8 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -107,12 +107,58 @@ struct cpu_priv { u32 slot_num; }; +struct fsl_asoc_card_priv; + +/* + * struct fsl_asoc_card_pdata - per-compatible static card description + * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) + * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) + * @slot_width: TDM slot width (0 = not TDM) + * @codec_dai_name: name of the codec DAI + * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec + * @codec_fll_id: FLL id; only valid when has_pll is true + * @codec_pll_id: PLL id; only valid when has_pll is true + * @codec_pll_ratio_s24: PLL output ratio for S24_LE + * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid + * @dai_fmt: DAI format flags + * @playback_only: restrict card to playback direction + * @capture_only: restrict card to capture direction + * @dapm_routes: DAPM route table override + * @num_dapm_routes: number of entries in dapm_routes + * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) + * @codec_init: codec-specific init run after mclk_freq is populated + * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) + */ +struct fsl_asoc_card_pdata { + u32 sysclk_dir[2]; + u32 sysclk_ratio[2]; + u32 slot_width; + const char *codec_dai_name; + u32 codec_mclk_id; + int codec_fll_id; + int codec_pll_id; + int codec_pll_ratio_s24; + bool has_pll; + bool playback_only; + bool capture_only; + u32 dai_fmt; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + u64 exclude_format; + int (*codec_init)(struct fsl_asoc_card_priv *priv); + int (*probe_init)(struct device_node *codec_np[], + struct device_node *cpu_np, + const char *codec_dai_name[], + struct fsl_asoc_card_priv *priv); +}; + /** * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data * @dai_link: DAI link structure including normal one and DPCM link * @hp_jack: Headphone Jack structure * @mic_jack: Microphone Jack structure * @pdev: platform device pointer + * @pdata: pointer to the per-compatible card platform data * @codec_priv: CODEC private data * @cpu_priv: CPU private data * @card: ASoC card structure @@ -133,6 +179,7 @@ struct fsl_asoc_card_priv { struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; struct platform_device *pdev; + const struct fsl_asoc_card_pdata *pdata; struct codec_priv codec_priv[2]; struct cpu_priv cpu_priv; struct snd_soc_card card; @@ -607,6 +654,186 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], return 0; } +static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) +{ + unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; + + priv->cpu_priv.sysclk_freq[TX] = mclk_freq; + priv->cpu_priv.sysclk_freq[RX] = mclk_freq; + + priv->constraint_channels = &cs42888_channel_constraints; + if (mclk_freq % 12288000 == 0) + priv->constraint_rates = &cs42888_rate_48k_constraints; + else if (mclk_freq % 11289600 == 0) + priv->constraint_rates = &cs42888_rate_44k_constraints; + else + dev_warn(&priv->pdev->dev, + "Unknown MCLK frequency %lu, no rate constraints\n", + mclk_freq); + + return 0; +} + +static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) +{ + priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; + return 0; +} + +static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { + .codec_dai_name = "cs42888", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, + .slot_width = 32, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .codec_init = fsl_asoc_card_cs42888_codec_init, +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { + .codec_dai_name = "cs4271-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = CS427x_SYSCLK_MCLK, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { + .codec_dai_name = "sgtl5000", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = SGTL5000_SYSCLK, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { + .codec_dai_name = "tlv320aic32x4-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { + .codec_dai_name = "tlv320dac31xx-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { + .codec_dai_name = "wm8962", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8962_SYSCLK_MCLK, + .has_pll = true, + .codec_fll_id = WM8962_SYSCLK_FLL, + .codec_pll_id = WM8962_FLL, + /* + * WM8962 has same BCLK generation limitations as WM8960. + * See WM8960 section for detailed explanation. + */ + .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { + .codec_dai_name = "wm8960-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .has_pll = true, + .codec_fll_id = WM8960_SYSCLK_AUTO, + .codec_pll_id = WM8960_SYSCLK_AUTO, + /* + * WM8960 in master mode cannot generate exact 1.92 MHz BCLK + * required for S20_3LE (48kHz x 2ch x 20bit). Closest available + * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. + * + * In SAI master mode, SAI derive BCLK from MCLK using integer + * dividers only. S20_3LE requires non-integer divider ratios + * with standard MCLK frequencies. For example, 48kHz stereo + * needs 1.920 MHz BCLK, which requires a divider of 6.4 from + * 12.288 MHz MCLK (not an integer). + */ + .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { + .codec_dai_name = "ac97-hifi", + .dai_fmt = SND_SOC_DAIFMT_AC97, + .dapm_routes = audio_map_ac97, + .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { + .codec_dai_name = "fsl-mqs-dai", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_NB_NF, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { + .codec_dai_name = "wm8524-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ + .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, + .sysclk_ratio = { 0, 256 }, + .slot_width = 32, + .playback_only = true, + .dapm_routes = audio_map_tx, + .num_dapm_routes = ARRAY_SIZE(audio_map_tx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { + .codec_dai_name = "si476x-codec", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, + .dapm_routes = audio_map_rx, + .num_dapm_routes = ARRAY_SIZE(audio_map_rx), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { + .codec_dai_name = "wm8994-aif1", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8994_FLL_SRC_MCLK1, + .has_pll = true, + .codec_fll_id = WM8994_SYSCLK_FLL1, + .codec_pll_id = WM8994_FLL1, + .codec_init = fsl_asoc_card_wm8958_codec_init, +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { + .codec_dai_name = "nau8822-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = NAU8822_CLK_MCLK, + .has_pll = true, + .codec_fll_id = NAU8822_CLK_PLL, + .codec_pll_id = NAU8822_CLK_PLL, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { + .codec_dai_name = "wm8904-hifi", + .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, + .codec_mclk_id = WM8904_FLL_MCLK, + .has_pll = true, + .codec_fll_id = WM8904_CLK_FLL, + .codec_pll_id = WM8904_FLL_MCLK, + .codec_pll_ratio_s24 = 192, + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { + .codec_dai_name = "spdif", + .dai_fmt = DAI_FMT_BASE, + .probe_init = fsl_asoc_card_spdif_init, +}; + static int hp_jack_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -694,6 +921,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct device_node *frameprovider = NULL; struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; + const struct fsl_asoc_card_pdata *pdata; struct device *codec_dev[2] = { NULL, NULL }; struct snd_soc_dai_link_component *dlc; const char *codec_dai_name[2]; @@ -709,6 +937,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->pdev = pdev; + pdata = of_device_get_match_data(&pdev->dev); + if (!pdata) + return -EINVAL; + priv->pdata = pdata; + cpu_np = of_parse_phandle(np, "audio-cpu", 0); /* Give a chance to old DT bindings */ if (!cpu_np) @@ -767,6 +1000,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } } + if (pdata->codec_init) { + ret = pdata->codec_init(priv); + if (ret) + goto asrc_fail; + } + /* Default sample rate and format, will be updated in hw_params() */ priv->sample_rate = 44100; priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; @@ -818,131 +1057,43 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Diversify the card configurations */ - if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { - codec_dai_name[0] = "cs42888"; - priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; - priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.slot_width = 32; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->constraint_channels = &cs42888_channel_constraints; - if (priv->codec_priv[0].mclk_freq % 12288000 == 0) - priv->constraint_rates = &cs42888_rate_48k_constraints; - else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) - priv->constraint_rates = &cs42888_rate_44k_constraints; - else - dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", - priv->codec_priv[0].mclk_freq); - } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { - codec_dai_name[0] = "cs4271-hifi"; - priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { - codec_dai_name[0] = "sgtl5000"; - priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { - codec_dai_name[0] = "tlv320aic32x4-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { - codec_dai_name[0] = "tlv320dac31xx-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; + priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; + priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; + priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; + priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; + priv->cpu_priv.slot_width = pdata->slot_width; + + codec_dai_name[0] = pdata->codec_dai_name; + priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; + if (pdata->has_pll) { + priv->codec_priv[0].fll_id = pdata->codec_fll_id; + priv->codec_priv[0].pll_id = pdata->codec_pll_id; + } + if (pdata->codec_pll_ratio_s24) + priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; + + if (pdata->playback_only) { priv->dai_link[1].playback_only = 1; priv->dai_link[2].playback_only = 1; - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { - codec_dai_name[0] = "wm8962"; - priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; - priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; - priv->codec_priv[0].pll_id = WM8962_FLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - /* - * WM8962 has same BCLK generation limitations as WM8960. - * See WM8960 section for detailed explanation. - */ - if (of_node_name_eq(cpu_np, "sai")) - priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { - codec_dai_name[0] = "wm8960-hifi"; - priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; - priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - /* - * WM8960 in master mode cannot generate exact 1.92 MHz BCLK - * required for S20_3LE (48kHz × 2ch × 20bit). Closest available - * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. - * - * In SAI master mode, SAI derive BCLK from MCLK using integer - * dividers only. S20_3LE requires non-integer divider ratios - * with standard MCLK frequencies. For example, 48kHz stereo - * needs 1.920 MHz BCLK, which requires a divider of 6.4 from - * 12.288 MHz MCLK (not an integer). - */ - if (of_node_name_eq(cpu_np, "sai")) - priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; - } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { - codec_dai_name[0] = "ac97-hifi"; - priv->dai_fmt = SND_SOC_DAIFMT_AC97; - priv->card.dapm_routes = audio_map_ac97; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); - } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { - codec_dai_name[0] = "fsl-mqs-dai"; - priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBC_CFC | - SND_SOC_DAIFMT_NB_NF; - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { - codec_dai_name[0] = "wm8524-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; - priv->cpu_priv.slot_width = 32; - priv->card.dapm_routes = audio_map_tx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); - priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; - priv->cpu_priv.sysclk_ratio[TX] = 256; - } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { - codec_dai_name[0] = "si476x-codec"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; - priv->card.dapm_routes = audio_map_rx; - priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { - codec_dai_name[0] = "wm8994-aif1"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; - priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; - priv->codec_priv[0].pll_id = WM8994_FLL1; - priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; - priv->card.dapm_routes = NULL; - priv->card.num_dapm_routes = 0; - } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { - codec_dai_name[0] = "nau8822-hifi"; - priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; - priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; - priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { - codec_dai_name[0] = "wm8904-hifi"; - priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; - priv->codec_priv[0].fll_id = WM8904_CLK_FLL; - priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; - priv->codec_priv[0].pll_ratio_s24 = 192; - priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { - ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); + } + if (pdata->capture_only) { + priv->dai_link[1].capture_only = 1; + priv->dai_link[2].capture_only = 1; + } + + priv->dai_fmt = pdata->dai_fmt; + + priv->card.dapm_routes = pdata->dapm_routes; + priv->card.num_dapm_routes = pdata->num_dapm_routes; + + if (pdata->exclude_format && of_node_name_eq(cpu_np, "sai")) + priv->exclude_format = pdata->exclude_format; + + if (pdata->probe_init) { + ret = pdata->probe_init(codec_np, cpu_np, + codec_dai_name, priv); if (ret) goto asrc_fail; - } else { - dev_err(&pdev->dev, "unknown Device Tree compatible\n"); - ret = -EINVAL; - goto asrc_fail; } /* @@ -1179,21 +1330,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } static const struct of_device_id fsl_asoc_card_dt_ids[] = { - { .compatible = "fsl,imx-audio-ac97", }, - { .compatible = "fsl,imx-audio-cs42888", }, - { .compatible = "fsl,imx-audio-cs427x", }, - { .compatible = "fsl,imx-audio-tlv320aic32x4", }, - { .compatible = "fsl,imx-audio-tlv320aic31xx", }, - { .compatible = "fsl,imx-audio-sgtl5000", }, - { .compatible = "fsl,imx-audio-wm8962", }, - { .compatible = "fsl,imx-audio-wm8960", }, - { .compatible = "fsl,imx-audio-mqs", }, - { .compatible = "fsl,imx-audio-wm8524", }, - { .compatible = "fsl,imx-audio-si476x", }, - { .compatible = "fsl,imx-audio-wm8958", }, - { .compatible = "fsl,imx-audio-nau8822", }, - { .compatible = "fsl,imx-audio-wm8904", }, - { .compatible = "fsl,imx-audio-spdif", }, + { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, + { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, + { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, + { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, + { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, + { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, + { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, + { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, + { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, + { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, + { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, + { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, + { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, + { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, + { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); From de27e0cadcce09053a8ae07575ecd5a327d8fec6 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 10 Aug 2026 18:35:31 +0900 Subject: [PATCH 768/791] ASoC: fsl-asoc-card: Move bound-component setup to late_probe Move all operations that require bound codec and CPU DAI components out of probe() and into late_probe(), which is the correct place for them now that ASoC supports deferrable card binding. late_probe() may be called multiple times after an unbind/rebind cycle, so every initialization step is guarded accordingly. Three new helpers are introduced: - fsl_asoc_card_init_cpu() CPU DAI-specific setup. Previously done in probe() while CPU DAI component maybe not ready. - fsl_asoc_card_init_codecs() Reads codec MCLK rates from the bound component devices, invokes the per-compatible pdata->codec_init callback if present. - fsl_asoc_card_init_jack() Registers headphone and microphone jacks. The call site of codec_init callbacks moves from probe() to fsl_asoc_card_init_codecs(), which runs in late_probe() after the bound codec device is known. This makes sure codecs can get proper MCLK. The old card-name fallback depended on codec_dev_name[], which required looking up the codec device in probe(). This is no longer valid under deferrable card binding because the codec component may not have probed yet. Since the DT binding requires "model", remove the fallback and fail with a clear error. Assisted-by: VeroCoder:claude-sonnet-4-6 Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260810093834.1511749-4-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 324 ++++++++++++++++++++-------------- 1 file changed, 191 insertions(+), 133 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 10e432c916b8..3532bf6d507e 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -658,8 +658,14 @@ static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) { unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; - priv->cpu_priv.sysclk_freq[TX] = mclk_freq; - priv->cpu_priv.sysclk_freq[RX] = mclk_freq; + /* + * Set CPU sysclk frequency from codec MCLK only if not already + * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). + */ + if (!priv->cpu_priv.sysclk_freq[TX]) + priv->cpu_priv.sysclk_freq[TX] = mclk_freq; + if (!priv->cpu_priv.sysclk_freq[RX]) + priv->cpu_priv.sysclk_freq[RX] = mclk_freq; priv->constraint_channels = &cs42888_channel_constraints; if (mclk_freq % 12288000 == 0) @@ -868,17 +874,173 @@ static struct notifier_block mic_jack_nb = { .notifier_call = mic_jack_event, }; -static int fsl_asoc_card_late_probe(struct snd_soc_card *card) +/* + * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. + * + * Called from late_probe() when the CPU DAI component is guaranteed bound. + */ +static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) { struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_pcm_runtime *rtd = list_first_entry( - &card->rtd_list, struct snd_soc_pcm_runtime, list); + struct device_node *np = priv->pdev->dev.of_node; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + const char *comp_drv_name = cpu_dai->component->driver->name; + struct device *dev = card->dev; + int ret; + + if (!strcmp(comp_drv_name, "fsl-ssi")) { + /* Only SSI needs to configure AUDMUX */ + ret = fsl_asoc_card_audmux_init(np, priv); + if (ret) { + dev_err(dev, "failed to init audmux\n"); + return ret; + } + } else if (!strcmp(comp_drv_name, "fsl-esai")) { + struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); + + if (!IS_ERR(esai_clk)) { + priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); + priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); + clk_put(esai_clk); + } else { + dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); + } + + priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; + priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; + } else if (!strcmp(comp_drv_name, "fsl-sai")) { + priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; + priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; + + if (priv->pdata->exclude_format) + priv->exclude_format = priv->pdata->exclude_format; + } + + return 0; +} + +/* + * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. + * + * Called from late_probe() after all components are bound. + */ +static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + const struct fsl_asoc_card_pdata *pdata = priv->pdata; struct snd_soc_dai *codec_dai; struct codec_priv *codec_priv; struct device *dev = card->dev; int codec_idx; int ret; + /* Read MCLK rate from each bound codec component */ + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); + + codec_priv = &priv->codec_priv[codec_idx]; + if (!IS_ERR(codec_clk)) { + codec_priv->mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } + } + + if (pdata->codec_init) { + ret = pdata->codec_init(priv); + if (ret) + return ret; + } + + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; + + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set sysclk in %s\n", __func__); + return ret; + } + } + + return 0; +} + +static void fsl_asoc_card_free_jack(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + + if (priv->hp_jack.gpio.desc) { + snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); + snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); + priv->hp_jack.gpio.desc = NULL; + } + + if (priv->mic_jack.gpio.desc) { + snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); + snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); + priv->mic_jack.gpio.desc = NULL; + } +} + +/* + * fsl_asoc_card_init_jack - register optional headphone and mic jacks. + * + * Called from late_probe() once per card bind cycle. + */ +static int fsl_asoc_card_init_jack(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct device_node *np = priv->pdev->dev.of_node; + int ret; + + /* + * Properties "hp-det-gpios" and "mic-det-gpios" are optional. + * simple_util_init_jack() checks for the GPIO property and + * does nothing if it is absent. + */ + if (of_property_present(np, "hp-det-gpios") || + of_property_present(np, "hp-det-gpio") /* deprecated */) { + ret = simple_util_init_jack(card, &priv->hp_jack, + 1, NULL, "Headphone Jack"); + if (ret) + return ret; + + snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); + } + + if (of_property_present(np, "mic-det-gpios") || + of_property_present(np, "mic-det-gpio") /* deprecated */) { + ret = simple_util_init_jack(card, &priv->mic_jack, + 0, NULL, "Mic Jack"); + if (ret) + return ret; + + snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); + } + + return 0; +} + +static int fsl_asoc_card_late_probe(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_pcm_runtime *rtd; + int ret; + + /* Use the first rtd which carries the CPU+codec DAIs */ + rtd = list_first_entry(&card->rtd_list, + struct snd_soc_pcm_runtime, list); + + ret = fsl_asoc_card_init_jack(card); + if (ret) + goto jack_fail; + + ret = fsl_asoc_card_init_cpu(card, rtd); + if (ret) + goto jack_fail; + if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; @@ -896,16 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) return 0; } - for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { - codec_priv = &priv->codec_priv[codec_idx]; + ret = fsl_asoc_card_init_codecs(card, rtd); + if (ret) + goto jack_fail; - ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, - codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set sysclk in %s\n", __func__); - return ret; - } - } + return 0; + +jack_fail: + fsl_asoc_card_free_jack(card); + return ret; +} + +static int fsl_asoc_card_card_remove(struct snd_soc_card *card) +{ + fsl_asoc_card_free_jack(card); return 0; } @@ -919,13 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *asrc_pdev = NULL; struct device_node *bitclkprovider = NULL; struct device_node *frameprovider = NULL; - struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; const struct fsl_asoc_card_pdata *pdata; - struct device *codec_dev[2] = { NULL, NULL }; struct snd_soc_dai_link_component *dlc; - const char *codec_dai_name[2]; - const char *codec_dev_name[2]; + const char *codec_dai_name[2] = { NULL, NULL }; u32 asrc_fmt = 0; int codec_idx; u32 width; @@ -938,8 +1101,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->pdev = pdev; pdata = of_device_get_match_data(&pdev->dev); - if (!pdata) + if (!pdata) { + dev_err(&pdev->dev, "unknown Device Tree compatible\n"); return -EINVAL; + } priv->pdata = pdata; cpu_np = of_parse_phandle(np, "audio-cpu", 0); @@ -954,58 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - cpu_pdev = of_find_device_by_node(cpu_np); - if (!cpu_pdev) { - dev_err(&pdev->dev, "failed to find CPU DAI device\n"); - ret = -EINVAL; - goto fail; - } - codec_np[0] = of_parse_phandle(np, "audio-codec", 0); codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - for (codec_idx = 0; codec_idx < 2; codec_idx++) { - if (codec_np[codec_idx]) { - struct platform_device *codec_pdev; - struct i2c_client *codec_i2c; - - codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); - if (codec_i2c) { - codec_dev[codec_idx] = &codec_i2c->dev; - codec_dev_name[codec_idx] = codec_i2c->name; - } - if (!codec_dev[codec_idx]) { - codec_pdev = of_find_device_by_node(codec_np[codec_idx]); - if (codec_pdev) { - codec_dev[codec_idx] = &codec_pdev->dev; - codec_dev_name[codec_idx] = codec_pdev->name; - } - } - } - } - asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); - /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - for (codec_idx = 0; codec_idx < 2; codec_idx++) { - if (codec_dev[codec_idx]) { - struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); - - if (!IS_ERR(codec_clk)) { - priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); - } - } - } - - if (pdata->codec_init) { - ret = pdata->codec_init(priv); - if (ret) - goto asrc_fail; - } - /* Default sample rate and format, will be updated in hw_params() */ priv->sample_rate = 44100; priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; @@ -1086,9 +1206,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = pdata->dapm_routes; priv->card.num_dapm_routes = pdata->num_dapm_routes; - if (pdata->exclude_format && of_node_name_eq(cpu_np, "sai")) - priv->exclude_format = pdata->exclude_format; - if (pdata->probe_init) { ret = pdata->probe_init(codec_np, cpu_np, codec_dai_name, priv); @@ -1139,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) of_node_put(bitclkprovider); of_node_put(frameprovider); - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] - && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { - dev_dbg(&pdev->dev, "failed to find codec device\n"); - ret = -EPROBE_DEFER; - goto asrc_fail; - } - - /* Common settings for corresponding Freescale CPU DAI driver */ - if (of_node_name_eq(cpu_np, "ssi")) { - /* Only SSI needs to configure AUDMUX */ - ret = fsl_asoc_card_audmux_init(np, priv); - if (ret) { - dev_err(&pdev->dev, "failed to init audmux\n"); - goto asrc_fail; - } - } else if (of_node_name_eq(cpu_np, "esai")) { - struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); - - if (!IS_ERR(esai_clk)) { - priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); - priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); - clk_put(esai_clk); - } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto asrc_fail; - } - - priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; - priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; - } else if (of_node_name_eq(cpu_np, "sai")) { - priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; - priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; - } - /* Initialize sound card */ priv->card.dev = &pdev->dev; priv->card.owner = THIS_MODULE; ret = snd_soc_of_parse_card_name(&priv->card, "model"); if (ret) { - snprintf(priv->name, sizeof(priv->name), "%s-audio", - fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); - priv->card.name = priv->name; + /* + * "model" is required by the DT binding. Enforce it here so + * the driver fails with a clear message. + */ + dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); + goto asrc_fail; } priv->card.dai_link = priv->dai_link; priv->card.late_probe = fsl_asoc_card_late_probe; + priv->card.remove = fsl_asoc_card_card_remove; priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); @@ -1290,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } - /* - * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and - * simple_util_init_jack() uses these properties for creating - * Headphone Jack and Microphone Jack. - * - * The notifier is initialized in snd_soc_card_jack_new(), then - * snd_soc_jack_notifier_register can be called. - */ - if (of_property_present(np, "hp-det-gpios") || - of_property_present(np, "hp-det-gpio") /* deprecated */) { - ret = simple_util_init_jack(&priv->card, &priv->hp_jack, - 1, NULL, "Headphone Jack"); - if (ret) - goto asrc_fail; - - snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); - } - - if (of_property_present(np, "mic-det-gpios") || - of_property_present(np, "mic-det-gpio") /* deprecated */) { - ret = simple_util_init_jack(&priv->card, &priv->mic_jack, - 0, NULL, "Mic Jack"); - if (ret) - goto asrc_fail; - - snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); - } - asrc_fail: of_node_put(asrc_np); of_node_put(codec_np[0]); of_node_put(codec_np[1]); - put_device(&cpu_pdev->dev); fail: of_node_put(cpu_np); From 0196c4b4f82beec2114ee3c13888314ca551e32a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 10 Aug 2026 21:19:25 -0700 Subject: [PATCH 769/791] ASoC: amd: acp: pass audio_drv_data to dma_irq_handler The IRQ handler only needs the audio_drv_data, so pass it directly as the request_irq argument instead of the device pointer and a dev_get_drvdata() lookup. Assisted-by: opencode:deepseek-v4-flash-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260811041925.25016-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 2b8848b6df2b..ee75737202dc 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) { u16 dscr_idx; u32 intr_flag, ext_intr_status; - struct audio_drv_data *irq_data; + struct audio_drv_data *irq_data = arg; void __iomem *acp_mmio; - struct device *dev = arg; bool valid_irq = false; - irq_data = dev_get_drvdata(dev); acp_mmio = irq_data->acp_mmio; ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); @@ -1294,7 +1292,7 @@ static int acp_audio_probe(struct platform_device *pdev) return irq; status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, - 0, "ACP_IRQ", &pdev->dev); + 0, "ACP_IRQ", audio_drv_data); if (status) { dev_err(&pdev->dev, "ACP IRQ request failed\n"); return status; From 4cc25cdd3cffa475edb8dec8199b3227038ebcfb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Aug 2026 16:42:16 +0200 Subject: [PATCH 770/791] ALSA: seq: midi: Optimize event_input locking with RCU The recent fix for serializing the output teardown introduced a spinlock invocation at every MIDI output event via event_process_midi. Since this is a hot path, let's do performance optimization with RCU. The new output_substream __rcu pointer is published via rcu_assign_pointer() in midisynth_use() after output_rfile is set, and cleared in midisynth_unuse() before the resource teardown. event_process_midi() reads it under rcu_read_lock() and bumps output_use_lock inside that section, which is necessary to close the window between the pointer dereference and the refcount increment. midisynth_unuse() calls synchronize_rcu() before snd_use_lock_sync(): this guarantees that any reader who obtained a non-NULL pointer has already called atomic_inc (output_use_lock), so the subsequent snd_use_lock_sync() sees the correct in-flight count. Fixes: ef7607ab1c8a ("ALSA: seq: midi: Serialize output teardown with event_input") Link: https://patch.msgid.link/20260813144224.753399-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/seq/seq_midi.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 2eb12199c92f..c2f89aee1914 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -43,8 +43,8 @@ struct seq_midisynth { int device; int subdevice; struct snd_rawmidi_file input_rfile; - spinlock_t output_lock; /* protects output_rfile publication */ snd_use_lock_t output_use_lock; /* in-flight event_input users */ + struct snd_rawmidi_substream __rcu *output_substream; struct snd_rawmidi_file output_rfile; int seq_client; int seq_port; @@ -134,8 +134,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, if (snd_BUG_ON(!msynth)) return -EINVAL; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - substream = msynth->output_rfile.output; + scoped_guard(rcu) { + substream = rcu_dereference(msynth->output_substream); if (!substream) return -ENODEV; snd_use_lock_use(&msynth->output_use_lock); @@ -177,7 +177,6 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; - spin_lock_init(&msynth->output_lock); snd_use_lock_init(&msynth->output_use_lock); return 0; } @@ -252,8 +251,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info return err; } snd_midi_event_reset_decode(msynth->parser); - scoped_guard(spinlock_irqsave, &msynth->output_lock) - msynth->output_rfile = rfile; + msynth->output_rfile = rfile; + rcu_assign_pointer(msynth->output_substream, rfile.output); return 0; } @@ -261,17 +260,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) { struct seq_midisynth *msynth = private_data; - struct snd_rawmidi_file rfile = {}; + struct snd_rawmidi_file rfile; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - rfile = msynth->output_rfile; - msynth->output_rfile = (struct snd_rawmidi_file){}; - } + rcu_assign_pointer(msynth->output_substream, NULL); + synchronize_rcu(); + snd_use_lock_sync(&msynth->output_use_lock); + rfile = msynth->output_rfile; + msynth->output_rfile = (struct snd_rawmidi_file){}; if (snd_BUG_ON(!rfile.output)) return -EINVAL; - - snd_use_lock_sync(&msynth->output_use_lock); snd_rawmidi_drain_output(rfile.output); return snd_rawmidi_kernel_release(&rfile); } From 9895573b0185f922be4cf500a0f2e5b9560ab1c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Aug 2026 17:03:49 +0200 Subject: [PATCH 771/791] ALSA: hda/intel: Add sanity check for BAR0 size The recent reports from syzkaller showed that we can bind any wild PCI device to HD-audio controller, and if PCI BAR of the device is too small, it may lead to a crash, as the driver believes as if the full register range were accessible. For avoiding such a problem, add a safeguard before the actual probe to check the available BAR0 size. Note that the threshold (0x200) is chosen to cover all needed registers at probing. But this doesn't mean that it would cover fully for all features including the extended ones. Reported-by: syzbot+10cd2d1efe8eeb604bee@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=10cd2d1efe8eeb604bee Reported-by: syzbot+5ebe7cd17e48b4293660@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=5ebe7cd17e48b4293660 Link: https://patch.msgid.link/20260813150354.763502-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 8f592032ac15..1e6d97e08fee 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2197,6 +2197,15 @@ static int azx_probe(struct pci_dev *pci, dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n"); } + /* A sanity check against wild device binding; + * here the range 0x200 is enough for the registers used at probe, + * but it doesn't mean covering all HD-audio registers + */ + if (pci_resource_len(pci, 0) < 0x200) { + dev_err(&pci->dev, "Too small PCI BAR0\n"); + return -EINVAL; + } + err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { From 403f7f3ad3808a0096d84cf228fab68dc253fd9d Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 13 Aug 2026 16:08:08 +0100 Subject: [PATCH 772/791] ALSA: seq: midi: Serialize input teardown with event_input snd_midi_input_event() must not be running while a rawmidi substream is closing, since this can lead to the trigger state becoming out-of-step through this sequence in snd_rawmidi_input_trigger(): snd_rawmidi_input_trigger(up=0) snd_midi_input_event() -> snd_rawmidi_kernel_read() -> snd_rawmidi_input_trigger(up=1) -> cancel_work_sync() which ends with the underlying device being active unexpectedly. When this is called from close_substream(), further input can re-trigger the input event leaving it running after rawmidi_release_priv() has set rfile->rmidi to NULL which leads to: Unable to handle kernel NULL pointer dereference at virtual address 00000000000000b0 Call trace: snd_midi_input_event+0x3c/0x134 [snd_seq_midi] (P) snd_rawmidi_input_event_work+0x1c/0x2c process_one_work+0x150/0x3a4 worker_thread+0x190/0x318 Apply a similar approach to commit ef7607ab1c8ad ("ALSA: seq: midi: Serialize output teardown with event_input") which fixed the same issue in the output direction, but updated to use RCU following Takashi Iwai's proposed follow-on patch [1]. With this change in place, midisynth_unsubscribe() clears the input file so snd_midi_input_event() will not re-trigger the stream and will be quiesced by the cancel_work_sync() in snd_rawmidi_input_trigger(). [1] https://lore.kernel.org/linux-sound/20260813144224.753399-1-tiwai@suse.de/ Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: John Keeping Link: https://patch.msgid.link/20260813150810.795393-1-jkeeping@inmusicbrands.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_midi.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index c2f89aee1914..a16a5debf339 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -42,6 +42,8 @@ struct seq_midisynth { struct snd_rawmidi *rmidi; int device; int subdevice; + struct snd_rawmidi_substream __rcu *input_substream; + snd_use_lock_t input_use_lock; /* in-flight event_input users */ struct snd_rawmidi_file input_rfile; snd_use_lock_t output_use_lock; /* in-flight event_input users */ struct snd_rawmidi_substream __rcu *output_substream; @@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) msynth = runtime->private_data; if (msynth == NULL) return; + + scoped_guard(rcu) { + if (rcu_dereference(msynth->input_substream) != substream) + return; + + snd_use_lock_use(&msynth->input_use_lock); + } + memset(&ev, 0, sizeof(ev)); while (runtime->avail > 0) { res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); @@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) memset(&ev, 0, sizeof(ev)); } } + + snd_use_lock_free(&msynth->input_use_lock); } static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count) @@ -177,6 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; + snd_use_lock_init(&msynth->input_use_lock); snd_use_lock_init(&msynth->output_use_lock); return 0; } @@ -187,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe int err; struct seq_midisynth *msynth = private_data; struct snd_rawmidi_runtime *runtime; + struct snd_rawmidi_file rfile = {}; struct snd_rawmidi_params params; /* open midi port */ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, - &msynth->input_rfile); + &rfile); if (err < 0) { pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); return err; } - runtime = msynth->input_rfile.input->runtime; + runtime = rfile.input->runtime; memset(¶ms, 0, sizeof(params)); params.avail_min = 1; params.buffer_size = input_buffer_size; - err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms); + err = snd_rawmidi_input_params(rfile.input, ¶ms); if (err < 0) { - snd_rawmidi_kernel_release(&msynth->input_rfile); + snd_rawmidi_kernel_release(&rfile); return err; } snd_midi_event_reset_encode(msynth->parser); runtime->event = snd_midi_input_event; runtime->private_data = msynth; + msynth->input_rfile = rfile; + rcu_assign_pointer(msynth->input_substream, rfile.input); snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); return 0; } @@ -218,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri { int err; struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile; - if (snd_BUG_ON(!msynth->input_rfile.input)) + rcu_assign_pointer(msynth->input_substream, NULL); + synchronize_rcu(); + snd_use_lock_sync(&msynth->input_use_lock); + + rfile = msynth->input_rfile; + msynth->input_rfile = (struct snd_rawmidi_file){}; + + if (snd_BUG_ON(!rfile.input)) return -EINVAL; - err = snd_rawmidi_kernel_release(&msynth->input_rfile); + + err = snd_rawmidi_kernel_release(&rfile); return err; } From bc34c37ed8265250c4c41c18953ab82fcc491c83 Mon Sep 17 00:00:00 2001 From: Hongyang Zhao Date: Thu, 13 Aug 2026 19:53:34 +0800 Subject: [PATCH 773/791] ASoC: dt-bindings: es8316: Fix supply property constraints The DT meta-schema requires a `then` clause when an `if` condition has an `else` clause. Invert the compatible check and move the supply property restrictions to `then` so they remain allowed only for ES8316. Fixes: e9966d450b46 ("ASoC: dt-bindings: es8316: Add regulator supplies") Reported-by: Rob Herring Closes: https://lore.kernel.org/r/20260812194234.GA693895-robh@kernel.org Signed-off-by: Hongyang Zhao Link: https://patch.msgid.link/20260813-b4-es8316-binding-conditional-fix-v1-1-6cd56aa1370c@thundersoft.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/everest,es8316.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml index 276c73bb4790..f4ff23120c5b 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml @@ -33,9 +33,10 @@ allOf: - if: properties: compatible: - contains: - const: everest,es8316 - else: + not: + contains: + const: everest,es8316 + then: properties: avdd-supply: false cpvdd-supply: false From ba5401135aa508f0cb5414f269edba1fe90460da Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Thu, 13 Aug 2026 16:05:40 +0800 Subject: [PATCH 774/791] ASoC: tas2781: Refactor calibration start kcontrol creation to separate helper Move the tas2781-specific calibration start kcontrol initialization logic out of tasdevice_create_cali_ctrls() into a new dedicated helper function create_tas2781_cali_start_ktrl(). This change eliminates duplicate inline code in the main calibration control registration routine, improves code readability, and makes further extension for custom calibration parameters much easier. No functional behavior changes. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260813080540.1030-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 71 ++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c029345b4644..6ea4d6488f4e 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1456,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, p->val_len = 4; } +static int create_tas2781_cali_start_ktrl(struct tasdevice_priv + *priv, struct snd_kcontrol_new *cali_ctrl) +{ + struct soc_bytes_ext *ext_cali_start; + char *cali_start_name; + + ext_cali_start = devm_kzalloc(priv->dev, + sizeof(*ext_cali_start), GFP_KERNEL); + if (!ext_cali_start) + return -ENOMEM; + + cali_start_name = devm_kstrdup(priv->dev, + "Calibration Start", GFP_KERNEL); + if (!cali_start_name) + return -ENOMEM; + /* + * package structure for tas2781 ftc start: + * Pkg len (1 byte) + * Reg id (1 byte, constant 'r') + * book, page, register for pilot threshold, pilot tone + * and sine gain (12 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info (1 byte) + * Sine gain for Device #i (8 bytes) + * } + */ + ext_cali_start->max = 14 + priv->ndev * 9; + cali_ctrl->name = cali_start_name; + cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + cali_ctrl->info = snd_soc_bytes_info_ext; + cali_ctrl->put = tas2781_calib_start_put; + cali_ctrl->get = tasdev_nop_get; + cali_ctrl->private_value = (unsigned long)ext_cali_start; + + return 0; +} + static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) { struct calidata *cali_data = &priv->cali_data; @@ -1574,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) */ cali_data->data[0] = 0xff; if (priv->chip_id == TAS2781) { - struct soc_bytes_ext *ext_cali_start; - char *cali_start_name; - - ext_cali_start = devm_kzalloc(priv->dev, - sizeof(*ext_cali_start), GFP_KERNEL); - if (!ext_cali_start) - return -ENOMEM; - - cali_start_name = devm_kstrdup(priv->dev, - "Calibration Start", GFP_KERNEL); - if (!cali_start_name) - return -ENOMEM; - /* - * package structure for tas2781 ftc start: - * Pkg len (1 byte) - * Reg id (1 byte, constant 'r') - * book, page, register for pilot threshold, pilot tone - * and sine gain (12 bytes) - * for (i = 0; i < Device-Sum; i++) { - * Device #i index_info (1 byte) - * Sine gain for Device #i (8 bytes) - * } - */ - ext_cali_start->max = 14 + priv->ndev * 9; - cali_ctrls[i].name = cali_start_name; - cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - cali_ctrls[i].info = snd_soc_bytes_info_ext; - cali_ctrls[i].put = tas2781_calib_start_put; - cali_ctrls[i].get = tasdev_nop_get; - cali_ctrls[i].private_value = (unsigned long)ext_cali_start; + rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); + if (rc != 0) + return rc; i++; + } return snd_soc_add_component_controls(priv->codec, cali_ctrls, From 9c0564fcc21aec4f5b7648f61865ce3fc2b84a7f Mon Sep 17 00:00:00 2001 From: Neil Andrews Date: Thu, 13 Aug 2026 20:41:24 +0000 Subject: [PATCH 775/791] ALSA: usb-audio: Rename the Audient iD14 monitor mix volume control On the Audient iD14 (2708:0008), feature unit 12 is traced through to the Speaker output terminal and is therefore exported as "Speaker Playback Volume". The name fits it badly. It advertises Volume on only four of its six logical channels, which the driver records as cmask=0xf, channels=4 on a 6-channel playback stream, and it sits on the monitor mixer branch rather than in the direct playback path: INPUT_TERMINAL 2 (USB streaming, 6ch) -> EXTENSION_UNIT 51 -> FEATURE_UNIT 10 (no controls) -> OUTPUT_TERMINAL 20 (Speaker) while FU 12 hangs off MIXER_UNIT 60 and feeds back into EXTENSION_UNIT 51. Userspace adopts the control as the stream's hardware playback volume, so any setting below 0 dB attenuates part of the stream and not the rest. Measured over the device's own digital loopback, with one -12 dBFS tone per channel played straight to hw:, PCM channel 0 is unaffected while channel 1 tracks the control: at 107/127 (-20 dB) the two read -15.89 and -35.89 dBFS, a 20.00 dB imbalance, and at 127/127 both read -15.89 dBFS. Give the unit a non-standard name so that it is no longer taken for the stream's master volume. Dropping the control instead also fixes the imbalance, but FU 12 keeps its value across a module reload, so dropping it strands a device that is already attenuated with nothing able to reset it. Renaming leaves the monitor gain reachable and that recovery path intact. The mapped name ends in "Playback" because a name from the map suppresses the automatic " Playback" but still gets " Volume" appended; the control comes out as "Monitor Mix Playback Volume". Tested on the ACP path with PipeWire, which is where the problem reproduces: the control now stays at 127 at every volume setting and the imbalance is 0.00 dB, and setting it by hand to 107 and back to 127 gives 20.00 dB and 0.00 dB as before. Link: https://lore.kernel.org/linux-sound/0102019fed22f9d3-fa294ec5-02f1-4fd3-b3fa-76efc14331cc-000000@eu-west-1.amazonses.com/T/#u Signed-off-by: Neil Andrews Link: https://patch.msgid.link/0102019ffcdbb1e6-9b59d3cc-ef05-4df1-8f9f-fb2f425bcda2-000000@eu-west-1.amazonses.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index ce27fc871f51..8046d5987d5b 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -505,6 +505,19 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { {} }; +/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels + * and sits on the monitor mixer branch, but it is traced through to the + * Speaker output terminal and gets named "Speaker Playback Volume". Userspace + * then adopts it as the stream's hardware volume, and any setting below 0 dB + * attenuates some channels but not others (20 dB imbalance at 80%). Give it a + * non-standard name so that it is no longer taken for the stream's master + * volume, while remaining reachable for anyone who wants the monitor gain. + */ +static const struct usbmix_name_map audient_id14_map[] = { + { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ + {} +}; + /* * Control map entries */ @@ -588,6 +601,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x2573, 0x0008), .map = maya44_map, }, + { + /* Audient iD14 */ + .id = USB_ID(0x2708, 0x0008), + .map = audient_id14_map, + }, { /* KEF X300A */ .id = USB_ID(0x27ac, 0x1000), From e5b03754d2707d89b8cc857f9c9c13efb1c6671d Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 14 Aug 2026 16:41:01 +0800 Subject: [PATCH 776/791] ALSA: hda/realtek: Drop duplicate quirk for Lenovo 0x17aa:0x38df The PCI SSID 17aa:38df is listed twice in alc269_fixup_tbl[], both mapping to the same fixup ALC287_FIXUP_TAS2781_I2C: SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), ... SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), The HDA quirk lookup (hda_quirk_lookup_id()) returns the first matching entry, so the second occurrence never takes effect; it is dead code. Drop the second entry. The retained "Yoga Y990 Intel YC Dual" label also follows the naming of the neighbouring 0x38e0 entry ("Yoga Y990 Intel VECO Dual"). Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260814084101.504471-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index be7287ea24ec..3f0e276fc606 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8101,7 +8101,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; * use codec SSID to distinguish them */ From 5ae1a690c522fea2900ff56c8c2ace7b059f5e04 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 14 Aug 2026 12:05:43 +0000 Subject: [PATCH 777/791] ALSA: core: Fix use-after-free in snd_card_do_free() A use-after-free was detected in snd_card_do_free() when a sound card managed by devres is unbound while a user-space application still holds an open file descriptor. For managed cards, the memory is allocated using devres_alloc(), and its release function is set to __snd_card_release(), which calls snd_card_free(). When the device is unbound, the unbind thread calls snd_card_free(), which drops a reference to the card's device. If the user thread still has an open file descriptor, the reference count does not reach zero, and the unbind thread blocks on wait_for_completion(&released). When the user thread closes the file descriptor, it drops the final reference, invoking the device release callback release_card_device(), which calls snd_card_do_free(). snd_card_do_free() performs cleanup and calls complete(card->release_completion). This wakes up the unbind thread, which returns from snd_card_free() and __snd_card_release(). The devres core then immediately frees the memory block containing the snd_card structure. Meanwhile, the user thread continues execution in snd_card_do_free() and evaluates `if (!card->managed)`. It reads the `managed` boolean from the snd_card structure that was just freed by the unbind thread, triggering a KASAN use-after-free. Fix this by caching the value of card->managed in a local variable before calling complete(). This ensures that the card pointer is not dereferenced after the unbind thread has been woken up and potentially freed the card. BUG: KASAN: use-after-free in snd_card_do_free sound/core/init.c:604 [inline] BUG: KASAN: use-after-free in release_card_device+0x1ab/0x1b0 sound/core/init.c:153 Read of size 1 at addr ffff8881912ec909 by task syz-executor130/5857 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 snd_card_do_free sound/core/init.c:604 [inline] release_card_device+0x1ab/0x1b0 sound/core/init.c:153 device_release+0xc4/0x1f0 drivers/base/core.c:-1 kobject_cleanup lib/kobject.c:689 [inline] kobject_release lib/kobject.c:720 [inline] kref_put include/linux/kref.h:65 [inline] kobject_put+0x222/0x550 lib/kobject.c:737 snd_card_file_remove+0x331/0x390 sound/core/init.c:1125 snd_pcm_release+0x12c/0x160 sound/core/pcm_native.c:2986 __fput+0x418/0xa50 fs/file_table.c:512 fput_close_sync+0x11f/0x240 fs/file_table.c:617 __do_sys_close fs/open.c:1511 [inline] __se_sys_close fs/open.c:1496 [inline] __x64_sys_close+0x7e/0x110 fs/open.c:1496 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: e8ad415b7a55 ("ALSA: core: Add managed card creation") Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+7061d72c26b7daebe2b4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7061d72c26b7daebe2b4 Link: https://syzkaller.appspot.com/ai_job?id=24752a23-f0b6-49c1-bf20-4fa89c2e7eb2 Signed-off-by: Aleksandr Nogikh Link: https://patch.msgid.link/02042186-27b7-42a9-b64e-f93ce8fbe05a@mail.kernel.org Signed-off-by: Takashi Iwai --- sound/core/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/init.c b/sound/core/init.c index 19ec68db561b..2f7f83a7611b 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -583,6 +583,8 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); static int snd_card_do_free(struct snd_card *card) { + bool managed = card->managed; + card->releasing = true; #if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) @@ -603,7 +605,7 @@ static int snd_card_do_free(struct snd_card *card) } if (card->release_completion) complete(card->release_completion); - if (!card->managed) + if (!managed) kfree(card); return 0; } From 888162dabf64128603b12ac2d23236cf2086b7ef Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 10 Aug 2026 11:40:42 +0100 Subject: [PATCH 778/791] ASoC: cs35l56: Request IRQ in cs35l56_common_probe() Call cs35l56_irq_request() in cs35l56_common_probe() instead of calling it afterwards in the probe() for each bus type. Calling cs35l56_irq_request() in each bus probe() is a legacy of dealing with the oddities of the SoundWire framework. It's no longer serving any useful purpose to do it outside of the main cs35l56_common_probe(). Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260810104045.60701-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-i2c.c | 10 +--------- sound/soc/codecs/cs35l56-sdw.c | 6 +----- sound/soc/codecs/cs35l56-spi.c | 10 +--------- sound/soc/codecs/cs35l56.c | 12 ++++++++++-- sound/soc/codecs/cs35l56.h | 2 +- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 4f6ddf1c5a3f..5e69ddbe342a 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -51,15 +51,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); } - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - ret = cs35l56_irq_request(&cs35l56->base, client->irq); - if (ret < 0) - cs35l56_remove(cs35l56); - - return ret; + return cs35l56_common_probe(cs35l56, client->irq); } static void cs35l56_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 303d37e7d0bf..14bb5d1793d3 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -484,11 +484,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi /* Start in cache-only until device is enumerated */ regcache_cache_only(cs35l56->base.regmap, true); - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - return 0; + return cs35l56_common_probe(cs35l56, -EINVAL); } static void cs35l56_sdw_remove(struct sdw_slave *peripheral) diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index b1eb924a5b6c..21b18da9e73d 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -40,15 +40,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) if (ret) return ret; - ret = cs35l56_common_probe(cs35l56); - if (ret != 0) - return ret; - - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); - if (ret < 0) - cs35l56_remove(cs35l56); - - return ret; + return cs35l56_common_probe(cs35l56, spi->irq); } static void cs35l56_spi_remove(struct spi_device *spi) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 0b7b080939a1..619be47060a4 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1942,7 +1942,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 return ret; } -int cs35l56_common_probe(struct cs35l56_private *cs35l56) +int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) { int ret; @@ -2019,16 +2019,24 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err_remove_wm_adsp; } + ret = cs35l56_irq_request(&cs35l56->base, irq); + if (ret) + goto err_remove_wm_adsp; + ret = snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err_remove_wm_adsp; + goto err_free_irq; } return 0; +err_free_irq: + if (cs35l56->base.irq) + devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); + err_remove_wm_adsp: wm_adsp2_remove(&cs35l56->dsp); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 9acd2e7e17c9..1ddee9ab6a87 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -78,7 +78,7 @@ int cs35l56_system_resume_early(struct device *dev); int cs35l56_system_resume(struct device *dev); irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); -int cs35l56_common_probe(struct cs35l56_private *cs35l56); +int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); int cs35l56_init(struct cs35l56_private *cs35l56); void cs35l56_remove(struct cs35l56_private *cs35l56); From a075fef187a6fe8ef99b022a69bc0b9ed584ba93 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 10 Aug 2026 11:40:43 +0100 Subject: [PATCH 779/791] ASoC: cs35l56: Move cs35l56_irq_request() after cs35l56_irq() cs35l56_irq_request() references cs35l56_irq() but was above it in the source (although they are in the other order in the header file). Switch to convertional C ordering. This is preparation for a future patch that will stop exporting cs35l56_irq() and make it static. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260810104045.60701-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index b40bd3a8d27b..0880b6a02247 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -616,25 +616,6 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) } EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); -int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) -{ - int ret; - - if (irq < 1) - return 0; - - ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "cs35l56", cs35l56_base); - if (!ret) - cs35l56_base->irq = irq; - else - dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); - irqreturn_t cs35l56_irq(int irq, void *data) { struct cs35l56_base *cs35l56_base = data; @@ -694,6 +675,25 @@ irqreturn_t cs35l56_irq(int irq, void *data) } EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +{ + int ret; + + if (irq < 1) + return 0; + + ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs35l56", cs35l56_base); + if (!ret) + cs35l56_base->irq = irq; + else + dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); + int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) { unsigned int val; From ee1811eacdbba15a374957c2bcc6ba7102e2b781 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 10 Aug 2026 11:40:44 +0100 Subject: [PATCH 780/791] soundwire: bus_type: Create IRQ mapping before calling driver probe() Call sdw_irq_create_mapping() before calling the peripheral driver probe() so that it is possible to request the IRQ during probe(). Previously creation of the mapping was conditional on the use_domain_irq flag in the driver properties. But these are filled in after probe(), which meant it wasn't possible to request the IRQ during probe(). This was ok for MFD drivers where only children requested the IRQ. But for normal drivers it led to the non-standard behavior of having to defer requesting the IRQ until after probe(). Signed-off-by: Richard Fitzgerald Acked-by: Vinod Koul Link: https://patch.msgid.link/20260810104045.60701-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/soundwire/bus_type.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index e73c1bea9059..d61a97c5b41e 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) } slave->index = ret; + /* Create IRQ mapping now so the driver can get it in probe() */ + sdw_irq_create_mapping(slave); + ret = drv->probe(slave, id); if (ret) { ida_free(&slave->bus->slave_ida, slave->index); @@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) if (drv->ops && drv->ops->read_prop) drv->ops->read_prop(slave); - if (slave->prop.use_domain_irq) - sdw_irq_create_mapping(slave); - /* init the dynamic sysfs attributes we need */ ret = sdw_slave_sysfs_dpn_init(slave); if (ret < 0) From 243ca1fb53834dfa445ccb5d4dd8c7411e89b878 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 10 Aug 2026 11:40:45 +0100 Subject: [PATCH 781/791] ASoC: cs35l56: Use IRQ provided by the SoundWire core Replace the custom SoundWire IRQ handling with the generic nested IRQ provided by the SoundWire core. This removes the local IRQ work function and the convoluted IRQ masking and pm_runtime management around it. We still need the local functions to mask/disable and unmask/enable the SoundWire interrupts because the devices handled by the cs35l56 driver don't have the generic mask bit for the ImpDef1 interrupt so masking and unmasking has to use a custom mask bit. cs35l56_sdw_remove() doesn't need to call cs35l56_disable_sdw_interrupts() now that there isn't a local work function to be flushed. It only masks the custom interrupt mask bit and the rest of the handler cleanup will be done the normal way by devm_free_irq() in cs35l56_remove(). Similar applies to cs35l56_sdw_system_suspend() - it is enough to write the custom mask bits. cs35l56_irq() doesn't need to be exported because cs35l56_sdw.c isn't calling it. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260810104045.60701-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 1 - sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/cs35l56-sdw.c | 65 +++++++------------------------ sound/soc/codecs/cs35l56-shared.c | 3 +- sound/soc/codecs/cs35l56.c | 45 ++++++++++----------- sound/soc/codecs/cs35l56.h | 9 +---- 6 files changed, 38 insertions(+), 86 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 2490b72c0a7a..45a5df574aa6 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); void cs35l56_wait_min_reset_pulse(void); void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); -irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 43162c7d23cf..d73109282447 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -896,6 +896,7 @@ config SND_SOC_CS35L56_SDW tristate "Cirrus Logic CS35L56 CODEC (SDW)" depends on SOUNDWIRE select REGMAP_SOUNDWIRE + select IRQ_DOMAIN select SND_SOC_CS35L56 select SND_SOC_CS35L56_SHARED help diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 14bb5d1793d3..4fba59e80c37 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) * a soft reset. */ if (cs35l56->base.init_done) - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + cs35l56_unmask_soundwire_interrupts(cs35l56); out: pm_runtime_put_autosuspend(cs35l56->base.dev); @@ -240,47 +240,17 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, struct sdw_slave_intr_status *status) { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - - /* SoundWire core holds our pm_runtime when calling this function. */ - - dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); - - if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) - return 0; - /* - * Prevent bus manager suspending and possibly issuing a - * bus-reset before the queued work has run. + * The IRQ itself was handled through the regmap_irq handler, this is + * just clearing up the additional Cirrus SoundWire registers that are + * not covered by the SoundWire framework or the IRQ handler itself. */ - pm_runtime_get_noresume(cs35l56->base.dev); - - /* - * Mask and clear until it has been handled. - * None of the interrupts are time-critical so use the - * power-efficient queue. - */ - cs35l56_mask_soundwire_interrupts(peripheral); - queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); return 0; } -static void cs35l56_sdw_irq_work(struct work_struct *work) -{ - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - sdw_irq_work); - - cs35l56_irq(-1, &cs35l56->base); - - /* unmask interrupts */ - if (!cs35l56->sdw_irq_no_unmask) - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); - - pm_runtime_put_autosuspend(cs35l56->base.dev); -} - static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); prop->paging_support = true; + prop->use_domain_irq = true; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; @@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) if (ret) return ret; - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + cs35l56_unmask_soundwire_interrupts(cs35l56); return 0; } @@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) if (!cs35l56->base.init_done) return 0; - cs35l56_disable_sdw_interrupts(cs35l56); + /* runtime_resume unmasks the interrupt */ + cs35l56_mask_soundwire_interrupts(cs35l56); return cs35l56_system_suspend(dev); } -static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - - cs35l56->sdw_irq_no_unmask = false; - /* runtime_resume re-enables the interrupt */ - - return cs35l56_system_resume(dev); -} - static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { struct device *dev = &peripheral->dev; @@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi cs35l56->base.dev = dev; cs35l56->sdw_peripheral = peripheral; cs35l56->sdw_link_num = peripheral->bus->link_id; - INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); dev_set_drvdata(dev, cs35l56); @@ -484,21 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi /* Start in cache-only until device is enumerated */ regcache_cache_only(cs35l56->base.regmap, true); - return cs35l56_common_probe(cs35l56, -EINVAL); + return cs35l56_common_probe(cs35l56, peripheral->irq); } static void cs35l56_sdw_remove(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - cs35l56_disable_sdw_interrupts(cs35l56); + cs35l56_mask_soundwire_interrupts(cs35l56); cs35l56_remove(cs35l56); } static const struct dev_pm_ops cs35l56_sdw_pm = { SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ }; diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 0880b6a02247..7b3e37d462d6 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -616,7 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) } EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); -irqreturn_t cs35l56_irq(int irq, void *data) +static irqreturn_t cs35l56_irq(int irq, void *data) { struct cs35l56_base *cs35l56_base = data; unsigned int status1 = 0, status8 = 0, status20 = 0; @@ -673,7 +673,6 @@ irqreturn_t cs35l56_irq(int irq, void *data) return IRQ_HANDLED; } -EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) { diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 619be47060a4..b9118ad8fab5 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -37,48 +37,49 @@ #include "wm_adsp.h" #include "cs35l56.h" -void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) +void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) { /* + * Mask unconditionally. + * * The read of GEN_INT_STAT_1 is required as per the SoundWire spec * for interrupt status bits to clear. * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. */ - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); } EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) +void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) { - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); + if (!cs35l56->base.irq) + return; + + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); } EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) +static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) { if (!cs35l56->sdw_peripheral) return; - cs35l56->sdw_irq_no_unmask = true; - flush_work(&cs35l56->sdw_irq_work); - - /* Mask interrupts and flush in case sdw_irq_work was queued again */ - cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); - flush_work(&cs35l56->sdw_irq_work); + cs35l56_mask_soundwire_interrupts(cs35l56); + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); } -EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); -void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) +static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) { - if (!cs35l56->sdw_peripheral) + if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) return; - cs35l56->sdw_irq_no_unmask = false; - cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); + enable_irq(cs35l56->base.irq); + cs35l56_unmask_soundwire_interrupts(cs35l56); } -EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -828,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing { int ret; - /* - * Disable SoundWire interrupts to prevent race with IRQ work. - * Setting sdw_irq_no_unmask prevents the handler re-enabling - * the SoundWire interrupt. - */ + /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ cs35l56_disable_sdw_interrupts(cs35l56); ret = cs35l56_firmware_shutdown(&cs35l56->base); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 1ddee9ab6a87..35c02ae17de3 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -39,8 +39,6 @@ struct cs35l56_private { struct sdw_slave *sdw_peripheral; struct regmap *sdw_bus_regmap; const char *fallback_fw_suffix; - struct work_struct sdw_irq_work; - bool sdw_irq_no_unmask; bool soft_resetting; bool sdw_attached; struct completion init_completion; @@ -65,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; -void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); -void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); -void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); -void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); +void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); +void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); int cs35l56_system_suspend(struct device *dev); int cs35l56_system_suspend_late(struct device *dev); @@ -76,7 +72,6 @@ int cs35l56_system_suspend_no_irq(struct device *dev); int cs35l56_system_resume_no_irq(struct device *dev); int cs35l56_system_resume_early(struct device *dev); int cs35l56_system_resume(struct device *dev); -irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); int cs35l56_init(struct cs35l56_private *cs35l56); From a698e4a60fa54268a38f4e66378851a196cb139b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 14 Aug 2026 16:12:38 +0800 Subject: [PATCH 782/791] ASoC: SOF: validate topology volume range before allocation SOF treats the topology mixer min and max values as non-negative indices into its volume table. It stores them in signed fields, allocates max + 1 entries through an int argument, and later indexes the table with the stored range. An inverted range is invalid, while a maximum at or above INT_MAX cannot be represented safely after the increment or in the signed fields. Validate the complete range before storing it or allocating the table. Fixes: 311ce4fe7637 ("ASoC: SOF: Add support for loading topologies") Assisted-by: Codex:gpt-5 Signed-off-by: Pengpeng Hou Acked-by: Peter Ujfalusi Link: https://patch.msgid.link/20260814081238.25434-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 820513bb2577..8338133899ab 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -846,6 +846,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); int tlv[SOF_TLV_ITEMS]; + u32 min, max; unsigned int mask; int ret; @@ -853,6 +854,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) return -EINVAL; + min = le32_to_cpu(mc->min); + max = le32_to_cpu(mc->max); + if (min > max || max >= INT_MAX) + return -EINVAL; + /* * If control has more than 2 channels we need to override the info. This is because even if * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the @@ -863,12 +869,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, kc->info = snd_sof_volume_info; scontrol->comp_id = sdev->next_comp_id; - scontrol->min_volume_step = le32_to_cpu(mc->min); - scontrol->max_volume_step = le32_to_cpu(mc->max); + scontrol->min_volume_step = min; + scontrol->max_volume_step = max; scontrol->num_channels = le32_to_cpu(mc->num_channels); - scontrol->max = le32_to_cpu(mc->max); - if (le32_to_cpu(mc->max) == 1) + scontrol->max = max; + if (max == 1) goto skip; /* extract tlv data */ @@ -878,7 +884,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, } /* set up volume table */ - ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); + ret = set_up_volume_table(scontrol, tlv, max + 1); if (ret < 0) { dev_err(scomp->dev, "error: setting up volume table\n"); return ret; @@ -911,7 +917,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, return 0; err: - if (le32_to_cpu(mc->max) > 1) + if (max > 1) kfree(scontrol->volume_table); return ret; From 0c7aeb0f5eceb95b5887bd8e83fef865e5a49a13 Mon Sep 17 00:00:00 2001 From: Andrey Golovko Date: Fri, 14 Aug 2026 09:40:00 +0300 Subject: [PATCH 783/791] ASoC: tas2783-sdw: do not treat read-only Controls as writable The regmap has no writeable_reg callback, so regmap considers every register up to max_register writable. That includes the read-only SDCA Controls the driver itself describes: the Latency of every Entity, the Clock Valid of every Clock Source, the actual power state of the Power Domain Entity, the protection status, the algorithm ready flag and the Extension Unit id and version. Most of them are also listed in tas2783_reg_default[] with a placeholder of zero, even though a default for, say, a latency reading is meaningless. Reading such a Control caches its real value, which no longer matches the placeholder, so regcache_sync() then tries to write it back. The peripheral rejects the transaction with -ENODATA and the sync aborts, leaving the rest of the cache unrestored. Add a writeable_reg callback that refuses the read-only Controls and otherwise keeps the previous behaviour. Every selector it lists is the read-only Control of its Entity type in sdca_function.h, and none of the Controls the driver writes is affected: the requested power state, the mutes, the Cluster Index, the protection mode, the algorithm enable and the file download Controls all stay writable. The list is static because the BIOS on the affected machines describes no Smart Amp SDCA function, so the driver runs its fallback tables and sdca_regmap_writeable() is not available to it. It would be good to have the list confirmed against the hardware documentation, and to know whether the read-only Controls belong in tas2783_reg_default[] at all. Signed-off-by: Andrey Golovko Link: https://patch.msgid.link/20260814094000.22118-2-andrey.golovko@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index c217da5fccdf..caf8fe1bf4db 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -495,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) return tas2783_sdca_mbq_size(dev, reg) > 0; } +static bool tas2783_writeable_register(struct device *dev, unsigned int reg) +{ + /* + * The Latency Control of every Entity, together with the Power Domain + * actual state and the protection status, is read-only. They are + * listed in tas2783_reg_default[] with a placeholder value, so without + * this a regcache_sync() would try to write them back and the + * peripheral would reject the transaction, aborting the sync. + */ + switch (reg) { + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): + return false; + + default: + return tas2783_sdca_mbq_size(dev, reg) > 0; + } +} + static bool tas2783_volatile_register(struct device *dev, u32 reg) { switch (reg) { @@ -516,6 +566,7 @@ static const struct regmap_config tas_regmap = { .reg_bits = 32, .val_bits = 8, .readable_reg = tas2783_readable_register, + .writeable_reg = tas2783_writeable_register, .volatile_reg = tas2783_volatile_register, .reg_defaults = tas2783_reg_default, .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), From 317ea3b870fd56c2c467b85ac181b938cbf53405 Mon Sep 17 00:00:00 2001 From: Giulio Gualtierotti Date: Sun, 16 Aug 2026 11:42:23 +0200 Subject: [PATCH 784/791] ALSA: hda/realtek: Add micmute LED quirk for Acer Aspire A515-57 The Acer Aspire A515-57 with subsystem ID 1025:1616 and Realtek ALC256 uses GPIO2 (0x04) for the microphone mute LED. Without a quirk, the GPIO mask and direction are not configured and the LED does not follow the microphone mute state. Reuse ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, which configures GPIO2 as the microphone mute LED. Tested on an Acer Aspire A515-57 with ALC256 (10ec:0256, subsystem 1025:1616). GPIO mask and direction are 0x04 and GPIO data switches between 0x00 and 0x04; the LED device is registered and follows the microphone mute state. Signed-off-by: Giulio Gualtierotti Link: https://patch.msgid.link/20260816094223.36617-1-ggualtierotti.dev@mailbox.org 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 3f0e276fc606..0ebbf91921c5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7063,6 +7063,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1616, "Acer Aspire A515-57", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x166c, "Acer Predator PH16-71", ALC2XX_FIXUP_HEADSET_MIC), From 19b02fecd8f857909af779afe3816e487ac10cd0 Mon Sep 17 00:00:00 2001 From: Yashraj Ghule Date: Sun, 16 Aug 2026 16:36:55 +0530 Subject: [PATCH 785/791] ALSA: hda/realtek: Fix mute LED for HP Victus 15-fa1xxx (MB 8C3F) The HP Victus 15-fa1xxx with motherboard 8C3F is missing the existing mute LED quirk for ALC245 codecs. Add the 103c:8c3f subsystem ID to the existing ALC245_FIXUP_HP_MUTE_LED_COEFBIT quirk. Tested on HP Victus 15-fa1xxx (MB 8C3F). The mute LED works as intended. Signed-off-by: Yashraj Ghule Link: https://patch.msgid.link/20260816110655.11592-1-yashrajghule.221@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 0ebbf91921c5..67c5692eee1f 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7472,6 +7472,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), From f6635d64e783ad66d800fefa57f897294004ea65 Mon Sep 17 00:00:00 2001 From: Zeliang Li Date: Sat, 15 Aug 2026 03:45:55 +0800 Subject: [PATCH 786/791] ALSA: hda/tas2781: Add hardware stabilization delay during firmware load retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During runtime resume transitions, loading calibration data blocks to the tas2781 amplifier may intermittently trigger transmission failures or block checksum mismatches (-EAGAIN) due to un-stabilized power rails or I2C bus glitches. The loop in tasdev_load_blk() decrements block->nr_retry and attempts an immediate re-transmission upon receiving -EAGAIN. However, without any inter-retry delay, all available retry slots are exhausted within less than a microsecond—long before the hardware can physically settle. This leads to permanent "ERROR_PRAM_CRCCHK" deadlocks and silent speakers on modern laptops after resuming media. Fix this cleanly by introducing a 2ms usleep_range() delay directly inside the tasdev_load_blk() retry paths prior to each 'continue' statement. This grants the chip sufficient time to stabilize before the next transmission attempt without introducing unnecessary latency on final failures. Signed-off-by: Zeliang Li Link: https://patch.msgid.link/20260815-master-v2-1-b4ea03c8b59e@gmail.com Signed-off-by: Takashi Iwai --- sound/soc/codecs/tas2781-fmwlib.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index dcbeb9618195..bfabff583d57 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -1849,16 +1849,26 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv, } } if (ret == -EAGAIN) { - if (block->nr_retry > 0) + if (block->nr_retry > 0) { + /* Give the hardware time to stabilize before + * next block re-transmission attempt. + */ + usleep_range(2000, 2500); continue; + } } else if (ret < 0) /*err in current device, skip it*/ break; if (block->is_pchksum_present) { ret = tasdev_block_chksum(tas_priv, block, chn); if (ret == -EAGAIN) { - if (block->nr_retry > 0) + if (block->nr_retry > 0) { + /* Give the bus time to recover after + * a checksum mismatch error. + */ + usleep_range(2000, 2500); continue; + } } else if (ret < 0) /*err in current device, skip it*/ break; } From 75dc2eda659f6be4a370734f11baf25df8a9fd80 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 17 Aug 2026 17:47:08 +0800 Subject: [PATCH 787/791] ALSA: hda/realtek: Fix Lenovo Yoga Slim 7 14AKP10 quirk ordering The Yoga Slim 7 14AKP10 has a PCI SSID of 17aa:38b4 but a codec SSID of 17aa:391a. The current quirk table contains a PCI quirk for 17aa:38b4 (for the Legion Slim 7 16IRH8) which matches first, so the codec-specific quirk for 17aa:391a is never applied. This results in the wrong fixup being used (CS35L41_I2C_2 instead of the correct bass speaker fixup), leaving the internal speakers misconfigured or silent. Remove the 17aa:391a entry from its PCI-SSID-sorted position and add it as an HDA_CODEC_QUIRK directly before the 17aa:38b4 entry, because it must match on the codec subsystem ID rather than the PCI SSID and it has to win over the colliding PCI quirk for the Legion Slim 7 16IRH8. A comment is added to explain the out-of-order placement, following the same style already used for the 17aa:38bb and 17aa:38f9 codec-SSID overrides. With this change, the correct ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN is applied, restoring speaker output and auto-mute functionality. The original quirk added in commit e6c888202297 ("ALSA: hda/realtek: Add quirk for Lenovo Yoga Slim 7 14AKP10") matched on the PCI SSID 17aa:391a, but this model actually exposes PCI SSID 17aa:38b4 (shared with the Legion Slim 7 16IRH8), so that quirk never matched and the bass speaker remained silent. Fix it by matching on the codec SSID and placing the entry before the colliding 17aa:38b4 PCI quirk. Fixes: e6c888202297 ("ALSA: hda/realtek: Add quirk for Lenovo Yoga Slim 7 14AKP10") Cc: stable@vger.kernel.org Link: https://bugzilla.kernel.org/show_bug.cgi?id=221298 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260817094708.222154-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 67c5692eee1f..cae327e1d90d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8071,6 +8071,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + /* Lenovo Yoga Slim 7 14AKP10 shares PCI SSID 17aa:38b4 with Legion Slim 7 + * 16IRH8; use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x391d, "Lenovo Yoga 7 2-in-1 16AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), @@ -8118,7 +8122,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), From ceaebef91d1aff671a512d5f003766ea41152d94 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 17 Aug 2026 16:55:31 +0800 Subject: [PATCH 788/791] ALSA: hda: Add Lisuan HDMI controller and codec support Lisuan GPUs expose an HD-audio controller at PCI ID 4c54:5010 and an HDMI/DP codec with codec ID 0x4c545020. Neither ID is currently matched by the HDA stack, leaving HDMI/DP audio unavailable on these devices. The existing downstream support uses the generic HDMI codec path and attaches no Lisuan-specific capability flags to the controller. Its dedicated AZX driver type only changes the short driver name. Use the corresponding generic upstream paths instead: bind 4c54:5010 to AZX_DRIVER_GENERIC, register 0x4c545020 as MODEL_GENERIC, and add the Lisuan codec vendor name. This keeps the enablement minimal and avoids a vendor-only AZX driver type with no vendor-specific behavior. Signed-off-by: Xu Rao Link: https://patch.msgid.link/DE1B62191D573D37+20260817085531.992573-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/hdmi/hdmi.c | 1 + sound/hda/controllers/intel.c | 2 ++ sound/hda/core/device.c | 1 + 3 files changed, 4 insertions(+) diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 053c41d98028..1e7a05c87733 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2348,6 +2348,7 @@ static const struct hda_device_id snd_hda_id_generichdmi[] = { HDA_CODEC_ID_MODEL(0x1d179f8e, "KX-7000 HDMI/DP", MODEL_GF), HDA_CODEC_ID_MODEL(0x1d179f8f, "KX-7000 HDMI/DP", MODEL_GF), HDA_CODEC_ID_MODEL(0x1d179f90, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x4c545020, "Lisuan HDMI/DP", MODEL_GENERIC), HDA_CODEC_ID_MODEL(0x67663d82, "Arise 82 HDMI/DP", MODEL_GF), HDA_CODEC_ID_MODEL(0x67663d83, "Arise 83 HDMI/DP", MODEL_GF), HDA_CODEC_ID_MODEL(0x67663d84, "Arise 84 HDMI/DP", MODEL_GF), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 1e6d97e08fee..24015c73a67f 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2857,6 +2857,8 @@ static const struct pci_device_id azx_ids[] = { /* Hygon HDAudio */ { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_HDA), .driver_data = AZX_DRIVER_HYGON | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI }, + /* Lisuan HD-audio */ + { PCI_DEVICE(0x4c54, 0x5010), .driver_data = AZX_DRIVER_GENERIC }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c index 832494035f0a..776d629ba252 100644 --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c @@ -663,6 +663,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { { 0x1af4, "QEMU" }, { 0x1fa8, "Senarytech" }, { 0x434d, "C-Media" }, + { 0x4c54, "Lisuan" }, { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, {} /* terminator */ From 19eadf550ba518db6509eed3c3f34d4fc1e02ee7 Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Mon, 17 Aug 2026 11:19:42 +0000 Subject: [PATCH 789/791] ALSA: usb-audio: Add delay quirk for SPACETOUCH USB Audio Audio control requests that set sampling frequency sometimes fail on this card. Adding delay between control messages eliminates that problem. usb 1-1: New USB device found, idVendor=0666, idProduct=0880 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: USB Audio usb 1-1: Manufacturer: SPACETOUCH usb 1-1: SerialNumber: 000000000 Signed-off-by: Lianqin Hu Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217D93F595D9995413C9721D2A72@TYUPR06MB6217.apcprd06.prod.outlook.com --- sound/usb/quirks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 00b686a6c25b..eb1750def067 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2314,7 +2314,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x0661, 0x0883, /* iBasso DC04 Ultra */ QUIRK_FLAG_DSD_RAW), DEVICE_FLG(0x0666, 0x0880, /* SPACETOUCH USB Audio */ - QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY | + QUIRK_FLAG_CTL_MSG_DELAY_5M), DEVICE_FLG(0x06f8, 0xb000, /* Hercules DJ Console (Windows Edition) */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x06f8, 0xd002, /* Hercules DJ Console (Macintosh Edition) */ From ff722d025853a33a15b080459e4c52be28d44b6e Mon Sep 17 00:00:00 2001 From: Ninad Naik Date: Sat, 21 Mar 2026 19:32:11 +0530 Subject: [PATCH 790/791] ALSA: docs: fix dead link to Intel HD-audio spec The existing link redirects to a generic page. Update the link to the specification document. Signed-off-by: Ninad Naik Link: https://patch.msgid.link/20260321140212.5026-1-ninadnaik07@gmail.com Signed-off-by: Takashi Iwai --- Documentation/sound/hd-audio/notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index 6993bfa159b4..c16f83875b80 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -42,7 +42,7 @@ If you are interested in the deep debugging of HD-audio, read the HD-audio specification at first. The specification is found on Intel's web page, for example: -* https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html +* https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf HD-Audio Controller From c139e7e44f58a6f8ddc9d850ea9924d34963b5da Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 18 Aug 2026 16:38:08 +0800 Subject: [PATCH 791/791] ALSA: hda: Fix connection list comparison in proc output print_conn_list() compares the raw hardware connection list with the connection list cached by the HDA driver. When they differ, it prints an additional "In-driver Connection" line so that /proc/asound/card*/codec#* shows the topology actually used by the driver. The comparison currently passes conn_len directly to memcmp(). However, conn_len is a number of connection-list entries, while memcmp() expects a size in bytes. Both list and conn are arrays of hda_nid_t, which is u16, so only half of the connection data is compared. For example, for two-entry lists such as: hardware: 0x0c 0x0d cached: 0x0c 0x0e conn_len is 2, and the current comparison checks only the first hda_nid_t. The lists are therefore incorrectly treated as identical even though the second connection differs. This can happen legitimately when codec fixups replace a cached connection list with snd_hda_override_conn_list(). The codec routing used by the driver is not affected, but the proc output can hide the overridden driver-visible routing and provide misleading topology information during codec debugging. Convert the entry count to a byte size so that memcmp() covers the complete connection list. Fixes: 8b2c7a5c404d ("ALSA: hda - Add In-driver connection info") Signed-off-by: Xu Rao Link: https://patch.msgid.link/7B802A4E225CC808+20260818083808.2735120-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/common/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c index c83796b13d3d..3dabdb418c7b 100644 --- a/sound/hda/common/proc.c +++ b/sound/hda/common/proc.c @@ -624,7 +624,7 @@ static void print_conn_list(struct snd_info_buffer *buffer, /* Get Cache connections info */ cache_len = snd_hda_get_conn_list(codec, nid, &list); if (cache_len >= 0 && (cache_len != conn_len || - memcmp(list, conn, conn_len) != 0)) { + memcmp(list, conn, conn_len * sizeof(*conn)) != 0)) { snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len); if (cache_len > 0) { snd_iprintf(buffer, " ");