From a89f7f08182954f51fcec1c8c65dec0c6cfc778e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:43 +0100 Subject: [PATCH 001/341] ALSA: seq: Use bus specific probe and remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a bus specific probe and remove function. For now this only allows to get rid of a cast of the generic device to an snd_seq device in the drivers and changes the remove prototype to return void---a non-zero return value is ignored anyhow. The objective is to get rid of users of struct device callbacks .probe(), .remove() and .shutdown() to eventually remove these. Until all snd_seq drivers are converted this results in a runtime warning about the drivers needing an update because there is a bus probe function and a driver probe function. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/f36b01b297fc5cbb6d0ed4959143add0c13eec99.1765283601.git.u.kleine-koenig@baylibre.com --- include/sound/seq_device.h | 2 ++ sound/core/seq_device.c | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index dead74b022f4..a72380c202e9 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -43,6 +43,8 @@ struct snd_seq_device { * Typically, call snd_device_free(dev->card, dev->driver_data) */ struct snd_seq_driver { + int (*probe)(struct snd_seq_device *dev); + void (*remove)(struct snd_seq_device *dev); struct device_driver driver; char *id; int argsize; diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index bac9f8603734..01def5b739a6 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -49,9 +49,31 @@ static int snd_seq_bus_match(struct device *dev, const struct device_driver *drv sdrv->argsize == sdev->argsize; } +static int snd_seq_bus_probe(struct device *dev) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + const struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + + if (sdrv->probe) + return sdrv->probe(sdev); + else + return 0; +} + +static void snd_seq_bus_remove(struct device *dev) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + const struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + + if (sdrv->remove) + sdrv->remove(sdev); +} + static const struct bus_type snd_seq_bus_type = { .name = "snd_seq", .match = snd_seq_bus_match, + .probe = snd_seq_bus_probe, + .remove = snd_seq_bus_remove, }; /* @@ -242,6 +264,25 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id, } EXPORT_SYMBOL(snd_seq_device_new); +static int snd_seq_driver_legacy_probe(struct snd_seq_device *sdev) +{ + struct device *dev = &sdev->dev; + struct device_driver *driver = dev->driver; + + return driver->probe(dev); +} + +static void snd_seq_driver_legacy_remove(struct snd_seq_device *sdev) +{ + struct device *dev = &sdev->dev; + struct device_driver *driver = dev->driver; + int ret; + + ret = driver->remove(dev); + if (unlikely(ret)) + dev_warn(dev, "Ignoring return value of remove callback (%pe)\n", ERR_PTR(ret)); +} + /* * driver registration */ @@ -251,6 +292,12 @@ int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) return -EINVAL; drv->driver.bus = &snd_seq_bus_type; drv->driver.owner = mod; + + if (!drv->probe && drv->driver.probe) + drv->probe = snd_seq_driver_legacy_probe; + if (!drv->remove && drv->driver.remove) + drv->remove = snd_seq_driver_legacy_remove; + return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(__snd_seq_driver_register); From 2e514916e90fd709c07be2a9e2965aa0c66997ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:44 +0100 Subject: [PATCH 002/341] ALSA: seq: midi: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Note that the remove callback returns void now. The actual return value was ignored before (see device_remove() in drivers/base/dd.c), so there is no problem introduced by converting `return -ENODEV` to `return`. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/054ae56db6b55eea60c8aa8f9633e8d3d180cb09.1765283601.git.u.kleine-koenig@baylibre.com --- sound/core/seq/seq_midi.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 581e138a3115..de7ec7d15f23 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -265,9 +265,8 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) /* register new midi synth port */ static int -snd_seq_midisynth_probe(struct device *_dev) +snd_seq_midisynth_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port __free(kfree) = NULL; @@ -411,10 +410,9 @@ snd_seq_midisynth_probe(struct device *_dev) } /* release midi synth port */ -static int -snd_seq_midisynth_remove(struct device *_dev) +static void +snd_seq_midisynth_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth; struct snd_card *card = dev->card; @@ -423,7 +421,7 @@ snd_seq_midisynth_remove(struct device *_dev) guard(mutex)(®ister_mutex); client = synths[card->number]; if (client == NULL || client->ports[device] == NULL) - return -ENODEV; + return; ports = client->ports_per_device[device]; client->ports_per_device[device] = 0; msynth = client->ports[device]; @@ -437,14 +435,13 @@ snd_seq_midisynth_remove(struct device *_dev) synths[card->number] = NULL; kfree(client); } - return 0; } static struct snd_seq_driver seq_midisynth_driver = { + .probe = snd_seq_midisynth_probe, + .remove = snd_seq_midisynth_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_seq_midisynth_probe, - .remove = snd_seq_midisynth_remove, }, .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, .argsize = 0, From d1c83a79e3985e7d5961dfe8adb76e1d9a624fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:45 +0100 Subject: [PATCH 003/341] ALSA: seq: ump: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/054f1a0536228ccfe5f539ce854804f789f2ee64.1765283601.git.u.kleine-koenig@baylibre.com --- sound/core/seq/seq_ump_client.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 27247babb16d..a96d21981cbe 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -452,9 +452,8 @@ static const struct snd_seq_ump_ops seq_ump_ops = { }; /* create a sequencer client and ports for the given UMP endpoint */ -static int snd_seq_ump_probe(struct device *_dev) +static int snd_seq_ump_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_ump_endpoint *ump = dev->private_data; struct snd_card *card = dev->card; struct seq_ump_client *client; @@ -513,21 +512,19 @@ static int snd_seq_ump_probe(struct device *_dev) } /* remove a sequencer client */ -static int snd_seq_ump_remove(struct device *_dev) +static void snd_seq_ump_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_ump_endpoint *ump = dev->private_data; if (ump->seq_client) seq_ump_client_free(ump->seq_client); - return 0; } static struct snd_seq_driver seq_ump_driver = { + .probe = snd_seq_ump_probe, + .remove = snd_seq_ump_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_seq_ump_probe, - .remove = snd_seq_ump_remove, }, .id = SNDRV_SEQ_DEV_ID_UMP, .argsize = 0, From 376cdcd3ff18c08540178bc95e1dba49228cd2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:46 +0100 Subject: [PATCH 004/341] ALSA: opl3: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Note that the remove callback returns void now. The actual return value was ignored before (see device_remove() in drivers/base/dd.c), so there is no problem introduced by converting `return -EINVAL` to `return`. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/fb5e44d995cf93b8feae22c4558598859712bc07.1765283601.git.u.kleine-koenig@baylibre.com --- sound/drivers/opl3/opl3_seq.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index d3278428d360..b77d8e8f12fb 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -201,9 +201,8 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) /* ------------------------------ */ -static int snd_opl3_seq_probe(struct device *_dev) +static int snd_opl3_seq_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; int client, err; char name[32]; @@ -244,14 +243,13 @@ static int snd_opl3_seq_probe(struct device *_dev) return 0; } -static int snd_opl3_seq_remove(struct device *_dev) +static void snd_opl3_seq_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); if (opl3 == NULL) - return -EINVAL; + return; #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_free_seq_oss(opl3); @@ -260,14 +258,13 @@ static int snd_opl3_seq_remove(struct device *_dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } - return 0; } static struct snd_seq_driver opl3_seq_driver = { + .probe = snd_opl3_seq_probe, + .remove = snd_opl3_seq_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_opl3_seq_probe, - .remove = snd_opl3_seq_remove, }, .id = SNDRV_SEQ_DEV_ID_OPL3, .argsize = sizeof(struct snd_opl3 *), From 505b57e52c9eeef810df23af6064aeb79b601f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:47 +0100 Subject: [PATCH 005/341] ALSA: opl4: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/15cd19c9c8ec32e92b956eec112e515335bc22cf.1765283601.git.u.kleine-koenig@baylibre.com --- sound/drivers/opl4/opl4_seq.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index 7bb22089a093..fd6f15be6109 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -118,9 +118,8 @@ static void snd_opl4_seq_free_port(void *private_data) snd_midi_channel_free_set(opl4->chset); } -static int snd_opl4_seq_probe(struct device *_dev) +static int snd_opl4_seq_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; int client; struct snd_seq_port_callback pcallbacks; @@ -175,27 +174,25 @@ static int snd_opl4_seq_probe(struct device *_dev) return 0; } -static int snd_opl4_seq_remove(struct device *_dev) +static void snd_opl4_seq_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); if (!opl4) - return -EINVAL; + return; if (opl4->seq_client >= 0) { snd_seq_delete_kernel_client(opl4->seq_client); opl4->seq_client = -1; } - return 0; } static struct snd_seq_driver opl4_seq_driver = { + .probe = snd_opl4_seq_probe, + .remove = snd_opl4_seq_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_opl4_seq_probe, - .remove = snd_opl4_seq_remove, }, .id = SNDRV_SEQ_DEV_ID_OPL4, .argsize = sizeof(struct snd_opl4 *), From 4983d2f55f34748e64f22b514dd5fbfab1c6b0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:48 +0100 Subject: [PATCH 006/341] ALSA: sb: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ccaf10073a6e8a68ea751bfc0e8aae1c66b57458.1765283601.git.u.kleine-koenig@baylibre.com --- sound/isa/sb/emu8000_synth.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 9bec85ec55b4..3414c6d0695f 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -21,9 +21,8 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu8000 */ -static int snd_emu8000_probe(struct device *_dev) +static int snd_emu8000_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; struct snd_emux *emu; @@ -81,13 +80,12 @@ static int snd_emu8000_probe(struct device *_dev) /* * free all resources */ -static int snd_emu8000_remove(struct device *_dev) +static void snd_emu8000_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; if (dev->driver_data == NULL) - return 0; /* no synth was allocated actually */ + return; /* no synth was allocated actually */ hw = dev->driver_data; if (hw->pcm) @@ -96,7 +94,6 @@ static int snd_emu8000_remove(struct device *_dev) snd_util_memhdr_free(hw->memhdr); hw->emu = NULL; hw->memhdr = NULL; - return 0; } /* @@ -104,10 +101,10 @@ static int snd_emu8000_remove(struct device *_dev) */ static struct snd_seq_driver emu8000_driver = { + .probe = snd_emu8000_probe, + .remove = snd_emu8000_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_emu8000_probe, - .remove = snd_emu8000_remove, }, .id = SNDRV_SEQ_DEV_ID_EMU8000, .argsize = sizeof(struct snd_emu8000 *), From 94afb5b7a130f3842baf0aa1fd936b2c7d73cbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:49 +0100 Subject: [PATCH 007/341] ALSA: emu10k1: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/31d351fc844348eac0955db8db78c19a0c23d888.1765283601.git.u.kleine-koenig@baylibre.com --- sound/pci/emu10k1/emu10k1_synth.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 662d20eb9689..26499c31eaa8 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -16,9 +16,8 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu10k1 */ -static int snd_emu10k1_synth_probe(struct device *_dev) +static int snd_emu10k1_synth_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; @@ -64,14 +63,13 @@ static int snd_emu10k1_synth_probe(struct device *_dev) return 0; } -static int snd_emu10k1_synth_remove(struct device *_dev) +static void snd_emu10k1_synth_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; if (dev->driver_data == NULL) - return 0; /* not registered actually */ + return; /* not registered actually */ emux = dev->driver_data; @@ -82,7 +80,6 @@ static int snd_emu10k1_synth_remove(struct device *_dev) } snd_emux_free(emux); - return 0; } /* @@ -90,10 +87,10 @@ static int snd_emu10k1_synth_remove(struct device *_dev) */ static struct snd_seq_driver emu10k1_synth_driver = { + .probe = snd_emu10k1_synth_probe, + .remove = snd_emu10k1_synth_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_emu10k1_synth_probe, - .remove = snd_emu10k1_synth_remove, }, .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, .argsize = sizeof(struct snd_emu10k1_synth_arg), From 4b8da6d08944f78b17bd9e846c8e3e38625088a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:50 +0100 Subject: [PATCH 008/341] ALSA: seq: oss: Convert to snd_seq bus probe mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snd_seq bus got a dedicated probe function. Make use of that. This fixes a runtime warning about the driver needing to be converted to the bus probe method. Note that the remove callback returns void now. The actual return value was ignored before (see device_remove() in drivers/base/dd.c), so there is no problem introduced by converting `return -EINVAL` to `return`. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/affb5a7107e9d678ce85dc7f0b87445928cd6b94.1765283601.git.u.kleine-koenig@baylibre.com --- sound/core/seq/oss/seq_oss.c | 4 ++-- sound/core/seq/oss/seq_oss_synth.c | 12 ++++-------- sound/core/seq/oss/seq_oss_synth.h | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 02d30d8b6c3a..021cd70f90db 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -54,10 +54,10 @@ static __poll_t odev_poll(struct file *file, poll_table * wait); */ static struct snd_seq_driver seq_oss_synth_driver = { + .probe = snd_seq_oss_synth_probe, + .remove = snd_seq_oss_synth_remove, .driver = { .name = KBUILD_MODNAME, - .probe = snd_seq_oss_synth_probe, - .remove = snd_seq_oss_synth_remove, }, .id = SNDRV_SEQ_DEV_ID_OSS, .argsize = sizeof(struct snd_seq_oss_reg), diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 8c4e5913c7e6..68bc6d4eed53 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -80,9 +80,8 @@ snd_seq_oss_synth_init(void) * registration of the synth device */ int -snd_seq_oss_synth_probe(struct device *_dev) +snd_seq_oss_synth_probe(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); int i; struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -128,10 +127,9 @@ snd_seq_oss_synth_probe(struct device *_dev) } -int -snd_seq_oss_synth_remove(struct device *_dev) +void +snd_seq_oss_synth_remove(struct snd_seq_device *dev) { - struct snd_seq_device *dev = to_seq_dev(_dev); int index; struct seq_oss_synth *rec = dev->driver_data; @@ -142,7 +140,7 @@ snd_seq_oss_synth_remove(struct device *_dev) } if (index >= max_synth_devs) { pr_err("ALSA: seq_oss: can't unregister synth\n"); - return -EINVAL; + return; } synth_devs[index] = NULL; if (index == max_synth_devs - 1) { @@ -160,8 +158,6 @@ snd_seq_oss_synth_remove(struct device *_dev) snd_use_lock_sync(&rec->use_lock); kfree(rec); - - return 0; } diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h index ffc40d8a7ef1..f52283904cba 100644 --- a/sound/core/seq/oss/seq_oss_synth.h +++ b/sound/core/seq/oss/seq_oss_synth.h @@ -15,8 +15,8 @@ #include void snd_seq_oss_synth_init(void); -int snd_seq_oss_synth_probe(struct device *dev); -int snd_seq_oss_synth_remove(struct device *dev); +int snd_seq_oss_synth_probe(struct snd_seq_device *dev); +void snd_seq_oss_synth_remove(struct snd_seq_device *dev); void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp); From a7b7afcc54cd51f60b840c173dc1223d1697750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 9 Dec 2025 13:38:51 +0100 Subject: [PATCH 009/341] ALSA: seq: Refuse to probe seq drivers with non-bus probe or remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all in-tree seq drivers are converted to bus methods, let old-style drivers fails to probe until driver methods are removed. Signed-off-by: Uwe Kleine-König Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/10adbd12b75984f6fd45e281438d475735cf5fdb.1765283601.git.u.kleine-koenig@baylibre.com --- sound/core/seq_device.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index 01def5b739a6..1b062d6b17ea 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -264,40 +264,17 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id, } EXPORT_SYMBOL(snd_seq_device_new); -static int snd_seq_driver_legacy_probe(struct snd_seq_device *sdev) -{ - struct device *dev = &sdev->dev; - struct device_driver *driver = dev->driver; - - return driver->probe(dev); -} - -static void snd_seq_driver_legacy_remove(struct snd_seq_device *sdev) -{ - struct device *dev = &sdev->dev; - struct device_driver *driver = dev->driver; - int ret; - - ret = driver->remove(dev); - if (unlikely(ret)) - dev_warn(dev, "Ignoring return value of remove callback (%pe)\n", ERR_PTR(ret)); -} - /* * driver registration */ int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) { - if (WARN_ON(!drv->driver.name || !drv->id)) + if (WARN_ON(!drv->driver.name || !drv->id || drv->driver.probe || drv->driver.remove)) return -EINVAL; + drv->driver.bus = &snd_seq_bus_type; drv->driver.owner = mod; - if (!drv->probe && drv->driver.probe) - drv->probe = snd_seq_driver_legacy_probe; - if (!drv->remove && drv->driver.remove) - drv->remove = snd_seq_driver_legacy_remove; - return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(__snd_seq_driver_register); From d6c160d5e86f4e7354dd6c3154b7cb562abc6c7d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 14 Nov 2025 07:58:48 +0000 Subject: [PATCH 010/341] ASoC: renesas: rz-ssi: Use dev variable in probe() Replace '&pdev->dev' by 'dev' in probe(), this makes few error paths shorter. Reviewed-by: Kuninori Morimoto Signed-off-by: Biju Das Link: https://patch.msgid.link/20251114075856.4751-2-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index f4dc2f68dead..845a55250d70 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -1119,19 +1119,16 @@ static int rz_ssi_probe(struct platform_device *pdev) audio_clk = devm_clk_get(dev, "audio_clk1"); if (IS_ERR(audio_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), - "no audio clk1"); + return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk1"); ssi->audio_clk_1 = clk_get_rate(audio_clk); audio_clk = devm_clk_get(dev, "audio_clk2"); if (IS_ERR(audio_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), - "no audio clk2"); + return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk2"); ssi->audio_clk_2 = clk_get_rate(audio_clk); if (!(ssi->audio_clk_1 || ssi->audio_clk_2)) - return dev_err_probe(&pdev->dev, -EINVAL, - "no audio clk1 or audio clk2"); + return dev_err_probe(dev, -EINVAL, "no audio clk1 or audio clk2"); ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2; From a472f0b157832fc91c83179b1628d8f660c84c82 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 14 Nov 2025 07:58:49 +0000 Subject: [PATCH 011/341] ASoC: renesas: rz-ssi: Remove trailing comma in the terminator entry Remove trailing comma in the terminator entry for OF table. While at it, add a space between the braces and comment block. Reviewed-by: Kuninori Morimoto Signed-off-by: Biju Das Link: https://patch.msgid.link/20251114075856.4751-3-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 845a55250d70..21f7d7c7c009 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -1244,7 +1244,7 @@ static void rz_ssi_remove(struct platform_device *pdev) static const struct of_device_id rz_ssi_of_match[] = { { .compatible = "renesas,rz-ssi", }, - {/* Sentinel */}, + { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, rz_ssi_of_match); From b541cb0a27dfa7504a8008320502f869c75f8bfc Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 14 Nov 2025 07:58:50 +0000 Subject: [PATCH 012/341] ASoC: renesas: rz-ssi: Move DMA configuration Move DMA configuration from rz_ssi_dma_request() to rz_ssi_dai_trigger() for supporting sample widths higher than 16. Signed-off-by: Biju Das Link: https://patch.msgid.link/20251114075856.4751-4-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 21f7d7c7c009..55aefff8857d 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -774,14 +774,6 @@ static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev) if (!rz_ssi_is_dma_enabled(ssi)) goto no_dma; - if (ssi->playback.dma_ch && - (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0)) - goto no_dma; - - if (ssi->capture.dma_ch && - (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0)) - goto no_dma; - return 0; no_dma: @@ -829,24 +821,27 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (cmd == SNDRV_PCM_TRIGGER_START) rz_ssi_stream_init(strm, substream); - if (ssi->dma_rt) { - bool is_playback; + if (rz_ssi_is_dma_enabled(ssi)) { + bool is_playback = rz_ssi_stream_is_play(substream); + + if (ssi->dma_rt) + ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, + is_playback); + else + ret = rz_ssi_dma_slave_config(ssi, strm->dma_ch, + is_playback); - is_playback = rz_ssi_stream_is_play(substream); - ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, - is_playback); /* Fallback to pio */ if (ret < 0) { ssi->playback.transfer = rz_ssi_pio_send; ssi->capture.transfer = rz_ssi_pio_recv; rz_ssi_release_dma_channels(ssi); + } else { + /* For DMA, queue up multiple DMA descriptors */ + num_transfer = 4; } } - /* For DMA, queue up multiple DMA descriptors */ - if (rz_ssi_is_dma_enabled(ssi)) - num_transfer = 4; - for (i = 0; i < num_transfer; i++) { ret = strm->transfer(ssi, strm); if (ret) From 9e10709f831408d948be66bc8f6329fa37a3dc82 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 14 Nov 2025 07:58:51 +0000 Subject: [PATCH 013/341] ASoC: renesas: rz-ssi: Add support for 24 bits sample width Add support for 24 bits sample format width for RZ/G2L SoCs. Signed-off-by: Biju Das Link: https://patch.msgid.link/20251114075856.4751-5-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 75 +++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 55aefff8857d..3ec7c9875b43 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -38,6 +38,7 @@ #define SSICR_MST BIT(14) #define SSICR_BCKP BIT(13) #define SSICR_LRCKP BIT(12) +#define SSICR_PDTA BIT(9) #define SSICR_CKDV(x) (((x) & 0xf) << 4) #define SSICR_TEN BIT(1) #define SSICR_REN BIT(0) @@ -74,7 +75,7 @@ #define PREALLOC_BUFFER_MAX (SZ_32K) #define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */ -#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE +#define SSI_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define SSI_CHAN_MIN 2 #define SSI_CHAN_MAX 2 #define SSI_FIFO_DEPTH 32 @@ -294,11 +295,24 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, } /* - * DWL: Data Word Length = 16 bits + * DWL: Data Word Length = {16, 24} bits * SWL: System Word Length = 32 bits */ ssicr |= SSICR_CKDV(clk_ckdv); - ssicr |= SSICR_DWL(1) | SSICR_SWL(3); + switch (ssi->hw_params_cache.sample_width) { + case 16: + ssicr |= SSICR_DWL(1); + break; + case 24: + ssicr |= SSICR_DWL(5) | SSICR_PDTA; + break; + default: + dev_err(ssi->dev, "Not support %u data width", + ssi->hw_params_cache.sample_width); + return -EINVAL; + } + + ssicr |= SSICR_SWL(3); rz_ssi_reg_writel(ssi, SSICR, ssicr); rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST); @@ -455,7 +469,6 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { struct snd_pcm_substream *substream = strm->substream; struct snd_pcm_runtime *runtime; - u16 *buf; int fifo_samples; int frames_left; int samples; @@ -490,12 +503,23 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) break; /* calculate new buffer index */ - buf = (u16 *)runtime->dma_area; - buf += strm->buffer_pos * runtime->channels; + if (ssi->hw_params_cache.sample_width == 16) { + u16 *buf; - /* Note, only supports 16-bit samples */ - for (i = 0; i < samples; i++) - *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); + buf = (u16 *)runtime->dma_area; + buf += strm->buffer_pos * runtime->channels; + + for (i = 0; i < samples; i++) + *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); + } else { + u32 *buf; + + buf = (u32 *)runtime->dma_area; + buf += strm->buffer_pos * runtime->channels; + + for (i = 0; i < samples; i++) + *buf++ = rz_ssi_reg_readl(ssi, SSIFRDR); + } rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); rz_ssi_pointer_update(strm, samples / runtime->channels); @@ -513,7 +537,6 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) int frames_left; int i; u32 ssifsr; - u16 *buf; if (!rz_ssi_stream_is_valid(ssi, strm)) return -EINVAL; @@ -542,12 +565,23 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) return 0; /* calculate new buffer index */ - buf = (u16 *)(runtime->dma_area); - buf += strm->buffer_pos * runtime->channels; + if (ssi->hw_params_cache.sample_width == 16) { + u16 *buf; - /* Note, only supports 16-bit samples */ - for (i = 0; i < samples; i++) - rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16)); + buf = (u16 *)(runtime->dma_area); + buf += strm->buffer_pos * runtime->channels; + + for (i = 0; i < samples; i++) + rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16)); + } else { + u32 *buf; + + buf = (u32 *)(runtime->dma_area); + buf += strm->buffer_pos * runtime->channels; + + for (i = 0; i < samples; i++) + rz_ssi_reg_writel(ssi, SSIFTDR, *buf++); + } rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0); rz_ssi_pointer_update(strm, samples / runtime->channels); @@ -658,8 +692,13 @@ static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi, cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; cfg.dst_addr = ssi->phys + SSIFTDR; cfg.src_addr = ssi->phys + SSIFRDR; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + if (ssi->hw_params_cache.sample_width == 16) { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } else { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } return dmaengine_slave_config(dma_ch, &cfg); } @@ -977,7 +1016,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, unsigned int rate = params_rate(params); int ret; - if (sample_bits != 16) { + if (!(sample_bits == 16 || sample_bits == 24)) { dev_err(ssi->dev, "Unsupported sample width: %d\n", sample_bits); return -EINVAL; From 124f6155f3d97b0e33f178c10a5138a42c8fd207 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 14 Nov 2025 07:58:52 +0000 Subject: [PATCH 014/341] ASoC: renesas: rz-ssi: Add support for 32 bits sample width Add support for 32 bits sample format width for RZ/G2L SoCs. Signed-off-by: Biju Das Link: https://patch.msgid.link/20251114075856.4751-6-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 3ec7c9875b43..5909778a6a70 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -75,7 +75,8 @@ #define PREALLOC_BUFFER_MAX (SZ_32K) #define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */ -#define SSI_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define SSI_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) #define SSI_CHAN_MIN 2 #define SSI_CHAN_MAX 2 #define SSI_FIFO_DEPTH 32 @@ -295,7 +296,7 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, } /* - * DWL: Data Word Length = {16, 24} bits + * DWL: Data Word Length = {16, 24, 32} bits * SWL: System Word Length = 32 bits */ ssicr |= SSICR_CKDV(clk_ckdv); @@ -306,6 +307,9 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, case 24: ssicr |= SSICR_DWL(5) | SSICR_PDTA; break; + case 32: + ssicr |= SSICR_DWL(6); + break; default: dev_err(ssi->dev, "Not support %u data width", ssi->hw_params_cache.sample_width); @@ -1016,7 +1020,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, unsigned int rate = params_rate(params); int ret; - if (!(sample_bits == 16 || sample_bits == 24)) { + if (!(sample_bits == 16 || sample_bits == 24 || sample_bits == 32)) { dev_err(ssi->dev, "Unsupported sample width: %d\n", sample_bits); return -EINVAL; From bd9e7182e36169cd7e1ea3b25b5c82b1c5698e64 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:53 +0100 Subject: [PATCH 015/341] ASoC: qcom: q6prm: Fix confusing cleanup.h syntax Commit de8e95773c48 ("ASoc: qcom: q6prm: Use automatic cleanup of kfree()") did not make the code simpler but more complicated. Already simple code of allocation and free, without any error paths, got now declaration with one constructor followed by another allocation, which is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-1-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6prm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 0b8fad0bc832..2544c4519b26 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -62,7 +62,6 @@ 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; - struct gpr_pkt *pkt __free(kfree) = NULL; if (enable) { opcode = PRM_CMD_REQUEST_HW_RSC; @@ -72,7 +71,8 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC; } - pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -111,10 +111,10 @@ static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_att struct apm_module_param_data *param_data; struct prm_cmd_request_rsc *req; gpr_device_t *gdev = prm->gdev; - struct gpr_pkt *pkt __free(kfree) = NULL; - pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id, - GPR_PRM_MODULE_IID); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, + gdev->svc.id, GPR_PRM_MODULE_IID); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -143,10 +143,10 @@ static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_att struct apm_module_param_data *param_data; struct prm_cmd_release_rsc *rel; gpr_device_t *gdev = prm->gdev; - struct gpr_pkt *pkt __free(kfree) = NULL; - pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id, - GPR_PRM_MODULE_IID); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, + gdev->svc.id, GPR_PRM_MODULE_IID); if (IS_ERR(pkt)) return PTR_ERR(pkt); From c862dc9019f517893eb83096076d7eed4ecbb372 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:54 +0100 Subject: [PATCH 016/341] ASoC: qcom: q6asm: Fix confusing cleanup.h syntax Commit 6e00112d31c8 ("ASoc: qcom: q6asm: Use automatic cleanup of kfree()") did not make the code simpler but more complicated. Already simple code of allocation and free, without any error paths, got now declaration with one constructor followed by another allocation, which is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-2-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index e7295b7b2461..890a1f786627 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -335,7 +335,6 @@ static int __q6asm_memory_unmap(struct audio_client *ac, struct q6asm *a = dev_get_drvdata(ac->dev->parent); struct apr_pkt *pkt; int rc, pkt_size; - void *p __free(kfree) = NULL; if (ac->port[dir].mem_map_handle == 0) { dev_err(ac->dev, "invalid mem handle\n"); @@ -343,7 +342,7 @@ static int __q6asm_memory_unmap(struct audio_client *ac, } pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap); - p = kzalloc(pkt_size, GFP_KERNEL); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -428,7 +427,6 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir, struct audio_port_data *port = NULL; struct audio_buffer *ab = NULL; struct apr_pkt *pkt; - void *p __free(kfree) = NULL; unsigned long flags; uint32_t num_regions, buf_sz; int i, pkt_size; @@ -447,7 +445,7 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir, pkt_size = APR_HDR_SIZE + sizeof(*cmd) + (sizeof(*mregions) * num_regions); - p = kzalloc(pkt_size, GFP_KERNEL); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; From 310e6f95eedaae04990072078adbb38beb149811 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:55 +0100 Subject: [PATCH 017/341] ASoC: qcom: q6apm: Fix confusing cleanup.h syntax Commit 89cf2223ee7b ("ASoc: qcom: q6apm: Use automatic cleanup of kfree()") did not make the code simpler but more complicated. Already simple code of allocation and free, without any error paths, got now declaration with one constructor followed by another allocation, which is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-3-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 94cc6376a367..4e5ad04ece50 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -259,7 +259,6 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) { struct apm_cmd_shared_mem_unmap_regions *cmd; struct audioreach_graph_data *data; - struct gpr_pkt *pkt __free(kfree) = NULL; int rc; if (dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -270,8 +269,9 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) if (!data->mem_map_handle) return 0; - pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir, - graph->port->id); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, + dir, graph->port->id); if (IS_ERR(pkt)) return PTR_ERR(pkt); From 3c84bfa47ff29ec0c202cb139d365421c6778d65 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:56 +0100 Subject: [PATCH 018/341] ASoC: qcom: q6afe: Fix confusing cleanup.h syntax Commit 55094e55ae36 ("ASoc: qcom: q6afe: Use automatic cleanup of kfree()") did not make the code simpler but more complicated. Already simple code of allocation and free, without any error paths, got now declaration with one constructor followed by another allocation, which is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-4-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 0b01fc9e13a7..0a13ebb11f16 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -1277,7 +1277,6 @@ int q6afe_port_stop(struct q6afe_port *port) int port_id = port->id; int ret = 0; int index, pkt_size; - void *p __free(kfree) = NULL; index = port->token; if (index < 0 || index >= AFE_PORT_MAX) { @@ -1286,7 +1285,7 @@ int q6afe_port_stop(struct q6afe_port *port) } pkt_size = APR_HDR_SIZE + sizeof(*stop); - p = kzalloc(pkt_size, GFP_KERNEL); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1667,7 +1666,6 @@ int q6afe_port_start(struct q6afe_port *port) int ret, param_id = port->cfg_type; struct apr_pkt *pkt; int pkt_size; - void *p __free(kfree) = NULL; ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id, AFE_MODULE_AUDIO_DEV_INTERFACE, @@ -1690,7 +1688,7 @@ int q6afe_port_start(struct q6afe_port *port) } pkt_size = APR_HDR_SIZE + sizeof(*start); - p = kzalloc(pkt_size, GFP_KERNEL); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; From 0e6071d656fb284e003a45ce158831d4d12aac5a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:57 +0100 Subject: [PATCH 019/341] ASoC: qcom: audioreach: Fix confusing cleanup.h syntax Commit 88a5f8e628ef ("ASoc: qcom: audioreach: Use automatic cleanup of kfree()") did not make the code simpler but more complicated. Already simple code of allocation and free, without any error paths, got now declaration with one constructor followed by another allocation, which is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-5-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index ded49124581b..329d916779f0 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -730,15 +730,15 @@ int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_modul uint32_t param_id, uint32_t param_val) { struct apm_module_param_data *param_data; - struct gpr_pkt *pkt __free(kfree) = NULL; uint32_t *param; int payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE; - void *p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); - if (IS_ERR(p)) + void *p; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) return -ENOMEM; - pkt = p; - p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; param_data = p; param_data->module_instance_id = module->instance_id; @@ -1043,7 +1043,6 @@ 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; - struct gpr_pkt *pkt __free(kfree) = NULL; if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); @@ -1052,7 +1051,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels); - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1090,7 +1090,6 @@ 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; - struct gpr_pkt *pkt __free(kfree) = NULL; void *p; if (num_channels > 4) { @@ -1100,8 +1099,9 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE; - pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, - graph->port->id, module->instance_id); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + graph->port->id, module->instance_id); if (IS_ERR(pkt)) return PTR_ERR(pkt); From 0bb160c92ad400c692984763996b758458adea17 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Nov 2025 14:17:58 +0100 Subject: [PATCH 020/341] ASoC: qcom: Minor readability improve with new lines Variables with automatic cleanup are special because they do not follow standard rules of declaration at top of function (see cleanup.h), but on the other hand we always expect line break between top-function declarations and first instructions. Don't pretend automatic cleanup variables are part of top-level declaration to improve readability when variable is followed by nun-NULL check. No functional impact, only style. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251129-asoc-wrong-cleanup-h-can-people-stop-sending-this-without-reading-docs-v1-6-c38b06884e39@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 8 +++++++- sound/soc/qcom/qdsp6/q6adm.c | 2 ++ sound/soc/qcom/qdsp6/q6afe.c | 4 ++++ sound/soc/qcom/qdsp6/q6apm.c | 3 +++ sound/soc/qcom/qdsp6/q6asm.c | 13 +++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 329d916779f0..f3fa0a5b4095 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -617,6 +617,7 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, int fs_sz = APM_FS_CFG_PSIZE; int size = ic_sz + ep_sz + fs_sz; 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); @@ -675,6 +676,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; + 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); @@ -788,6 +790,7 @@ static int audioreach_set_module_config(struct q6apm_graph *graph, { int size = le32_to_cpu(module->data->size); 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); @@ -810,6 +813,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, APM_MODULE_PARAM_DATA_SIZE; int i; void *p; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -922,13 +926,13 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu void *p; int iid = q6apm_graph_get_rx_shmem_module_iid(graph); int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, 0, graph->port->id, iid); if (IS_ERR(pkt)) return -ENOMEM; - p = (void *)pkt + GPR_HDR_SIZE; header = p; rc = audioreach_set_compr_media_format(header, p, mcfg); @@ -952,6 +956,7 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, int fs_sz = APM_FS_CFG_PSIZE; int size = ic_sz + ep_sz + fs_sz; 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); @@ -1013,6 +1018,7 @@ static int audioreach_logging_set_media_format(struct q6apm_graph *graph, struct data_logging_config *cfg; int size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE; 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); diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 0b8d06ec8b26..bbe986293ec3 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -331,6 +331,7 @@ static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp, int afe_port = q6afe_get_port_id(port_id); struct apr_pkt *pkt; int ret, pkt_size = APR_HDR_SIZE + sizeof(*open); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -466,6 +467,7 @@ int q6adm_matrix_map(struct device *dev, int path, struct q6copp *copp; int pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) + (sizeof(uint32_t) * payload_map.num_copps)); + void *matrix_map __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!matrix_map) return -ENOMEM; diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 0a13ebb11f16..a50477056e7b 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -1077,6 +1077,7 @@ static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port, struct apr_pkt *pkt; int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize; void *pl; + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1128,6 +1129,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, u16 port_id = port->id; int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize; void *pl; + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1832,6 +1834,7 @@ int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, struct apr_pkt *pkt; int ret = 0; int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1866,6 +1869,7 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, struct apr_pkt *pkt; int ret = 0; int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 4e5ad04ece50..e30f8648ae15 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -100,6 +100,7 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op struct audioreach_sub_graph *sg; struct q6apm *apm = graph->apm; int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -409,6 +410,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; struct audio_buffer *ab; int iid = q6apm_graph_get_rx_shmem_module_iid(graph); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), @@ -446,6 +448,7 @@ int q6apm_read(struct q6apm_graph *graph) struct audioreach_graph_data *port; struct audio_buffer *ab; int iid = q6apm_graph_get_tx_shmem_module_iid(graph); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, graph->tx_data.dsp_buf, graph->port->id, iid); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 890a1f786627..420176f80ffe 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -928,6 +928,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; int rc, pkt_size = APR_HDR_SIZE + sizeof(*open); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1005,6 +1006,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t stream_id, struct asm_session_cmd_run_v2 *run; struct apr_pkt *pkt; int rc, pkt_size = APR_HDR_SIZE + sizeof(*run); + void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; @@ -1087,6 +1089,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, struct apr_pkt *pkt; u8 *channel_mapping; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1125,6 +1128,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, struct asm_flac_fmt_blk_v2 *fmt; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1156,6 +1160,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, struct asm_wmastdv9_fmt_blk_v2 *fmt; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1188,6 +1193,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, struct asm_wmaprov10_fmt_blk_v2 *fmt; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1221,6 +1227,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, struct asm_alac_fmt_blk_v2 *fmt; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1257,6 +1264,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, struct asm_ape_fmt_blk_v2 *fmt; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*fmt); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1291,6 +1299,7 @@ static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_ uint32_t *samples; struct apr_pkt *pkt; int rc, pkt_size = APR_HDR_SIZE + sizeof(uint32_t); + void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; @@ -1349,6 +1358,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, u8 *channel_mapping; u32 frames_per_buf = 0; int pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1395,6 +1405,7 @@ int q6asm_read(struct audio_client *ac, uint32_t stream_id) unsigned long flags; int pkt_size = APR_HDR_SIZE + sizeof(*read); int rc = 0; + void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; @@ -1437,6 +1448,7 @@ static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id, struct asm_stream_cmd_open_read_v3 *open; struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*open); + void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; @@ -1507,6 +1519,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, struct apr_pkt *pkt; int pkt_size = APR_HDR_SIZE + sizeof(*write); int rc = 0; + void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; From 5a7e236925b417332a674a8488ef19da233a5764 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Dec 2025 17:12:38 +0100 Subject: [PATCH 021/341] ASoC: amd: acp-sdw-legacy: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251203-asoc-wrong-cleanup-h-continued-v1-1-5142be4874fb@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 8 ++++---- 1 file 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 fae94b9edd5a..9cb55d592c3c 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -358,8 +358,6 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; - struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL; - struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL; struct snd_soc_aux_dev *soc_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; @@ -380,12 +378,14 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) num_confs = num_ends; /* One per DAI link, worst case is a DAI link for every endpoint */ - soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL); + struct asoc_sdw_dailink *soc_dais __free(kfree) = + kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL); if (!soc_dais) return -ENOMEM; /* One per endpoint, ie. each DAI on each codec/amp */ - soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL); + struct asoc_sdw_endpoint *soc_ends __free(kfree) = + kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL); if (!soc_ends) return -ENOMEM; From bafd5cf04b2822bf3c865add7262d86e22b9588e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Dec 2025 17:12:39 +0100 Subject: [PATCH 022/341] ASoC: amd: acp-sdw-sof: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251203-asoc-wrong-cleanup-h-continued-v1-2-5142be4874fb@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-sof-mach.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index 5677ae63fca9..da815b3f6389 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -270,8 +270,6 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; - struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL; - struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL; struct snd_soc_aux_dev *sof_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; @@ -289,12 +287,14 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) } /* One per DAI link, worst case is a DAI link for every endpoint */ - sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL); + struct asoc_sdw_dailink *sof_dais __free(kfree) = + kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL); if (!sof_dais) return -ENOMEM; /* One per endpoint, ie. each DAI on each codec/amp */ - sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL); + struct asoc_sdw_endpoint *sof_ends __free(kfree) = + kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL); if (!sof_ends) return -ENOMEM; From e39011184f23de3d04ca8e80b4df76c9047b4026 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Dec 2025 17:12:40 +0100 Subject: [PATCH 023/341] ASoC: SDCA: functions: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251203-asoc-wrong-cleanup-h-continued-v1-3-5142be4874fb@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 5a1f120487ef..acac066f1d8d 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1184,7 +1184,6 @@ static int find_sdca_entity_pde(struct device *dev, { static const int mult_delay = 3; struct sdca_entity_pde *power = &entity->pde; - u32 *delay_list __free(kfree) = NULL; struct sdca_pde_delay *delays; int num_delays; int i, j; @@ -1205,7 +1204,8 @@ static int find_sdca_entity_pde(struct device *dev, return -EINVAL; } - delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL); + u32 *delay_list __free(kfree) = kcalloc(num_delays, sizeof(*delay_list), + GFP_KERNEL); if (!delay_list) return -ENOMEM; @@ -1250,7 +1250,6 @@ static int find_sdca_entity_ge(struct device *dev, struct sdca_entity *entity) { struct sdca_entity_ge *group = &entity->ge; - u8 *affected_list __free(kfree) = NULL; u8 *affected_iter; int num_affected; int i, j; @@ -1269,7 +1268,8 @@ static int find_sdca_entity_ge(struct device *dev, return -EINVAL; } - affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL); + u8 *affected_list __free(kfree) = kcalloc(num_affected, sizeof(*affected_list), + GFP_KERNEL); if (!affected_list) return -ENOMEM; @@ -1495,7 +1495,6 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, struct fwnode_handle *function_node, struct sdca_function_data *function) { - u32 *entity_list __free(kfree) = NULL; struct sdca_entity *entities; int num_entities; int i, ret; @@ -1517,7 +1516,8 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, if (!entities) return -ENOMEM; - entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL); + u32 *entity_list __free(kfree) = kcalloc(num_entities, sizeof(*entity_list), + GFP_KERNEL); if (!entity_list) return -ENOMEM; @@ -1642,7 +1642,6 @@ static int find_sdca_entity_connection_pde(struct device *dev, struct sdca_entity *entity) { struct sdca_entity_pde *power = &entity->pde; - u32 *managed_list __free(kfree) = NULL; struct sdca_entity **managed; int num_managed; int i; @@ -1664,7 +1663,8 @@ static int find_sdca_entity_connection_pde(struct device *dev, if (!managed) return -ENOMEM; - managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL); + u32 *managed_list __free(kfree) = kcalloc(num_managed, sizeof(*managed_list), + GFP_KERNEL); if (!managed_list) return -ENOMEM; @@ -1961,7 +1961,6 @@ static int find_sdca_clusters(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { - u32 *cluster_list __free(kfree) = NULL; struct sdca_cluster *clusters; int num_clusters; int i, ret; @@ -1982,7 +1981,8 @@ static int find_sdca_clusters(struct device *dev, if (!clusters) return -ENOMEM; - cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL); + u32 *cluster_list __free(kfree) = kcalloc(num_clusters, sizeof(*cluster_list), + GFP_KERNEL); if (!cluster_list) return -ENOMEM; @@ -2026,7 +2026,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, { static const int mult_fileset = 3; char fileset_name[SDCA_PROPERTY_LENGTH]; - u32 *filesets_list __free(kfree) = NULL; struct sdca_fdl_set *sets; int num_sets; int i, j; @@ -2041,7 +2040,8 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, return num_sets; } - filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL); + u32 *filesets_list __free(kfree) = kcalloc(num_sets, sizeof(u32), + GFP_KERNEL); if (!filesets_list) return -ENOMEM; @@ -2053,7 +2053,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, return -ENOMEM; for (i = 0; i < num_sets; i++) { - u32 *fileset_entries __free(kfree) = NULL; struct sdca_fdl_set *set = &sets[i]; struct sdca_fdl_file *files; int num_files, num_entries; @@ -2079,7 +2078,8 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, if (!files) return -ENOMEM; - fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL); + u32 *fileset_entries __free(kfree) = kcalloc(num_entries, sizeof(u32), + GFP_KERNEL); if (!fileset_entries) return -ENOMEM; From 384b13038715f16713d1b2bfe5fb927c8437e48b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 12 Dec 2025 11:38:54 +0100 Subject: [PATCH 024/341] ASoC: Intel: catpt: Move IPC error messages one level down Code size can be reduced if catpt_dsp_do_send_msg() takes responsibility for dumping logs in case of an IPC message failure. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20251212103858.110701-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 4 +- sound/soc/intel/catpt/ipc.c | 12 +++-- sound/soc/intel/catpt/messages.c | 89 +++++--------------------------- 3 files changed, 21 insertions(+), 84 deletions(-) diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index c01d27e9fd88..7b7c30a0d2ac 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -133,9 +133,9 @@ irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id); int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, struct catpt_ipc_msg request, - struct catpt_ipc_msg *reply, int timeout); + struct catpt_ipc_msg *reply, int timeout, const char *name); int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, - struct catpt_ipc_msg *reply); + struct catpt_ipc_msg *reply, const char *name); int catpt_first_boot_firmware(struct catpt_dev *cdev); int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index d26863249097..5a01a9afb26e 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -84,7 +84,7 @@ static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout) static int catpt_dsp_do_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, - struct catpt_ipc_msg *reply, int timeout) + struct catpt_ipc_msg *reply, int timeout, const char *name) { struct catpt_ipc *ipc = &cdev->ipc; unsigned long flags; @@ -111,6 +111,8 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev, } ret = ipc->rx.rsp.status; + if (ret) + dev_err(cdev->dev, "%s (0x%08x) failed: %d\n", name, request.header, ret); if (reply) { reply->header = ipc->rx.header; @@ -123,23 +125,23 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev, int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, struct catpt_ipc_msg request, - struct catpt_ipc_msg *reply, int timeout) + struct catpt_ipc_msg *reply, int timeout, const char *name) { struct catpt_ipc *ipc = &cdev->ipc; int ret; mutex_lock(&ipc->mutex); - ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout); + ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout, name); mutex_unlock(&ipc->mutex); return ret; } int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, - struct catpt_ipc_msg *reply) + struct catpt_ipc_msg *reply, const char *name) { return catpt_dsp_send_msg_timeout(cdev, request, reply, - cdev->ipc.default_timeout); + cdev->ipc.default_timeout, name); } static void diff --git a/sound/soc/intel/catpt/messages.c b/sound/soc/intel/catpt/messages.c index 30eec2de4dc1..688a2d79500d 100644 --- a/sound/soc/intel/catpt/messages.c +++ b/sound/soc/intel/catpt/messages.c @@ -15,17 +15,12 @@ int catpt_ipc_get_fw_version(struct catpt_dev *cdev, { union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION); struct catpt_ipc_msg request = {{0}}, reply; - int ret; request.header = msg.val; reply.size = sizeof(*version); reply.data = version; - ret = catpt_dsp_send_msg(cdev, request, &reply); - if (ret) - dev_err(cdev->dev, "get fw version failed: %d\n", ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, &reply, "get fw version"); } struct catpt_alloc_stream_input { @@ -94,11 +89,7 @@ int catpt_ipc_alloc_stream(struct catpt_dev *cdev, reply.size = sizeof(*sinfo); reply.data = sinfo; - ret = catpt_dsp_send_msg(cdev, request, &reply); - if (ret) - dev_err(cdev->dev, "alloc stream type %d failed: %d\n", - type, ret); - + ret = catpt_dsp_send_msg(cdev, request, &reply, "alloc stream"); kfree(payload); return ret; } @@ -107,18 +98,12 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id) { union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM); struct catpt_ipc_msg request; - int ret; request.header = msg.val; request.size = sizeof(stream_hw_id); request.data = &stream_hw_id; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "free stream %d failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "free stream"); } int catpt_ipc_set_device_format(struct catpt_dev *cdev, @@ -126,17 +111,12 @@ int catpt_ipc_set_device_format(struct catpt_dev *cdev, { union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS); struct catpt_ipc_msg request; - int ret; request.header = msg.val; request.size = sizeof(*devfmt); request.data = devfmt; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "set device format failed: %d\n", ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "set device format"); } int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state, @@ -144,7 +124,6 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state, { union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE); struct catpt_ipc_msg request, reply; - int ret; request.header = msg.val; request.size = sizeof(state); @@ -152,11 +131,7 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state, reply.size = sizeof(*context); reply.data = context; - ret = catpt_dsp_send_msg(cdev, request, &reply); - if (ret) - dev_err(cdev->dev, "enter dx state failed: %d\n", ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, &reply, "enter dx state"); } int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev, @@ -164,68 +139,45 @@ int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev, { union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO); struct catpt_ipc_msg request = {{0}}, reply; - int ret; request.header = msg.val; reply.size = sizeof(*info); reply.data = info; - ret = catpt_dsp_send_msg(cdev, request, &reply); - if (ret) - dev_err(cdev->dev, "get mixer info failed: %d\n", ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, &reply, "get mixer info"); } int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id) { union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM); struct catpt_ipc_msg request = {{0}}; - int ret; msg.stream_hw_id = stream_hw_id; request.header = msg.val; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "reset stream %d failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "reset stream"); } int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id) { union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM); struct catpt_ipc_msg request = {{0}}; - int ret; msg.stream_hw_id = stream_hw_id; request.header = msg.val; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "pause stream %d failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "pause stream"); } int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id) { union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM); struct catpt_ipc_msg request = {{0}}; - int ret; msg.stream_hw_id = stream_hw_id; request.header = msg.val; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "resume stream %d failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "resume stream"); } struct catpt_set_volume_input { @@ -243,7 +195,6 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id, union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME); struct catpt_ipc_msg request; struct catpt_set_volume_input input; - int ret; msg.stream_hw_id = stream_hw_id; input.channel = channel; @@ -255,12 +206,7 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id, request.size = sizeof(input); request.data = &input; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "set stream %d volume failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "set stream volume"); } struct catpt_set_write_pos_input { @@ -275,7 +221,6 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id, union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION); struct catpt_ipc_msg request; struct catpt_set_write_pos_input input; - int ret; msg.stream_hw_id = stream_hw_id; input.new_write_pos = pos; @@ -286,28 +231,18 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id, request.size = sizeof(input); request.data = &input; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "set stream %d write pos failed: %d\n", - stream_hw_id, ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "set stream write pos"); } int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute) { union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK); struct catpt_ipc_msg request; - int ret; msg.stream_hw_id = stream_hw_id; request.header = msg.val; request.size = sizeof(mute); request.data = &mute; - ret = catpt_dsp_send_msg(cdev, request, NULL); - if (ret) - dev_err(cdev->dev, "mute loopback failed: %d\n", ret); - - return ret; + return catpt_dsp_send_msg(cdev, request, NULL, "mute loopback"); } From eded4483b8a21eaeb0886ef6f961ccf4e0d9c976 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 12 Dec 2025 11:38:55 +0100 Subject: [PATCH 025/341] ASoC: Intel: catpt: Update CATPT_IPC_ERROR macro Make it easier for functions that call IPC handlers to deal with their results by accounting for '0' (success) code. Rename the macro to reflect this behaviour change. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20251212103858.110701-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 2 +- sound/soc/intel/catpt/device.c | 4 ++-- sound/soc/intel/catpt/loader.c | 2 +- sound/soc/intel/catpt/pcm.c | 24 ++++++++++-------------- sound/soc/intel/catpt/sysfs.c | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 7b7c30a0d2ac..d273f24d3d53 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -129,7 +129,7 @@ irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id); * HOST <-> DSP communication yet failure to process specific request. * Use below macro to convert returned non-zero values appropriately */ -#define CATPT_IPC_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO) +#define CATPT_IPC_RET(ret) (((ret) <= 0) ? (ret) : -EREMOTEIO) int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, struct catpt_ipc_msg request, diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index d13062c8e907..d8e0da558495 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -41,7 +41,7 @@ static int catpt_do_suspend(struct device *dev) 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_ERROR(ret); + ret = CATPT_IPC_RET(ret); goto release_dma_chan; } @@ -107,7 +107,7 @@ static int catpt_resume(struct device *dev) ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); } return 0; diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index f5705cd2c1e1..80c4ab335525 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -656,7 +656,7 @@ int catpt_first_boot_firmware(struct catpt_dev *cdev) ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); ret = catpt_arm_stream_templates(cdev); if (ret) { diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index abd1cb07c60c..cc43346f83af 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -365,9 +365,7 @@ static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK) return catpt_set_dspvol(cdev, id, (long *)pos->private_value); ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value); - if (ret) - return CATPT_IPC_ERROR(ret); - return 0; + return CATPT_IPC_RET(ret); } static int catpt_dai_hw_params(struct snd_pcm_substream *substream, @@ -414,7 +412,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, cdev->scratch, &stream->info); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); ret = catpt_dai_apply_usettings(dai, stream); if (ret) { @@ -456,11 +454,11 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream, ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); stream->prepared = true; return 0; @@ -491,7 +489,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, pos, false, false); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); fallthrough; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -499,7 +497,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, catpt_dsp_update_lpclock(cdev); ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); break; case SNDRV_PCM_TRIGGER_STOP: @@ -510,7 +508,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id); catpt_dsp_update_lpclock(cdev); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); break; default: @@ -679,7 +677,7 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, pm_runtime_put_autosuspend(cdev->dev); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); /* store device format set for given SSP */ memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)); @@ -849,9 +847,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) } } - if (ret) - return CATPT_IPC_ERROR(ret); - return 0; + return CATPT_IPC_RET(ret); } static int catpt_volume_info(struct snd_kcontrol *kcontrol, @@ -1041,7 +1037,7 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, pm_runtime_put_autosuspend(cdev->dev); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); *(bool *)kcontrol->private_value = mute; return 0; diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c index e961e172f9b7..0cb122a4dfd2 100644 --- a/sound/soc/intel/catpt/sysfs.c +++ b/sound/soc/intel/catpt/sysfs.c @@ -24,7 +24,7 @@ static ssize_t fw_version_show(struct device *dev, pm_runtime_put_autosuspend(cdev->dev); if (ret) - return CATPT_IPC_ERROR(ret); + return CATPT_IPC_RET(ret); return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major, version.minor, version.build); From d44f62b09b1e97baee3b10484a1c3c203bb83caf Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 12 Dec 2025 11:38:56 +0100 Subject: [PATCH 026/341] ASoC: Intel: catpt: Simplify catpt_stream_read_position() Add position to the argument list to simplify the wrapper. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20251212103858.110701-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index cc43346f83af..cbb1c3942409 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -114,14 +114,10 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) return result; } -static u32 catpt_stream_read_position(struct catpt_dev *cdev, - struct catpt_stream_runtime *stream) +static void catpt_stream_read_position(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream, u32 *pos) { - u32 pos; - - memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr, - sizeof(pos)); - return pos; + memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos)); } static u32 catpt_stream_volume(struct catpt_dev *cdev, @@ -615,7 +611,7 @@ catpt_component_pointer(struct snd_soc_component *component, return 0; stream = snd_soc_dai_get_dma_data(cpu_dai, substream); - pos = catpt_stream_read_position(cdev, stream); + catpt_stream_read_position(cdev, stream, &pos); return bytes_to_frames(substream->runtime, pos); } From e97e07138f956a551895a9556d1a929978a5346d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 12 Dec 2025 11:38:57 +0100 Subject: [PATCH 027/341] ASoC: Intel: catpt: Specify image names in the device descriptor State files to load explicitly in the device descriptor instead of hiding the details within a loading function. Apart from readability, this also reduces the catpt module size slightly. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20251212103858.110701-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 1 + sound/soc/intel/catpt/device.c | 2 ++ sound/soc/intel/catpt/loader.c | 6 +----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index d273f24d3d53..df8a5fd95e13 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -62,6 +62,7 @@ struct catpt_module_type { struct catpt_spec { struct snd_soc_acpi_mach *machines; u8 core_id; + const char *fw_name; u32 host_dram_offset; u32 host_iram_offset; u32 host_shim_offset; diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index d8e0da558495..0638aecba40d 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -348,6 +348,7 @@ static struct snd_soc_acpi_mach wpt_machines[] = { static struct catpt_spec lpt_desc = { .machines = lpt_machines, .core_id = 0x01, + .fw_name = "intel/IntcSST1.bin", .host_dram_offset = 0x000000, .host_iram_offset = 0x080000, .host_shim_offset = 0x0E7000, @@ -363,6 +364,7 @@ static struct catpt_spec lpt_desc = { static struct catpt_spec wpt_desc = { .machines = wpt_machines, .core_id = 0x02, + .fw_name = "intel/IntcSST2.bin", .host_dram_offset = 0x000000, .host_iram_offset = 0x0A0000, .host_shim_offset = 0x0FB000, diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 80c4ab335525..dc7afe587e6f 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -580,10 +580,6 @@ release_fw: static int catpt_load_images(struct catpt_dev *cdev, bool restore) { - static const char *const names[] = { - "intel/IntcSST1.bin", - "intel/IntcSST2.bin", - }; struct dma_chan *chan; int ret; @@ -591,7 +587,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, names[cdev->spec->core_id - 1], + ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, FW_SIGNATURE, restore); if (ret) goto release_dma_chan; From aa30193af8873b3ccfd70a4275336ab6cbd4e5e6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 12 Dec 2025 11:38:58 +0100 Subject: [PATCH 028/341] ASoC: Intel: catpt: Drop superfluous space in PCM code Those spaces are redundant. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20251212103858.110701-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index cbb1c3942409..2c3405686f79 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -687,7 +687,7 @@ static const struct snd_soc_dai_ops catpt_dai_ops = { static struct snd_soc_dai_driver dai_drivers[] = { /* FE DAIs */ { - .name = "System Pin", + .name = "System Pin", .id = CATPT_STRM_TYPE_SYSTEM, .ops = &catpt_fe_dai_ops, .playback = { @@ -710,7 +710,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { }, }, { - .name = "Offload0 Pin", + .name = "Offload0 Pin", .id = CATPT_STRM_TYPE_RENDER, .ops = &catpt_fe_dai_ops, .playback = { @@ -724,7 +724,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { }, }, { - .name = "Offload1 Pin", + .name = "Offload1 Pin", .id = CATPT_STRM_TYPE_RENDER, .ops = &catpt_fe_dai_ops, .playback = { @@ -738,7 +738,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { }, }, { - .name = "Loopback Pin", + .name = "Loopback Pin", .id = CATPT_STRM_TYPE_LOOPBACK, .ops = &catpt_fe_dai_ops, .capture = { @@ -752,7 +752,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { }, }, { - .name = "Bluetooth Pin", + .name = "Bluetooth Pin", .id = CATPT_STRM_TYPE_BLUETOOTH_RENDER, .ops = &catpt_fe_dai_ops, .playback = { From 774d075a80c652a0f35a5dd6f9e35cac5b7f6bdd Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Mon, 17 Nov 2025 18:18:48 +0530 Subject: [PATCH 029/341] ASoC: fsl: fsl_ssi: Replace deprecated strcpy() with strscpy() strcpy() is deprecated,use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/29c40b5a-3e4d-e89d-ca22-a1059cca3480@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 320108bebf30..b2e1da1781ae 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1447,7 +1447,7 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi) dev_err(dev, "failed to get SSI index property\n"); return -EINVAL; } - strcpy(ssi->card_name, "ac97-codec"); + strscpy(ssi->card_name, "ac97-codec"); } else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) { /* * In synchronous mode, STCK and STFS ports are used by RX From d00e80606e76233f4ae03486a9809c9edfe8b27e Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Fri, 21 Nov 2025 19:39:43 +0530 Subject: [PATCH 030/341] ASoC: codec: rt274: Use devm_request_threaded_irq to manage IRQ lifetime and fix smatch warning Replace manual "request_threaded_irq()" with the device managed "devm_request_threaded_irq" to manage the IRQ lifetime and also it removes the smatch reported warning. Remove the manual "free_irq()" in the "remove" function as free_irq is tied to device teardown. Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251121140940.40678-4-hariconscious@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt274.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 5c33aeaced2f..bba714020c70 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1189,7 +1189,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c) regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82); if (rt274->i2c->irq) { - ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq, + ret = devm_request_threaded_irq(&rt274->i2c->dev, rt274->i2c->irq, NULL, rt274_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); if (ret != 0) { dev_err(&i2c->dev, @@ -1205,15 +1205,6 @@ static int rt274_i2c_probe(struct i2c_client *i2c) return ret; } -static void rt274_i2c_remove(struct i2c_client *i2c) -{ - struct rt274_priv *rt274 = i2c_get_clientdata(i2c); - - if (i2c->irq) - free_irq(i2c->irq, rt274); -} - - static struct i2c_driver rt274_i2c_driver = { .driver = { .name = "rt274", @@ -1223,7 +1214,6 @@ static struct i2c_driver rt274_i2c_driver = { #endif }, .probe = rt274_i2c_probe, - .remove = rt274_i2c_remove, .id_table = rt274_i2c_id, }; From 69927c13d5c5444f5f774e891fa5970ae1bac4b5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 3 Dec 2025 05:27:55 +0000 Subject: [PATCH 031/341] ASoC: mediatek: mt8189-nau8825: don't use card->dapm directly We should get dapm via snd_soc_card_to_dapm(card), and use it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87bjkgnnhg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8189/mt8189-nau8825.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c index 5ef15ec988be..e849e7a649bc 100644 --- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c +++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c @@ -342,9 +342,10 @@ static const struct snd_soc_ops mt8189_es8326_ops = { static int mt8189_dumb_amp_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); int ret; - ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_dumb_spk_widgets, + ret = snd_soc_dapm_new_controls(dapm, mt8189_dumb_spk_widgets, ARRAY_SIZE(mt8189_dumb_spk_widgets)); if (ret) { dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret); @@ -418,10 +419,11 @@ static int mt8189_headset_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_HEADSET]; struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; struct mtk_platform_card_data *card_data = soc_card_data->card_data; + struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); int ret; int type; - ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_headset_widgets, + ret = snd_soc_dapm_new_controls(dapm, mt8189_headset_widgets, ARRAY_SIZE(mt8189_headset_widgets)); if (ret) { dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret); From 7a9fa7fda93b7b3ae515f40f67bbf8e1d16337e8 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 2 Dec 2025 11:34:25 +0000 Subject: [PATCH 032/341] firmware: cs_dsp: Remove __free() from cs_dsp_debugfs_string_read() Don't use __free(kfree) in cs_dsp_debugfs_string_read. Instead use normal kfree() to cleanup. The use of __free() can create new cleanup bugs that are difficult to spot because the defective code is idiomatically correct regular C. This function used the suspect declaration __free(kfree) = NULL;. The __free(kfree) didn't really do anything here. The function can be rearranged to avoid any need to return or goto within the code. Signed-off-by: Richard Fitzgerald Fixes: 3045e29d248b ("firmware: cs_dsp: Append \n to debugfs string during read") Link: https://patch.msgid.link/20251202113425.413700-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 525ac0f0a75d..8d2e7267d973 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -375,18 +375,23 @@ static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp, size_t count, loff_t *ppos, const char **pstr) { - const char *str __free(kfree) = NULL; + const char *str; + ssize_t ret = 0; scoped_guard(mutex, &dsp->pwr_lock) { - if (!*pstr) - return 0; - - str = kasprintf(GFP_KERNEL, "%s\n", *pstr); - if (!str) - return -ENOMEM; - - return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); + if (*pstr) { + str = kasprintf(GFP_KERNEL, "%s\n", *pstr); + if (str) { + ret = simple_read_from_buffer(user_buf, count, + ppos, str, strlen(str)); + kfree(str); + } else { + ret = -ENOMEM; + } + } } + + return ret; } static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file, From 180cdb96e821e30528b02708b927c93daa0ed40b Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Fri, 12 Dec 2025 10:14:09 +0530 Subject: [PATCH 033/341] ASoC: mediatek: mt8195: optimize property formatting error handling by using scnprintf() Replace snprintf() with scnprintf() when constructing the property and remove negative return error handling as scnprintf() returns the actual number of bytes written to buffer. snprintf() as defined by the C99 standard,returns the number of characters that *would have been* written if enough space were available.Use scnprintf() that returns the actual number of characters written. Link: https://github.com/KSPP/linux/issues/105 Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251212044408.1286-2-hariconscious@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-dai-etdm.c | 44 ++++++--------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c index 723cab01e72e..5dcc8ed26e00 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c @@ -2651,14 +2651,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe) etdm_data = afe_priv->dai_priv[dai_id]; - ret = snprintf(prop, sizeof(prop), - "mediatek,%s-mclk-always-on-rate", - of_afe_etdms[i].name); - if (ret < 0) { - dev_info(afe->dev, "%s snprintf err=%d\n", - __func__, ret); - return; - } + scnprintf(prop, sizeof(prop), + "mediatek,%s-mclk-always-on-rate", + of_afe_etdms[i].name); ret = of_property_read_u32(of_node, prop, &sel); if (ret == 0) { etdm_data->mclk_dir = SND_SOC_CLOCK_OUT; @@ -2667,24 +2662,14 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe) __func__, sel); } - ret = snprintf(prop, sizeof(prop), - "mediatek,%s-multi-pin-mode", - of_afe_etdms[i].name); - if (ret < 0) { - dev_info(afe->dev, "%s snprintf err=%d\n", - __func__, ret); - return; - } + scnprintf(prop, sizeof(prop), + "mediatek,%s-multi-pin-mode", + of_afe_etdms[i].name); etdm_data->data_mode = of_property_read_bool(of_node, prop); - ret = snprintf(prop, sizeof(prop), - "mediatek,%s-cowork-source", - of_afe_etdms[i].name); - if (ret < 0) { - dev_info(afe->dev, "%s snprintf err=%d\n", - __func__, ret); - return; - } + scnprintf(prop, sizeof(prop), + "mediatek,%s-cowork-source", + of_afe_etdms[i].name); ret = of_property_read_u32(of_node, prop, &sel); if (ret == 0) { if (sel >= MT8195_AFE_IO_ETDM_NUM) { @@ -2706,14 +2691,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe) dai_id = ETDM_TO_DAI_ID(i); etdm_data = afe_priv->dai_priv[dai_id]; - ret = snprintf(prop, sizeof(prop), - "mediatek,%s-chn-disabled", - of_afe_etdms[i].name); - if (ret < 0) { - dev_info(afe->dev, "%s snprintf err=%d\n", - __func__, ret); - return; - } + scnprintf(prop, sizeof(prop), + "mediatek,%s-chn-disabled", + of_afe_etdms[i].name); ret = of_property_read_variable_u8_array(of_node, prop, disable_chn, 1, max_chn); From 0c1db366642172e85ee98eeed7c127b80eb609a3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Dec 2025 10:05:34 +0100 Subject: [PATCH 034/341] ASoC: davinci-mcasp: remove unneeded #ifdef The enablement of the CONFIG_OF_GPIO switch has nothing to do with the "gpio-controller" property which may as well come from software nodes and GPIOLIB can still be enabled separately. This driver does not call any symbols from gpiolib-of.h so has no need to check this option at all. Just use the generic device property accessor. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20251205090534.27845-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 621a9d5f9377..db6913c05378 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1875,11 +1876,7 @@ err1: static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp) { -#ifdef CONFIG_OF_GPIO - return of_property_read_bool(mcasp->dev->of_node, "gpio-controller"); -#else - return false; -#endif + return device_property_present(mcasp->dev, "gpio-controller"); } static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, From dfbbd3c04f0f782e83f8677749e2f02359ffd1b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 3 Dec 2025 11:55:42 +0100 Subject: [PATCH 035/341] ASoc: qcom: q6afe: use guards consistently A recent change switched to using guards for the port list lock but only modified two out of three functions where the lock is held. Convert also the third function for consistency while switching to a scoped guard in q6afe_port_get_from_id() for clarity. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251203105542.24765-3-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 0b01fc9e13a7..d8173cd9e60a 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -931,13 +931,11 @@ static void q6afe_port_free(struct kref *ref) { struct q6afe_port *port; struct q6afe *afe; - unsigned long flags; port = container_of(ref, struct q6afe_port, refcount); afe = port->afe; - spin_lock_irqsave(&afe->port_list_lock, flags); - list_del(&port->node); - spin_unlock_irqrestore(&afe->port_list_lock, flags); + scoped_guard(spinlock_irqsave, &afe->port_list_lock) + list_del(&port->node); kfree(port->scfg); kfree(port); } @@ -1807,8 +1805,8 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) port->cfg_type = cfg_type; kref_init(&port->refcount); - guard(spinlock_irqsave)(&afe->port_list_lock); - list_add_tail(&port->node, &afe->port_list); + scoped_guard(spinlock_irqsave, &afe->port_list_lock) + list_add_tail(&port->node, &afe->port_list); return port; From a49e098be20063c91b673a674b8f0f92135448da Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 9 Dec 2025 16:18:50 +0100 Subject: [PATCH 036/341] ASoC: codecs: wm0010: Replace cpu_to_be64 + le64_to_cpu with swab64 Replace cpu_to_be64(le64_to_cpu()) with swab64() to simplify byte_swap_64(). No functional changes. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20251209151853.432518-1-thorsten.blum@linux.dev Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 7511c71695c6..6e097d8ed288 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -326,7 +326,7 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) int i; for (i = 0; i < len / 8; i++) - data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); + data_out[i] = swab64(data_in[i]); } static int wm0010_firmware_load(const char *name, struct snd_soc_component *component) From ae9ccaed3f6701ee0fe40ad919516e0aa0844f21 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Dec 2025 16:07:29 +0000 Subject: [PATCH 037/341] firmware: cs_dsp: Don't use __free() in cs_dsp_load() and cs_dsp_load_coeff() Replace the __free(kfree) in cs_dsp_load() and cs_dsp_load_coeff() with a kfree(buf) at the end of the function. The use of __free() can create new cleanup bugs that are difficult to spot because the defective code is idiomatically correct regular C. In these two functions the __free() was mixed with gotos, and also used the suspect declaration __free(kfree) = NULL;. The __free() did not do anything to simplify the code. There aren't any early returns after the pointer is set, and the __free() can be replaced by a kfree() at the end of the function. Signed-off-by: Richard Fitzgerald Fixes: 900baa6e7bb0 ("firmware: cs_dsp: Remove redundant download buffer allocator") Link: https://patch.msgid.link/20251201160729.231867-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 8d2e7267d973..d35d0f5ccaf7 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1451,7 +1451,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, const struct wmfw_region *region; const struct cs_dsp_region *mem; const char *region_name; - u8 *buf __free(kfree) = NULL; + u8 *buf = NULL; size_t buf_len = 0; size_t region_len; unsigned int reg; @@ -1606,6 +1606,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, ret = 0; out_fw: + kfree(buf); + if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); @@ -2137,7 +2139,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware struct cs_dsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg, version; - u8 *buf __free(kfree) = NULL; + u8 *buf = NULL; size_t buf_len = 0; size_t region_len; @@ -2316,6 +2318,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware ret = 0; out_fw: + kfree(buf); + if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); From 81acbdc51bbbec822a1525481f2f70677c47aee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 12 Dec 2025 08:35:53 +0100 Subject: [PATCH 038/341] ASoC: sdw-mockup: Drop dummy remove function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A remove callback is optional and having no such function has the same semantic as one returning zero (and other return values are effectively ignored). This allows to remove the remove function without replacement. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/20251212073555.1065284-2-u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/codecs/sdw-mockup.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index 574c08b14f0c..b7e6546f1b5a 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -237,11 +237,6 @@ static int sdw_mockup_sdw_probe(struct sdw_slave *slave, return ret; } -static int sdw_mockup_sdw_remove(struct sdw_slave *slave) -{ - return 0; -} - /* * Intel reserved parts ID with the following mapping expected: * 0xAAAA: generic full-duplex codec @@ -264,7 +259,6 @@ static struct sdw_driver sdw_mockup_sdw_driver = { .name = "sdw-mockup", }, .probe = sdw_mockup_sdw_probe, - .remove = sdw_mockup_sdw_remove, .ops = &sdw_mockup_slave_ops, .id_table = sdw_mockup_id, }; From b8e54b447cdec234f0e0d80487af9540063d17dd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:39 +0200 Subject: [PATCH 039/341] ASoC: SOF: ipc4-loader: Remove redundant rpm resume_and_get from load_library The initial library loading is happening during topology loading, which is already protected with pm_runtime_resume_and_get() via pcm.c The redundant rpm code can be dropped from sof_ipc4_load_library() Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-loader.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index b0d293f62d1c..07a78cb3c25c 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -175,7 +175,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id, struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_fw_library *fw_lib; ssize_t payload_offset; - int ret, i, err; + int ret, i; if (!ipc4_data->load_library) { dev_err(sdev->dev, "Library loading is not supported on this platform\n"); @@ -223,24 +223,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id, for (i = 0; i < fw_lib->num_modules; i++) fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT); - /* - * Make sure that the DSP is booted and stays up while attempting the - * loading the library for the first time - */ - ret = pm_runtime_resume_and_get(sdev->dev); - if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n", - __func__, ret); - goto release; - } - ret = ipc4_data->load_library(sdev, fw_lib, false); - - err = pm_runtime_put_autosuspend(sdev->dev); - if (err < 0) - dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n", - __func__, err); - if (ret) goto release; From 2c77ff200f59103ecdee60c00818a43cee38a6b8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:40 +0200 Subject: [PATCH 040/341] ASoC: SOF: control: skip rpm calls in ext_volatile_get if not implemented Test earlier for the existence of ext_volatile_get callback and if it is missing, skip the rpm calls to avoid needles DSP power on. No change in functionality, we just skip the DSP power on in the unlikely case when the ext_volatile _get is not supported and yet the topology adds such control. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index a3fd1d523c09..9582ab5f1113 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -187,14 +187,18 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); int ret, err; + /* ignore the ext_volatile_get call if the callbacks are not provided */ + if (!tplg_ops || !tplg_ops->control || + !tplg_ops->control->bytes_ext_volatile_get) + return 0; + ret = pm_runtime_resume_and_get(scomp->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret); return ret; } - if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get) - ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); + ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) From c3e1549811747e4b4ff7e4bba691980d9dab2d9e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:41 +0200 Subject: [PATCH 041/341] ASoC: SOF: Add support for on-demand DSP boot On system suspend / resume we always power up the DSP and boot the firmware, which is not strictly needed as right after the firmware booted up we power the DSP down again on suspend and we also power it down after resume after some inactivity. Out of caution, add a new platform descriptor flag to enable on-demand DSP boot since this might not work without changes to platform code on certain platforms. With the on-demand dsp boot enabled we will not boot the DSP and firmware up on system or rpm resume, just enable audio subsystem since audio IPs, like HDA and SoundWire might be needed (codecs suspend/resume operation). Only boot up the DSP during the first hw_params() call when the DSP is really going to be needed. In this way we can handle the audio related use cases: normal audio use (rpm suspend/resume) system suspend/resume without active audio system suspend/resume with active audio system suspend/resume without active audio, and audio start before the rpm suspend timeout Add module option to force the on-demand DSP boot to allow it to be disabled or enabled without kernel change for testing. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 3 + sound/soc/sof/compress.c | 8 ++ sound/soc/sof/control.c | 7 +- sound/soc/sof/core.c | 1 + sound/soc/sof/debug.c | 7 +- sound/soc/sof/ipc3-dtrace.c | 7 +- sound/soc/sof/ipc4.c | 13 +++ sound/soc/sof/pcm.c | 10 +++ sound/soc/sof/pm.c | 175 ++++++++++++++++++++++-------------- sound/soc/sof/sof-priv.h | 3 + 10 files changed, 162 insertions(+), 72 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index eddea82c7b5a..38d6c8cb5e83 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -159,6 +159,9 @@ struct sof_dev_desc { /* The platform supports DSPless mode */ bool dspless_mode_supported; + /* On demand DSP booting is possible on the platform */ + bool on_demand_dsp_boot; + /* defaults paths for firmware, library and topology files */ const char *default_fw_path[SOF_IPC_TYPE_COUNT]; const char *default_lib_path[SOF_IPC_TYPE_COUNT]; diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 90b932ae3bab..86d563c864e5 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -195,6 +195,14 @@ static int sof_compr_set_params(struct snd_soc_component *component, if (sizeof(*pcm) + ext_data_size > sdev->ipc->max_payload_size) return -EINVAL; + /* + * Make sure that the DSP is booted up, which might not be the + * case if the on-demand DSP boot is used + */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (ret) + return ret; + pcm = kzalloc(sizeof(*pcm) + ext_data_size, GFP_KERNEL); if (!pcm) return -ENOMEM; diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 9582ab5f1113..74d997a4f620 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -198,7 +198,12 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ return ret; } - ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); + /* Make sure the DSP/firmware is booted up */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (!ret) + ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, + binary_data, + size); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index b11f408f1366..2d394389c945 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -680,6 +680,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_event_handler_mutex); + mutex_init(&sdev->dsp_fw_boot_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index b24943a65c89..6b9e1f1ee657 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -216,7 +216,12 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s goto error; } - ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); + /* Make sure the DSP/firmware is booted up */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (!ret) + ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, + SOF_IPC_MSG_MAX_SIZE); + pm_runtime_put_autosuspend(sdev->dev); if (ret < 0 || reply->rhdr.error < 0) { ret = min(ret, reply->rhdr.error); diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 6ec391fd39a9..50700f5cb0ef 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -171,7 +171,12 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, dev_err(sdev->dev, "enabling device failed: %d\n", ret); goto error; } - ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size); + + /* Make sure the DSP/firmware is booted up */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (!ret) + ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size); + pm_runtime_put_autosuspend(sdev->dev); error: diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index a4a090e6724a..1df97129cee6 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -892,6 +892,19 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state) struct sof_ipc4_msg msg; u32 data = state; + /* + * The mic privacy change notification's role is to notify the running + * firmware that there is a change in mic privacy state from whatever + * the state was before - since the firmware booted up or since the + * previous change during runtime. + * + * If the firmware has not been booted up, there is no need to send + * change notification (the firmware is not booted up). + * The firmware checks the current state during its boot. + */ + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) + return; + msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index cee04574264e..31879a11c33e 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -122,6 +122,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: hw_params\n"); + if (!sdev->dspless_mode_selected) { + /* + * Make sure that the DSP is booted up, which might not be the + * case if the on-demand DSP boot is used + */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (ret) + return ret; + } + /* * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8e3bcf602beb..dd7cd87f1fa5 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -8,10 +8,15 @@ // Author: Liam Girdwood // +#include #include "ops.h" #include "sof-priv.h" #include "sof-audio.h" +static int override_on_demand_boot = -1; +module_param_named(on_demand_boot, override_on_demand_boot, int, 0444); +MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled"); + /* * Helper function to determine the target DSP state during * system suspend. This function only cares about the device @@ -70,12 +75,96 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) } #endif +int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev) +{ + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + int ret; + + guard(mutex)(&sdev->dsp_fw_boot_mutex); + + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { + /* Firmware already booted, just return */ + return 0; + } + + dev_dbg(sdev->dev, "Booting DSP firmware\n"); + + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to load DSP firmware: %d\n", + __func__, ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); + return ret; + } + + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); + + /* + * Boot the firmware. The FW boot status will be modified + * in snd_sof_run_firmware() depending on the outcome. + */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to boot DSP firmware: %d\n", + __func__, ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); + return ret; + } + + /* resume DMA trace */ + ret = sof_fw_trace_resume(sdev); + if (ret < 0) { + /* non fatal */ + dev_warn(sdev->dev, "%s: failed to resume trace: %d\n", + __func__, ret); + } + + /* restore pipelines */ + if (tplg_ops && tplg_ops->set_up_all_pipelines) { + ret = tplg_ops->set_up_all_pipelines(sdev, false); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n", + __func__, ret); + goto setup_fail; + } + } + + /* Notify clients not managed by pm framework about core resume */ + sof_resume_clients(sdev); + + /* notify DSP of system resume */ + if (pm_ops && pm_ops->ctx_restore) { + ret = pm_ops->ctx_restore(sdev); + if (ret < 0) + dev_err(sdev->dev, "%s: ctx_restore IPC failed: %d\n", + __func__, ret); + } + +setup_fail: +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) + if (ret < 0) { + /* + * Debugfs cannot be read in runtime suspend, so cache + * the contents upon failure. This allows to capture + * possible DSP coredump information. + */ + sof_cache_debugfs(sdev); + } +#endif + + return ret; +} +EXPORT_SYMBOL(snd_sof_boot_dsp_firmware); + static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); u32 old_state = sdev->dsp_power_state.state; + bool on_demand_boot; int ret; /* do nothing if dsp resume callbacks are not set */ @@ -123,74 +212,18 @@ static int sof_resume(struct device *dev, bool runtime_resume) return 0; } - sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); + if (override_on_demand_boot > -1) + on_demand_boot = override_on_demand_boot ? true : false; + else + on_demand_boot = sdev->pdata->desc->on_demand_dsp_boot; - /* load the firmware */ - ret = snd_sof_load_firmware(sdev); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to load DSP firmware after resume %d\n", - ret); - sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); - return ret; + if (on_demand_boot) { + /* Only change the fw_state to PREPARE but skip booting */ + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); + return 0; } - sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); - - /* - * Boot the firmware. The FW boot status will be modified - * in snd_sof_run_firmware() depending on the outcome. - */ - ret = snd_sof_run_firmware(sdev); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to boot DSP firmware after resume %d\n", - ret); - sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); - return ret; - } - - /* resume DMA trace */ - ret = sof_fw_trace_resume(sdev); - if (ret < 0) { - /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to init trace after resume %d\n", - ret); - } - - /* restore pipelines */ - if (tplg_ops && tplg_ops->set_up_all_pipelines) { - ret = tplg_ops->set_up_all_pipelines(sdev, false); - if (ret < 0) { - dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); - goto setup_fail; - } - } - - /* Notify clients not managed by pm framework about core resume */ - sof_resume_clients(sdev); - - /* notify DSP of system resume */ - if (pm_ops && pm_ops->ctx_restore) { - ret = pm_ops->ctx_restore(sdev); - if (ret < 0) - dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret); - } - -setup_fail: -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) - if (ret < 0) { - /* - * Debugfs cannot be read in runtime suspend, so cache - * the contents upon failure. This allows to capture - * possible DSP coredump information. - */ - sof_cache_debugfs(sdev); - } -#endif - - return ret; + return snd_sof_boot_dsp_firmware(sdev); } static int sof_suspend(struct device *dev, bool runtime_suspend) @@ -297,8 +330,12 @@ int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) { const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - /* Notify DSP of upcoming power down */ - if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save) + /* + * Notify DSP of upcoming power down only if the firmware has been + * booted up + */ + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE && sof_ops(sdev)->remove && + pm_ops && pm_ops->ctx_save) return pm_ops->ctx_save(sdev); return 0; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0f624d8cde20..693d063830fa 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -580,6 +580,8 @@ struct snd_sof_dev { wait_queue_head_t boot_wait; enum sof_fw_state fw_state; bool first_boot; + /* mutex to protect DSP firmware boot (except initial, probe time boot */ + struct mutex dsp_fw_boot_mutex; /* work queue in case the probe is implemented in two steps */ struct work_struct probe_work; @@ -703,6 +705,7 @@ int snd_sof_suspend(struct device *dev); int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev); int snd_sof_prepare(struct device *dev); void snd_sof_complete(struct device *dev); +int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); From 2cf7a9ced3c1a999f7de8711cb2a212557fbd800 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:42 +0200 Subject: [PATCH 042/341] ASoC: SOF: sof-client: Add support for on-demand DSP boot With the introduction of on-demand DSP boot the rpm status not necessary tells that the DSP firmware is booted up. Introduce the sof_client_boot_dsp() which can be used to make sure that the DSP is booted and it can handle IPCs. Update the client drivers to use the new function where it is expected that the DSP is booted up. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-client-ipc-flood-test.c | 7 ++--- .../soc/sof/sof-client-ipc-kernel-injector.c | 4 ++- sound/soc/sof/sof-client-ipc-msg-injector.c | 14 ++++++---- sound/soc/sof/sof-client-probes.c | 26 ++++++++++++++----- sound/soc/sof/sof-client.c | 6 +++++ sound/soc/sof/sof-client.h | 3 +++ 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c index 373f3a125372..7b72d1c9c739 100644 --- a/sound/soc/sof/sof-client-ipc-flood-test.c +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -219,9 +219,10 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf goto out; } - /* flood test */ - ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, - ipc_duration_ms, ipc_count); + ret = sof_client_boot_dsp(cdev); + if (!ret) + ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, + ipc_duration_ms, ipc_count); err = pm_runtime_put_autosuspend(dev); if (err < 0) diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c index 249bd2d6c8d2..d5984990098a 100644 --- a/sound/soc/sof/sof-client-ipc-kernel-injector.c +++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c @@ -63,7 +63,9 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u return ret; } - sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); + ret = sof_client_boot_dsp(cdev); + if (!ret) + sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); ret = pm_runtime_put_autosuspend(dev); if (ret < 0) diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c index 9c8a0fbfb8df..c28f106de6ba 100644 --- a/sound/soc/sof/sof-client-ipc-msg-injector.c +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -131,11 +131,15 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev) return ret; } - /* send the message */ - ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer, - priv->max_msg_size); - if (ret) - dev_err(dev, "IPC message send failed: %d\n", ret); + ret = sof_client_boot_dsp(cdev); + if (!ret) { + /* send the message */ + ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, + priv->rx_buffer, + priv->max_msg_size); + if (ret) + dev_err(dev, "IPC message send failed: %d\n", ret); + } err = pm_runtime_put_autosuspend(dev); if (err < 0) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index f753e0faff99..124f55508159 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -123,6 +123,10 @@ static int sof_probes_compr_set_params(struct snd_compr_stream *cstream, if (ret) return ret; + ret = sof_client_boot_dsp(cdev); + if (ret) + return ret; + ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); if (ret < 0) { dev_err(dai->dev, "Failed to init probe: %d\n", ret); @@ -224,6 +228,10 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, goto exit; } + ret = sof_client_boot_dsp(cdev); + if (ret) + goto pm_error; + ret = ipc->points_info(cdev, &desc, &num_desc, type); if (ret < 0) goto pm_error; @@ -312,9 +320,12 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from, goto exit; } - ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc)); - if (!ret) - ret = count; + ret = sof_client_boot_dsp(cdev); + if (!ret) { + ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc)); + if (!ret) + ret = count; + } err = pm_runtime_put_autosuspend(dev); if (err < 0) @@ -367,9 +378,12 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, goto exit; } - ret = ipc->points_remove(cdev, &array[1], array[0]); - if (!ret) - ret = count; + ret = sof_client_boot_dsp(cdev); + if (!ret) { + ret = ipc->points_remove(cdev, &array[1], array[0]); + if (!ret) + ret = count; + } err = pm_runtime_put_autosuspend(dev); if (err < 0) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 2dbfc7699c73..b0802484a2d3 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -486,6 +486,12 @@ enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT"); +int sof_client_boot_dsp(struct sof_client_dev *cdev) +{ + return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); +} +EXPORT_SYMBOL_NS_GPL(sof_client_boot_dsp, "SND_SOC_SOF_CLIENT"); + /* module refcount management of SOF core */ int sof_client_core_module_get(struct sof_client_dev *cdev) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 1a9015e38474..3b02506c03f1 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -50,6 +50,9 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev); enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev); +/* DSP/firmware boot request */ +int sof_client_boot_dsp(struct sof_client_dev *cdev); + /* module refcount management of SOF core */ int sof_client_core_module_get(struct sof_client_dev *cdev); void sof_client_core_module_put(struct sof_client_dev *cdev); From d4e34f4ef88fc48a09b654bbe1b23c6788e7844a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:43 +0200 Subject: [PATCH 043/341] ASoC: SOF: Intel: hda-sdw-bpt: Add support for on-demand DSP boot If on-demand DSP boot is used we need to make sure that the DSP is booted up - which might not be the case - since we need ChainDMA in normal, non DSPless mode for the BRA to work. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-sdw-bpt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index e45dd051ab8c..ae2f8d55dbd0 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -98,6 +98,17 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream ** struct hdac_ext_stream *bpt_stream; unsigned int format = HDA_CL_STREAM_FORMAT; + if (!sdev->dspless_mode_selected) { + int ret; + + /* + * Make sure that the DSP is booted up, which might not be the + * case if the on-demand DSP boot is used + */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (ret) + return ret; + } /* * the baseline format needs to be adjusted to * bandwidth requirements From 6780fb864d5b80f20694c9141036717b23190150 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:44 +0200 Subject: [PATCH 044/341] ASoC: SOF: Intel: pci-lnl: Set on_demand_dsp_boot for LNL LNL can be used with on-demand DSP booting, set the flag to enable it. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-lnl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index ae379c23f008..acb4429df9ec 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -40,6 +40,7 @@ static const struct sof_dev_desc lnl_desc = { .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), .ipc_default = SOF_IPC_TYPE_4, .dspless_mode_supported = true, + .on_demand_dsp_boot = true, .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl", }, From aabcb01353013d38533ba4346b6ca84bff1b96f0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:45 +0200 Subject: [PATCH 045/341] ASoC: SOF: Intel: pci-ptl: Set on_demand_dsp_boot for PTL and WCL PTL and WCL can be used with on-demand DSP booting, set the flag to enable it. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20251215132946.2155-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-ptl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c index 68f6a9841633..9cb785ef763f 100644 --- a/sound/soc/sof/intel/pci-ptl.c +++ b/sound/soc/sof/intel/pci-ptl.c @@ -38,6 +38,7 @@ static const struct sof_dev_desc ptl_desc = { .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), .ipc_default = SOF_IPC_TYPE_4, .dspless_mode_supported = true, + .on_demand_dsp_boot = true, .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/ptl", }, @@ -67,6 +68,7 @@ static const struct sof_dev_desc wcl_desc = { .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), .ipc_default = SOF_IPC_TYPE_4, .dspless_mode_supported = true, + .on_demand_dsp_boot = true, .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/wcl", }, From f25c7d709b93602ee9a08eba522808a18e1f5d56 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:29:46 +0200 Subject: [PATCH 046/341] ASoC: SOF: Intel: pci-nvl: Set on_demand_dsp_boot for NVL-S NVL-S can be used with on-demand DSP booting, set the flag to enable it. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20251215132946.2155-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-nvl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c index c499c14b93d5..f75aa996a5bd 100644 --- a/sound/soc/sof/intel/pci-nvl.c +++ b/sound/soc/sof/intel/pci-nvl.c @@ -38,6 +38,7 @@ static const struct sof_dev_desc nvl_s_desc = { .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), .ipc_default = SOF_IPC_TYPE_4, .dspless_mode_supported = true, + .on_demand_dsp_boot = true, .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl-s", }, From a1bcb66209a745c9ca18deae9f1c207b009dee1c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Dec 2025 19:16:20 +0100 Subject: [PATCH 047/341] ASoC: Fix acronym for Intel Gemini Lake While the used GML is consistent with the pattern for other Intel * Lake SoCs, the de facto use is GLK. Update the acronym and users accordingly. Note, a handful of the drivers for Gemini Lake in the Linux kernel use GLK already (LPC, MEI, pin control, SDHCI, ...) and even some in ASoC. The only ones in this patch used the inconsistent one. Acked-by: Bjorn Helgaas # pci_ids.h Signed-off-by: Andy Shevchenko Reviewed-by: Peter Ujfalusi Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20251212181742.3944789-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- include/linux/pci_ids.h | 3 ++- sound/hda/controllers/intel.c | 2 +- sound/hda/core/intel-dsp-config.c | 4 ++-- sound/soc/intel/avs/board_selection.c | 2 +- sound/soc/intel/avs/core.c | 2 +- sound/soc/sof/intel/pci-apl.c | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a9a089566b7c..84b830036fb4 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2950,7 +2950,8 @@ #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3 -#define PCI_DEVICE_ID_INTEL_HDA_GML 0x3198 +/* In a few of the Intel documents the GML acronym is used for Gemini Lake */ +#define PCI_DEVICE_ID_INTEL_HDA_GLK 0x3198 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 1e8e3d61291a..bb9a64d41580 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2555,7 +2555,7 @@ static const struct pci_device_id azx_ids[] = { /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, + { PCI_DEVICE_DATA(INTEL, HDA_GLK, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Haswell */ { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c index 0c25e87408de..ddb8db3e8e39 100644 --- a/sound/hda/core/intel-dsp-config.c +++ b/sound/hda/core/intel-dsp-config.c @@ -154,7 +154,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) { .flags = FLAG_SOF, - .device = PCI_DEVICE_ID_INTEL_HDA_GML, + .device = PCI_DEVICE_ID_INTEL_HDA_GLK, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -167,7 +167,7 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = PCI_DEVICE_ID_INTEL_HDA_GML, + .device = PCI_DEVICE_ID_INTEL_HDA_GLK, .codec_hid = &essx_83x6, }, #endif diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 52e6266a7cb8..8a46285181fa 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -367,7 +367,7 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines), AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines), AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines), - AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines), + AVS_MACH_ENTRY(HDA_GLK, avs_gml_i2s_machines), AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines), AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines), AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines), diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 6e0e65584c7f..1a53856c2ffb 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -897,7 +897,7 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) }, - { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_GLK, &apl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) }, diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 0bf7ee753bc3..3241403efa60 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -86,7 +86,7 @@ static const struct sof_dev_desc glk_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_APL, &bxt_desc) }, - { PCI_DEVICE_DATA(INTEL, HDA_GML, &glk_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_GLK, &glk_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); From 524ee559948d8d079b13466e70fa741f909699c0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 15 Dec 2025 15:08:19 +0200 Subject: [PATCH 048/341] ASoC: SOF: Intel: hda: Only check SSP MCLK mask in case of IPC3 IPC4 is using the NHLT blob itself and sends the SSP blob from it directly to the firmware, there is no need for the MCLK quirk based on the SSP blob since the SSP blob is in use. At the same time reword the error, info and debug messages for clarity. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Reviewed-by: Balamurugan C Link: https://patch.msgid.link/20251215130819.31218-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c1518dbee1b7..72c98154c027 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1611,7 +1611,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) mach->mach_params.i2s_link_mask) { const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); int ssp_num; - int mclk_mask; if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) @@ -1636,19 +1635,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) sof_pdata->tplg_filename = tplg_filename; - mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num); + if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) { + int mclk_mask = check_nhlt_ssp_mclk_mask(sdev, + ssp_num); - if (mclk_mask < 0) { - dev_err(sdev->dev, "Invalid MCLK configuration\n"); - return NULL; - } + if (mclk_mask < 0) { + dev_err(sdev->dev, + "Invalid MCLK configuration for SSP%d\n", + ssp_num); + return NULL; + } - dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask); - - if (mclk_mask) { - dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask); - sdev->mclk_id_override = true; - sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; + if (mclk_mask) { + sdev->mclk_id_override = true; + sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; + dev_info(sdev->dev, + "SSP%d to use MCLK id %d (mask: %#x)\n", + ssp_num, sdev->mclk_id_quirk, mclk_mask); + } else { + dev_dbg(sdev->dev, + "MCLK mask is empty for SSP%d in NHLT\n", + ssp_num); + } } } From 14324b8f0760ca6f56202bb4ad356ec459ce165b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:23 +0100 Subject: [PATCH 049/341] ALSA: compress_offload: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment. Fixes: 9b02221422a5 ("ALSA: compress_offload: Use automatic cleanup of kfree()") Fixes: 04177158cf98 ("ALSA: compress_offload: introduce accel operation mode") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-2-tiwai@suse.de --- sound/core/compress_offload.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index da514fef45bc..ed2eeb914c6d 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -514,12 +514,12 @@ static int snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) { int retval; - struct snd_compr_codec_caps *caps __free(kfree) = NULL; if (!stream->ops->get_codec_caps) return -ENXIO; - caps = kzalloc(sizeof(*caps), GFP_KERNEL); + struct snd_compr_codec_caps *caps __free(kfree) = + kzalloc(sizeof(*caps), GFP_KERNEL); if (!caps) return -ENOMEM; @@ -647,7 +647,6 @@ snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_param static int snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_params *params __free(kfree) = NULL; int retval; if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) { @@ -655,7 +654,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) * we should allow parameter change only when stream has been * opened not in other cases */ - params = memdup_user((void __user *)arg, sizeof(*params)); + struct snd_compr_params *params __free(kfree) = + memdup_user((void __user *)arg, sizeof(*params)); + if (IS_ERR(params)) return PTR_ERR(params); @@ -687,13 +688,13 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) static int snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_codec *params __free(kfree) = NULL; int retval; if (!stream->ops->get_params) return -EBADFD; - params = kzalloc(sizeof(*params), GFP_KERNEL); + struct snd_codec *params __free(kfree) = + kzalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; retval = stream->ops->get_params(stream, params); @@ -1104,12 +1105,13 @@ cleanup: static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_task *task __free(kfree) = NULL; int retval; if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) return -EPERM; - task = memdup_user((void __user *)arg, sizeof(*task)); + + struct snd_compr_task *task __free(kfree) = + memdup_user((void __user *)arg, sizeof(*task)); if (IS_ERR(task)) return PTR_ERR(task); retval = snd_compr_task_new(stream, task); @@ -1165,12 +1167,13 @@ static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_comp static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_task *task __free(kfree) = NULL; int retval; if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) return -EPERM; - task = memdup_user((void __user *)arg, sizeof(*task)); + + struct snd_compr_task *task __free(kfree) = + memdup_user((void __user *)arg, sizeof(*task)); if (IS_ERR(task)) return PTR_ERR(task); retval = snd_compr_task_start(stream, task); @@ -1256,12 +1259,13 @@ static int snd_compr_task_status(struct snd_compr_stream *stream, static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_task_status *status __free(kfree) = NULL; int retval; if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) return -EPERM; - status = memdup_user((void __user *)arg, sizeof(*status)); + + struct snd_compr_task_status *status __free(kfree) = + memdup_user((void __user *)arg, sizeof(*status)); if (IS_ERR(status)) return PTR_ERR(status); retval = snd_compr_task_status(stream, status); From 7b4721ca3159bce6338dbdf9188b785083571ed4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:24 +0100 Subject: [PATCH 050/341] ALSA: control: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: 7dba48a474e6 ("ALSA: control_led: Use guard() for locking") Fixes: 1052d9882269 ("ALSA: control: Use automatic cleanup of kfree()") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-3-tiwai@suse.de --- sound/core/control.c | 12 ++++++------ sound/core/control_compat.c | 21 +++++++++++---------- sound/core/control_led.c | 12 ++++++------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 9c3fd5113a61..486d1bc4dac2 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -867,9 +867,9 @@ EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, unsigned int cmd, void __user *arg) { - struct snd_ctl_card_info *info __free(kfree) = NULL; + struct snd_ctl_card_info *info __free(kfree) = + kzalloc(sizeof(*info), GFP_KERNEL); - info = kzalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; scoped_guard(rwsem_read, &snd_ioctl_rwsem) { @@ -1244,10 +1244,10 @@ static int snd_ctl_elem_read(struct snd_card *card, static int snd_ctl_elem_read_user(struct snd_card *card, struct snd_ctl_elem_value __user *_control) { - struct snd_ctl_elem_value *control __free(kfree) = NULL; int result; + struct snd_ctl_elem_value *control __free(kfree) = + memdup_user(_control, sizeof(*control)); - control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) return PTR_ERR(control); @@ -1320,11 +1320,11 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control) { - struct snd_ctl_elem_value *control __free(kfree) = NULL; struct snd_card *card; int result; + struct snd_ctl_elem_value *control __free(kfree) = + memdup_user(_control, sizeof(*control)); - control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) return PTR_ERR(control); diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 6459809ed364..b8988a4bcd9b 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -80,10 +80,10 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, struct snd_ctl_elem_info32 __user *data32) { struct snd_card *card = ctl->card; - struct snd_ctl_elem_info *data __free(kfree) = NULL; int err; + struct snd_ctl_elem_info *data __free(kfree) = + kzalloc(sizeof(*data), GFP_KERNEL); - data = kzalloc(sizeof(*data), GFP_KERNEL); if (! data) return -ENOMEM; @@ -169,14 +169,15 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, int *countp) { struct snd_kcontrol *kctl; - struct snd_ctl_elem_info *info __free(kfree) = NULL; int err; guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (!kctl) return -ENOENT; - info = kzalloc(sizeof(*info), GFP_KERNEL); + + struct snd_ctl_elem_info *info __free(kfree) = + kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) return -ENOMEM; info->id = *id; @@ -280,10 +281,10 @@ static int copy_ctl_value_to_user(void __user *userdata, static int __ctl_elem_read_user(struct snd_card *card, void __user *userdata, void __user *valuep) { - struct snd_ctl_elem_value *data __free(kfree) = NULL; int err, type, count; + struct snd_ctl_elem_value *data __free(kfree) = + kzalloc(sizeof(*data), GFP_KERNEL); - data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -314,11 +315,11 @@ static int ctl_elem_read_user(struct snd_card *card, static int __ctl_elem_write_user(struct snd_ctl_file *file, void __user *userdata, void __user *valuep) { - struct snd_ctl_elem_value *data __free(kfree) = NULL; struct snd_card *card = file->card; int err, type, count; + struct snd_ctl_elem_value *data __free(kfree) = + kzalloc(sizeof(*data), GFP_KERNEL); - data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -378,9 +379,9 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, struct snd_ctl_elem_info32 __user *data32, int replace) { - struct snd_ctl_elem_info *data __free(kfree) = NULL; + struct snd_ctl_elem_info *data __free(kfree) = + kzalloc(sizeof(*data), GFP_KERNEL); - data = kzalloc(sizeof(*data), GFP_KERNEL); if (! data) return -ENOMEM; diff --git a/sound/core/control_led.c b/sound/core/control_led.c index e33dfcf863cf..c7641d5084e7 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -245,12 +245,12 @@ DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, unsigned int group, bool set) { - struct snd_card *card __free(snd_card_unref) = NULL; struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsigned int ioff, access, new_access; + struct snd_card *card __free(snd_card_unref) = + snd_card_ref(card_number); - card = snd_card_ref(card_number); if (!card) return -ENXIO; guard(rwsem_write)(&card->controls_rwsem); @@ -302,13 +302,13 @@ static void snd_ctl_led_clean(struct snd_card *card) static int snd_ctl_led_reset(int card_number, unsigned int group) { - struct snd_card *card __free(snd_card_unref) = NULL; struct snd_ctl_led_ctl *lctl, *_lctl; struct snd_ctl_led *led; struct snd_kcontrol_volatile *vd; bool change = false; + struct snd_card *card __free(snd_card_unref) = + snd_card_ref(card_number); - card = snd_card_ref(card_number); if (!card) return -ENXIO; @@ -598,11 +598,11 @@ static ssize_t list_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev); - struct snd_card *card __free(snd_card_unref) = NULL; struct snd_ctl_led_ctl *lctl; size_t l = 0; + struct snd_card *card __free(snd_card_unref) = + snd_card_ref(led_card->number); - card = snd_card_ref(led_card->number); if (!card) return -ENXIO; guard(rwsem_read)(&card->controls_rwsem); From f3d233daf011abbad2f6ebd0e545b42d2f378a4f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:25 +0100 Subject: [PATCH 051/341] ALSA: pcm: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: ae9213984864 ("ALSA: pcm: Use automatic cleanup of kfree()") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-4-tiwai@suse.de --- sound/core/pcm.c | 4 ++-- sound/core/pcm_compat.c | 9 ++++---- sound/core/pcm_native.c | 50 +++++++++++++++++++++-------------------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 283aac441fa0..0b512085eb63 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -328,13 +328,13 @@ static const char *snd_pcm_oss_format_name(int format) static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, struct snd_info_buffer *buffer) { - struct snd_pcm_info *info __free(kfree) = NULL; int err; if (! substream) return; - info = kmalloc(sizeof(*info), GFP_KERNEL); + struct snd_pcm_info *info __free(kfree) = + kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 54eb9bd8eb21..e86f68f1f23c 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -235,7 +235,6 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, int refine, struct snd_pcm_hw_params32 __user *data32) { - struct snd_pcm_hw_params *data __free(kfree) = NULL; struct snd_pcm_runtime *runtime; int err; @@ -243,7 +242,8 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, if (!runtime) return -ENOTTY; - data = kmalloc(sizeof(*data), GFP_KERNEL); + struct snd_pcm_hw_params *data __free(kfree) = + kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -332,7 +332,6 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, compat_caddr_t buf; compat_caddr_t __user *bufptr; u32 frames; - void __user **bufs __free(kfree) = NULL; int err, ch, i; if (! substream->runtime) @@ -349,7 +348,9 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, get_user(frames, &data32->frames)) return -EFAULT; bufptr = compat_ptr(buf); - bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL); + + void __user **bufs __free(kfree) = + kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL); if (bufs == NULL) return -ENOMEM; for (i = 0; i < ch; i++) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 68bee40c9ada..4352c0a40f8d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -242,10 +242,10 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) int snd_pcm_info_user(struct snd_pcm_substream *substream, struct snd_pcm_info __user * _info) { - struct snd_pcm_info *info __free(kfree) = NULL; int err; + struct snd_pcm_info *info __free(kfree) = + kmalloc(sizeof(*info), GFP_KERNEL); - info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; err = snd_pcm_info(substream, info); @@ -364,7 +364,6 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; unsigned int k; - unsigned int *rstamps __free(kfree) = NULL; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp; struct snd_pcm_hw_rule *r; @@ -380,7 +379,8 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, * Each member of 'rstamps' array represents the sequence number of * recent application of corresponding rule. */ - rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL); + unsigned int *rstamps __free(kfree) = + kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL); if (!rstamps) return -ENOMEM; @@ -583,10 +583,10 @@ EXPORT_SYMBOL(snd_pcm_hw_refine); static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { - struct snd_pcm_hw_params *params __free(kfree) = NULL; int err; + struct snd_pcm_hw_params *params __free(kfree) = + memdup_user(_params, sizeof(*params)); - params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) return PTR_ERR(params); @@ -884,10 +884,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { - struct snd_pcm_hw_params *params __free(kfree) = NULL; int err; + struct snd_pcm_hw_params *params __free(kfree) = + memdup_user(_params, sizeof(*params)); - params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) return PTR_ERR(params); @@ -2262,7 +2262,6 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; - struct snd_pcm_group *group __free(kfree) = NULL; struct snd_pcm_group *target_group; bool nonatomic = substream->pcm->nonatomic; CLASS(fd, f)(fd); @@ -2278,7 +2277,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) if (substream == substream1) return -EINVAL; - group = kzalloc(sizeof(*group), GFP_KERNEL); + struct snd_pcm_group *group __free(kfree) = + kzalloc(sizeof(*group), GFP_KERNEL); if (!group) return -ENOMEM; snd_pcm_group_init(group); @@ -3286,7 +3286,6 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, { struct snd_xfern xfern; struct snd_pcm_runtime *runtime = substream->runtime; - void *bufs __free(kfree) = NULL; snd_pcm_sframes_t result; if (runtime->state == SNDRV_PCM_STATE_OPEN) @@ -3298,7 +3297,8 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); + void *bufs __free(kfree) = + memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); if (IS_ERR(bufs)) return PTR_ERR(bufs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -3572,7 +3572,6 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) struct snd_pcm_runtime *runtime; snd_pcm_sframes_t result; unsigned long i; - void __user **bufs __free(kfree) = NULL; snd_pcm_uframes_t frames; const struct iovec *iov = iter_iov(to); @@ -3591,7 +3590,9 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) if (!frame_aligned(runtime, iov->iov_len)) return -EINVAL; frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL); + + void __user **bufs __free(kfree) = + kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL); if (bufs == NULL) return -ENOMEM; for (i = 0; i < to->nr_segs; ++i) { @@ -3611,7 +3612,6 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) struct snd_pcm_runtime *runtime; snd_pcm_sframes_t result; unsigned long i; - void __user **bufs __free(kfree) = NULL; snd_pcm_uframes_t frames; const struct iovec *iov = iter_iov(from); @@ -3629,7 +3629,9 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) !frame_aligned(runtime, iov->iov_len)) return -EINVAL; frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL); + + void __user **bufs __free(kfree) = + kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL); if (bufs == NULL) return -ENOMEM; for (i = 0; i < from->nr_segs; ++i) { @@ -4101,15 +4103,15 @@ static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *opara static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams) { - struct snd_pcm_hw_params *params __free(kfree) = NULL; - struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); + struct snd_pcm_hw_params *params __free(kfree) = + kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; - oparams = memdup_user(_oparams, sizeof(*oparams)); + struct snd_pcm_hw_params_old *oparams __free(kfree) = + memdup_user(_oparams, sizeof(*oparams)); if (IS_ERR(oparams)) return PTR_ERR(oparams); snd_pcm_hw_convert_from_old_params(params, oparams); @@ -4130,15 +4132,15 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams) { - struct snd_pcm_hw_params *params __free(kfree) = NULL; - struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); + struct snd_pcm_hw_params *params __free(kfree) = + kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; - oparams = memdup_user(_oparams, sizeof(*oparams)); + struct snd_pcm_hw_params_old *oparams __free(kfree) = + memdup_user(_oparams, sizeof(*oparams)); if (IS_ERR(oparams)) return PTR_ERR(oparams); From 55f98ece9939a0ad5f83c6124dd1f00d678f9f46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:26 +0100 Subject: [PATCH 052/341] ALSA: oss: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: a55bc334d3df ("ALSA: pcm_oss: ump: Use automatic cleanup of kfree()") Fixes: 6c40eec521af ("ALSA: mixer_oss: ump: Use automatic cleanup of kfree()") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-5-tiwai@suse.de --- sound/core/oss/mixer_oss.c | 64 ++++++++++++++++++++------------------ sound/core/oss/pcm_oss.c | 19 ++++++----- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index e839a4bb93f8..69422ab2d808 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -517,8 +517,6 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, unsigned int numid, int *left, int *right) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; @@ -528,8 +526,11 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return; if (kctl->info(kctl, uinfo)) @@ -550,8 +551,6 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, int *left, int *right, int route) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; @@ -561,8 +560,11 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return; if (kctl->info(kctl, uinfo)) @@ -609,8 +611,6 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, unsigned int numid, int left, int right) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; int res; @@ -621,8 +621,11 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return; if (kctl->info(kctl, uinfo)) @@ -646,8 +649,6 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, int left, int right, int route) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; int res; @@ -658,8 +659,11 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return; if (kctl->info(kctl, uinfo)) @@ -783,12 +787,12 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *pslot; struct slot *slot; - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err, idx; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return -ENOMEM; guard(rwsem_read)(&card->controls_rwsem); @@ -825,13 +829,13 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *pslot; struct slot *slot = NULL; - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err; unsigned int idx; - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return -ENOMEM; guard(rwsem_read)(&card->controls_rwsem); @@ -872,18 +876,18 @@ struct snd_mixer_oss_assign_table { static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item) { - struct snd_ctl_elem_info *info __free(kfree) = NULL; struct snd_kcontrol *kcontrol; struct snd_card *card = mixer->card; int err; + struct snd_ctl_elem_info *info __free(kfree) = + kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; scoped_guard(rwsem_read, &card->controls_rwsem) { kcontrol = snd_mixer_oss_test_id(mixer, name, index); if (kcontrol == NULL) return 0; - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; err = kcontrol->info(kcontrol, info); if (err < 0) return err; @@ -1006,9 +1010,9 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, if (!ptr->index) kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); if (kctl) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_info *uinfo __free(kfree) = + kzalloc(sizeof(*uinfo), GFP_KERNEL); - uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); if (!uinfo) return -ENOMEM; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index a82dd155e1d3..1dffa8fdf429 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -377,7 +377,6 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, snd_pcm_hw_param_t var, unsigned int best, int *dir) { - struct snd_pcm_hw_params *save __free(kfree) = NULL; int v; unsigned int saved_min; int last = 0; @@ -397,19 +396,22 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, maxdir = 1; max--; } - save = kmalloc(sizeof(*save), GFP_KERNEL); + + struct snd_pcm_hw_params *save __free(kfree) = + kmalloc(sizeof(*save), GFP_KERNEL); if (save == NULL) return -ENOMEM; *save = *params; saved_min = min; min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); if (min >= 0) { - struct snd_pcm_hw_params *params1 __free(kfree) = NULL; if (max < 0) goto _end; if ((unsigned int)min == saved_min && mindir == valdir) goto _end; - params1 = kmalloc(sizeof(*params1), GFP_KERNEL); + + struct snd_pcm_hw_params *params1 __free(kfree) = + kmalloc(sizeof(*params1), GFP_KERNEL); if (params1 == NULL) return -ENOMEM; *params1 = *save; @@ -781,10 +783,10 @@ static int choose_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, unsigned int best_rate) { const struct snd_interval *it; - struct snd_pcm_hw_params *save __free(kfree) = NULL; unsigned int rate, prev; - save = kmalloc(sizeof(*save), GFP_KERNEL); + struct snd_pcm_hw_params *save __free(kfree) = + kmalloc(sizeof(*save), GFP_KERNEL); if (save == NULL) return -ENOMEM; *save = *params; @@ -1834,7 +1836,6 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) struct snd_pcm_substream *substream; int err; int direct; - struct snd_pcm_hw_params *params __free(kfree) = NULL; unsigned int formats = 0; const struct snd_mask *format_mask; int fmt; @@ -1854,7 +1855,9 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | AFMT_S24_PACKED; - params = kmalloc(sizeof(*params), GFP_KERNEL); + + struct snd_pcm_hw_params *params __free(kfree) = + kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; _snd_pcm_hw_params_any(params); From df27c92753474cc8540e46a476119857ced7ae21 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:27 +0100 Subject: [PATCH 053/341] ALSA: seq: oss: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: 80ccbe91adab ("ALSA: seq: oss/synth: Clean up with guard and auto cleanup") Fixes: 895a46e034f9 ("ALSA: seq: oss/midi: Cleanup with guard and auto-cleanup") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-6-tiwai@suse.de --- sound/core/seq/oss/seq_oss_init.c | 4 +-- sound/core/seq/oss/seq_oss_midi.c | 45 +++++++++++++++--------------- sound/core/seq/oss/seq_oss_synth.c | 23 +++++++-------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 973f057eb731..e0c368bd09cb 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -63,10 +63,10 @@ int __init snd_seq_oss_create_client(void) { int rc; - struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback port_callback; + struct snd_seq_port_info *port __free(kfree) = + kzalloc(sizeof(*port), GFP_KERNEL); - port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 023e5d0a4351..2d48c25ff4df 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -65,11 +65,11 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int snd_seq_oss_midi_lookup_ports(int client) { - struct snd_seq_client_info *clinfo __free(kfree) = NULL; - struct snd_seq_port_info *pinfo __free(kfree) = NULL; + struct snd_seq_client_info *clinfo __free(kfree) = + kzalloc(sizeof(*clinfo), GFP_KERNEL); + struct snd_seq_port_info *pinfo __free(kfree) = + kzalloc(sizeof(*pinfo), GFP_KERNEL); - clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL); - pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); if (!clinfo || !pinfo) return -ENOMEM; clinfo->client = -1; @@ -305,10 +305,10 @@ int snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode) { int perm; - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; struct snd_seq_port_subscribe subs; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return -ENODEV; @@ -364,10 +364,10 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode) int snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; struct snd_seq_port_subscribe subs; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return -ENODEV; guard(mutex)(&mdev->open_mutex); @@ -399,10 +399,10 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) int snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; int mode; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return 0; @@ -422,9 +422,9 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev) void snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return; if (!mdev->opened) @@ -468,9 +468,9 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) void snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return; addr->client = mdev->client; @@ -485,11 +485,11 @@ int snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data) { struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data; - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; if (dp->readq == NULL) return 0; - mdev = find_slot(ev->source.client, ev->source.port); + struct seq_oss_midi *mdev __free(seq_oss_midi) = + find_slot(ev->source.client, ev->source.port); if (!mdev) return 0; if (!(mdev->opened & PERM_READ)) @@ -595,9 +595,9 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq int snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return -ENODEV; if (snd_midi_event_encode_byte(mdev->coder, c, ev)) { @@ -613,9 +613,9 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru int snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mididev(dp, dev); - mdev = get_mididev(dp, dev); if (!mdev) return -ENXIO; inf->device = dev; @@ -651,10 +651,9 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf) snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); for (i = 0; i < max_midi_devs; i++) { - struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; - snd_iprintf(buf, "\nmidi %d: ", i); - mdev = get_mdev(i); + struct seq_oss_midi *mdev __free(seq_oss_midi) = + get_mdev(i); if (mdev == NULL) { snd_iprintf(buf, "*empty*\n"); continue; diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 68bc6d4eed53..c7f81f2053a7 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -364,7 +364,6 @@ reset_channels(struct seq_oss_synthinfo *info) void snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) { - struct seq_oss_synth *rec __free(seq_oss_synth) = NULL; struct seq_oss_synthinfo *info; info = get_synthinfo_nospec(dp, dev); @@ -387,7 +386,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) return; } - rec = get_sdev(dev); + struct seq_oss_synth *rec __free(seq_oss_synth) = + get_sdev(dev); if (rec == NULL) return; if (rec->oper.reset) { @@ -411,7 +411,6 @@ int snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt, const char __user *buf, int p, int c) { - struct seq_oss_synth *rec __free(seq_oss_synth) = NULL; struct seq_oss_synthinfo *info; info = get_synthinfo_nospec(dp, dev); @@ -420,7 +419,9 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt, if (info->is_midi) return 0; - rec = get_synthdev(dp, dev); + + struct seq_oss_synth *rec __free(seq_oss_synth) = + get_synthdev(dp, dev); if (!rec) return -ENXIO; @@ -436,9 +437,9 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt, struct seq_oss_synthinfo * snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev) { - struct seq_oss_synth *rec __free(seq_oss_synth) = NULL; + struct seq_oss_synth *rec __free(seq_oss_synth) = + get_synthdev(dp, dev); - rec = get_synthdev(dp, dev); if (rec) return get_synthinfo_nospec(dp, dev); return NULL; @@ -491,13 +492,14 @@ snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event int snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr) { - struct seq_oss_synth *rec __free(seq_oss_synth) = NULL; struct seq_oss_synthinfo *info; info = get_synthinfo_nospec(dp, dev); if (!info || info->is_midi) return -ENXIO; - rec = get_synthdev(dp, dev); + + struct seq_oss_synth *rec __free(seq_oss_synth) = + get_synthdev(dp, dev); if (!rec) return -ENXIO; if (rec->oper.ioctl == NULL) @@ -571,10 +573,9 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs); for (i = 0; i < max_synth_devs; i++) { - struct seq_oss_synth *rec __free(seq_oss_synth) = NULL; - snd_iprintf(buf, "\nsynth %d: ", i); - rec = get_sdev(i); + struct seq_oss_synth *rec __free(seq_oss_synth) = + get_sdev(i); if (rec == NULL) { snd_iprintf(buf, "*empty*\n"); continue; From 13bc5c5394b22fd0a0585733bbbd9266159a840c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:28 +0100 Subject: [PATCH 054/341] ALSA: seq: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Note that there is a remaining __free() with NULL initialization; it's because of the non-trivial code conditionally assigning the data. Fixes: 04a86185b785 ("ALSA: seq: Clean up queue locking with auto cleanup") Fixes: 0869afc958a0 ("ALSA: seq: Clean up port locking with auto cleanup") Fixes: 99e16633958b ("ALSA: seq: Use auto-cleanup for client refcounting") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-7-tiwai@suse.de --- sound/core/seq/seq_clientmgr.c | 171 ++++++++++++++++---------------- sound/core/seq/seq_compat.c | 4 +- sound/core/seq/seq_midi.c | 10 +- sound/core/seq/seq_ports.c | 11 +- sound/core/seq/seq_queue.c | 32 +++--- sound/core/seq/seq_ump_client.c | 16 +-- sound/core/seq/seq_virmidi.c | 4 +- 7 files changed, 126 insertions(+), 122 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index f9a6e497f997..75a7a2af9d8c 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -494,9 +494,9 @@ static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags) */ static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event) { - struct snd_seq_client *dest __free(snd_seq_client) = NULL; + struct snd_seq_client *dest __free(snd_seq_client) = + snd_seq_client_use_ptr(event->dest.client); - dest = snd_seq_client_use_ptr(event->dest.client); if (dest == NULL) return NULL; if (! dest->accept_input) @@ -565,9 +565,9 @@ static int bounce_error_event(struct snd_seq_client *client, static int update_timestamp_of_queue(struct snd_seq_event *event, int queue, int real_time) { - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(queue); - q = queueptr(queue); if (! q) return 0; event->queue = queue; @@ -609,13 +609,13 @@ static int _snd_seq_deliver_single_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop) { - struct snd_seq_client *dest __free(snd_seq_client) = NULL; - struct snd_seq_client_port *dest_port __free(snd_seq_port) = NULL; - - dest = get_event_dest_client(event); + struct snd_seq_client *dest __free(snd_seq_client) = + get_event_dest_client(event); if (dest == NULL) return -ENOENT; - dest_port = snd_seq_port_use_ptr(dest, event->dest.port); + + struct snd_seq_client_port *dest_port __free(snd_seq_port) = + snd_seq_port_use_ptr(dest, event->dest.port); if (dest_port == NULL) return -ENOENT; @@ -672,7 +672,6 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, struct snd_seq_event *event, int port, int atomic, int hop) { - struct snd_seq_client_port *src_port __free(snd_seq_port) = NULL; struct snd_seq_subscribers *subs; int err, result = 0, num_ev = 0; union __snd_seq_event event_saved; @@ -681,7 +680,9 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, if (port < 0) return 0; - src_port = snd_seq_port_use_ptr(client, port); + + struct snd_seq_client_port *src_port __free(snd_seq_port) = + snd_seq_port_use_ptr(client, port); if (!src_port) return 0; @@ -801,13 +802,13 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e */ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop) { - struct snd_seq_client *client __free(snd_seq_client) = NULL; int result; if (snd_BUG_ON(!cell)) return -EINVAL; - client = snd_seq_client_use_ptr(cell->event.source.client); + struct snd_seq_client *client __free(snd_seq_client) = + snd_seq_client_use_ptr(cell->event.source.client); if (client == NULL) { snd_seq_cell_free(cell); /* release this cell */ return -EINVAL; @@ -1154,10 +1155,10 @@ static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg) static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg) { struct snd_seq_running_info *info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - /* requested client number */ - cptr = client_load_and_use_ptr(info->client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(info->client); + if (cptr == NULL) return -ENOENT; /* don't change !!! */ @@ -1207,10 +1208,10 @@ static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client, void *arg) { struct snd_seq_client_info *client_info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - /* requested client number */ - cptr = client_load_and_use_ptr(client_info->client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(client_info->client); + if (cptr == NULL) return -ENOENT; /* don't change !!! */ @@ -1344,14 +1345,14 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg) static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg) { struct snd_seq_port_info *info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - struct snd_seq_client_port *port __free(snd_seq_port) = NULL; - cptr = client_load_and_use_ptr(info->addr.client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; - port = snd_seq_port_use_ptr(cptr, info->addr.port); + struct snd_seq_client_port *port __free(snd_seq_port) = + snd_seq_port_use_ptr(cptr, info->addr.port); if (port == NULL) return -ENOENT; /* don't change */ @@ -1367,11 +1368,12 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg) static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg) { struct snd_seq_port_info *info = arg; - struct snd_seq_client_port *port __free(snd_seq_port) = NULL; if (info->addr.client != client->number) /* only set our own ports ! */ return -EPERM; - port = snd_seq_port_use_ptr(client, info->addr.port); + + struct snd_seq_client_port *port __free(snd_seq_port) = + snd_seq_port_use_ptr(client, info->addr.port); if (port) { snd_seq_set_port_info(port, info); /* notify the change */ @@ -1444,22 +1446,22 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client, void *arg) { struct snd_seq_port_subscribe *subs = arg; - struct snd_seq_client *receiver __free(snd_seq_client) = NULL; - struct snd_seq_client *sender __free(snd_seq_client) = NULL; - struct snd_seq_client_port *sport __free(snd_seq_port) = NULL; - struct snd_seq_client_port *dport __free(snd_seq_port) = NULL; int result; - receiver = client_load_and_use_ptr(subs->dest.client); + struct snd_seq_client *receiver __free(snd_seq_client) = + client_load_and_use_ptr(subs->dest.client); if (!receiver) return -EINVAL; - sender = client_load_and_use_ptr(subs->sender.client); + struct snd_seq_client *sender __free(snd_seq_client) = + client_load_and_use_ptr(subs->sender.client); if (!sender) return -EINVAL; - sport = snd_seq_port_use_ptr(sender, subs->sender.port); + struct snd_seq_client_port *sport __free(snd_seq_port) = + snd_seq_port_use_ptr(sender, subs->sender.port); if (!sport) return -EINVAL; - dport = snd_seq_port_use_ptr(receiver, subs->dest.port); + struct snd_seq_client_port *dport __free(snd_seq_port) = + snd_seq_port_use_ptr(receiver, subs->dest.port); if (!dport) return -EINVAL; @@ -1483,22 +1485,22 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client, void *arg) { struct snd_seq_port_subscribe *subs = arg; - struct snd_seq_client *receiver __free(snd_seq_client) = NULL; - struct snd_seq_client *sender __free(snd_seq_client) = NULL; - struct snd_seq_client_port *sport __free(snd_seq_port) = NULL; - struct snd_seq_client_port *dport __free(snd_seq_port) = NULL; int result; - receiver = snd_seq_client_use_ptr(subs->dest.client); + struct snd_seq_client *receiver __free(snd_seq_client) = + snd_seq_client_use_ptr(subs->dest.client); if (!receiver) return -ENXIO; - sender = snd_seq_client_use_ptr(subs->sender.client); + struct snd_seq_client *sender __free(snd_seq_client) = + snd_seq_client_use_ptr(subs->sender.client); if (!sender) return -ENXIO; - sport = snd_seq_port_use_ptr(sender, subs->sender.port); + struct snd_seq_client_port *sport __free(snd_seq_port) = + snd_seq_port_use_ptr(sender, subs->sender.port); if (!sport) return -ENXIO; - dport = snd_seq_port_use_ptr(receiver, subs->dest.port); + struct snd_seq_client_port *dport __free(snd_seq_port) = + snd_seq_port_use_ptr(receiver, subs->dest.port); if (!dport) return -ENXIO; @@ -1518,9 +1520,9 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client, static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_info *info = arg; - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; + struct snd_seq_queue *q __free(snd_seq_queue) = + snd_seq_queue_alloc(client->number, info->locked, info->flags); - q = snd_seq_queue_alloc(client->number, info->locked, info->flags); if (IS_ERR(q)) return PTR_ERR(q); @@ -1549,9 +1551,9 @@ static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_info *info = arg; - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(info->queue); - q = queueptr(info->queue); if (q == NULL) return -EINVAL; @@ -1569,7 +1571,6 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_info *info = arg; - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; if (info->owner != client->number) return -EINVAL; @@ -1584,7 +1585,8 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client, return -EPERM; } - q = queueptr(info->queue); + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(info->queue); if (! q) return -EINVAL; if (q->owner != client->number) @@ -1599,9 +1601,9 @@ static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_info *info = arg; - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; + struct snd_seq_queue *q __free(snd_seq_queue) = + snd_seq_queue_find_name(info->name); - q = snd_seq_queue_find_name(info->name); if (q == NULL) return -EINVAL; info->queue = q->queue; @@ -1616,10 +1618,10 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_status *status = arg; - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; struct snd_seq_timer *tmr; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(status->queue); - queue = queueptr(status->queue); if (queue == NULL) return -EINVAL; memset(status, 0, sizeof(*status)); @@ -1644,10 +1646,10 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_tempo *tempo = arg; - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; struct snd_seq_timer *tmr; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(tempo->queue); - queue = queueptr(tempo->queue); if (queue == NULL) return -EINVAL; memset(tempo, 0, sizeof(*tempo)); @@ -1693,10 +1695,10 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client, void *arg) { struct snd_seq_queue_timer *timer = arg; - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; struct snd_seq_timer *tmr; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(timer->queue); - queue = queueptr(timer->queue); if (queue == NULL) return -EINVAL; @@ -1726,10 +1728,10 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client, return -EINVAL; if (snd_seq_queue_check_access(timer->queue, client->number)) { - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; struct snd_seq_timer *tmr; + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(timer->queue); - q = queueptr(timer->queue); if (q == NULL) return -ENXIO; guard(mutex)(&q->timer_mutex); @@ -1788,9 +1790,9 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, void *arg) { struct snd_seq_client_pool *info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(info->client); - cptr = client_load_and_use_ptr(info->client); if (cptr == NULL) return -ENOENT; memset(info, 0, sizeof(*info)); @@ -1888,13 +1890,13 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, void *arg) { struct snd_seq_port_subscribe *subs = arg; - struct snd_seq_client *sender __free(snd_seq_client) = NULL; - struct snd_seq_client_port *sport __free(snd_seq_port) = NULL; - sender = client_load_and_use_ptr(subs->sender.client); + struct snd_seq_client *sender __free(snd_seq_client) = + client_load_and_use_ptr(subs->sender.client); if (!sender) return -EINVAL; - sport = snd_seq_port_use_ptr(sender, subs->sender.port); + struct snd_seq_client_port *sport __free(snd_seq_port) = + snd_seq_port_use_ptr(sender, subs->sender.port); if (!sport) return -EINVAL; return snd_seq_port_get_subscription(&sport->c_src, &subs->dest, subs); @@ -1907,16 +1909,16 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { struct snd_seq_query_subs *subs = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - struct snd_seq_client_port *port __free(snd_seq_port) = NULL; struct snd_seq_port_subs_info *group; struct list_head *p; int i; - cptr = client_load_and_use_ptr(subs->root.client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(subs->root.client); if (!cptr) return -ENXIO; - port = snd_seq_port_use_ptr(cptr, subs->root.port); + struct snd_seq_client_port *port __free(snd_seq_port) = + snd_seq_port_use_ptr(cptr, subs->root.port); if (!port) return -ENXIO; @@ -1963,7 +1965,6 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, void *arg) { struct snd_seq_client_info *info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; /* search for next client */ if (info->client < INT_MAX) @@ -1971,7 +1972,8 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, if (info->client < 0) info->client = 0; for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) { - cptr = client_load_and_use_ptr(info->client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(info->client); if (cptr) { get_client_info(cptr, info); return 0; /* found */ @@ -1987,16 +1989,16 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, void *arg) { struct snd_seq_port_info *info = arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - struct snd_seq_client_port *port __free(snd_seq_port) = NULL; - cptr = client_load_and_use_ptr(info->addr.client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; /* search for next port */ info->addr.port++; - port = snd_seq_port_query_nearest(cptr, info); + struct snd_seq_client_port *port __free(snd_seq_port) = + snd_seq_port_query_nearest(cptr, info); if (port == NULL) return -ENOENT; @@ -2067,7 +2069,6 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, { struct snd_seq_client_ump_info __user *argp = (struct snd_seq_client_ump_info __user *)arg; - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; int client, type, err = 0; size_t size; void *p; @@ -2083,7 +2084,9 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, size = sizeof(struct snd_ump_endpoint_info); else size = sizeof(struct snd_ump_block_info); - cptr = client_load_and_use_ptr(client); + + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(client); if (!cptr) return -ENOENT; @@ -2342,8 +2345,6 @@ EXPORT_SYMBOL(snd_seq_delete_kernel_client); int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, struct file *file, bool blocking) { - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - if (snd_BUG_ON(!ev)) return -EINVAL; @@ -2360,7 +2361,8 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, if (check_event_type_and_length(ev)) return -EINVAL; - cptr = client_load_and_use_ptr(client); + struct snd_seq_client *cptr __free(snd_seq_client) = + client_load_and_use_ptr(client); if (cptr == NULL) return -EINVAL; @@ -2385,8 +2387,6 @@ EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, int atomic, int hop) { - struct snd_seq_client *cptr __free(snd_seq_client) = NULL; - if (snd_BUG_ON(!ev)) return -EINVAL; @@ -2397,7 +2397,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, if (check_event_type_and_length(ev)) return -EINVAL; - cptr = snd_seq_client_use_ptr(client); + struct snd_seq_client *cptr __free(snd_seq_client) = + snd_seq_client_use_ptr(client); if (cptr == NULL) return -EINVAL; @@ -2450,9 +2451,9 @@ EXPORT_SYMBOL(snd_seq_kernel_client_ctl); /* a similar like above but taking locks; used only from OSS sequencer layer */ int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg) { - struct snd_seq_client *client __free(snd_seq_client) = NULL; + struct snd_seq_client *client __free(snd_seq_client) = + client_load_and_use_ptr(clientid); - client = client_load_and_use_ptr(clientid); if (!client) return -ENXIO; guard(mutex)(&client->ioctl_mutex); @@ -2597,9 +2598,9 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, /* list the client table */ for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { - struct snd_seq_client *client __free(snd_seq_client) = NULL; + struct snd_seq_client *client __free(snd_seq_client) = + client_load_and_use_ptr(c); - client = client_load_and_use_ptr(c); if (client == NULL) continue; if (client->type == NO_CLIENT) diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 643af4c1e838..260428747e33 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -31,10 +31,10 @@ struct snd_seq_port_info32 { static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned int cmd, struct snd_seq_port_info32 __user *data32) { - struct snd_seq_port_info *data __free(kfree) = NULL; int err; + struct snd_seq_port_info *data __free(kfree) = + kmalloc(sizeof(*data), GFP_KERNEL); - data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index de7ec7d15f23..88e930980f16 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -269,8 +269,6 @@ snd_seq_midisynth_probe(struct snd_seq_device *dev) { struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; - struct snd_seq_port_info *port __free(kfree) = NULL; - struct snd_rawmidi_info *info __free(kfree) = NULL; struct snd_rawmidi *rmidi = dev->private_data; int newclient = 0; unsigned int p, ports; @@ -281,7 +279,9 @@ snd_seq_midisynth_probe(struct snd_seq_device *dev) if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES)) return -EINVAL; - info = kmalloc(sizeof(*info), GFP_KERNEL); + + struct snd_rawmidi_info *info __free(kfree) = + kmalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; info->device = device; @@ -319,7 +319,9 @@ snd_seq_midisynth_probe(struct snd_seq_device *dev) } msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL); - port = kmalloc(sizeof(*port), GFP_KERNEL); + + struct snd_seq_port_info *port __free(kfree) = + kmalloc(sizeof(*port), GFP_KERNEL); if (msynth == NULL || port == NULL) goto __nomem; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 40fa379847e5..bbec34bba4f9 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -211,14 +211,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, list_for_each_safe(p, n, &grp->list_head) { struct snd_seq_subscribers *subs; - struct snd_seq_client *c __free(snd_seq_client) = NULL; - struct snd_seq_client_port *aport __free(snd_seq_port) = NULL; subs = get_subscriber(p, is_src); - if (is_src) - aport = get_client_port(&subs->info.dest, &c); - else - aport = get_client_port(&subs->info.sender, &c); + struct snd_seq_client *c __free(snd_seq_client) = NULL; + struct snd_seq_client_port *aport __free(snd_seq_port) = + is_src ? + get_client_port(&subs->info.dest, &c) : + get_client_port(&subs->info.sender, &c); delete_and_unsubscribe_port(client, port, subs, is_src, false); if (!aport) { diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index f5c0e401c8ae..c0c5e1424c5a 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -211,8 +211,9 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name) int i; for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; - q = queueptr(i); + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(i); + if (q) { if (strncmp(q->name, name, sizeof(q->name)) == 0) return no_free_ptr(q); @@ -285,12 +286,13 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop) { int dest, err; - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; if (snd_BUG_ON(!cell)) return -EINVAL; dest = cell->event.queue; /* destination queue */ - q = queueptr(dest); + + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(dest); if (q == NULL) return -EINVAL; /* handle relative time stamps, convert them into absolute */ @@ -403,10 +405,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked) int snd_seq_queue_timer_open(int queueid) { int result = 0; - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; struct snd_seq_timer *tmr; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(queueid); - queue = queueptr(queueid); if (queue == NULL) return -EINVAL; tmr = queue->timer; @@ -423,10 +425,10 @@ int snd_seq_queue_timer_open(int queueid) */ int snd_seq_queue_timer_close(int queueid) { - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; int result = 0; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(queueid); - queue = queueptr(queueid); if (queue == NULL) return -EINVAL; snd_seq_timer_close(queue); @@ -479,9 +481,9 @@ static void queue_use(struct snd_seq_queue *queue, int client, int use) */ int snd_seq_queue_use(int queueid, int client, int use) { - struct snd_seq_queue *queue __free(snd_seq_queue) = NULL; + struct snd_seq_queue *queue __free(snd_seq_queue) = + queueptr(queueid); - queue = queueptr(queueid); if (queue == NULL) return -EINVAL; guard(mutex)(&queue->timer_mutex); @@ -496,9 +498,9 @@ int snd_seq_queue_use(int queueid, int client, int use) */ int snd_seq_queue_is_used(int queueid, int client) { - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(queueid); - q = queueptr(queueid); if (q == NULL) return -EINVAL; /* invalid queue */ return test_bit(client, q->clients_bitmap) ? 1 : 0; @@ -642,11 +644,11 @@ static void snd_seq_queue_process_event(struct snd_seq_queue *q, */ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop) { - struct snd_seq_queue *q __free(snd_seq_queue) = NULL; - if (snd_BUG_ON(!ev)) return -EINVAL; - q = queueptr(ev->data.queue.queue); + + struct snd_seq_queue *q __free(snd_seq_queue) = + queueptr(ev->data.queue.queue); if (q == NULL) return -EINVAL; diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index a96d21981cbe..7bc18415a540 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -214,13 +214,13 @@ static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *grou static int seq_ump_group_init(struct seq_ump_client *client, int group_index) { struct snd_ump_group *group = &client->ump->groups[group_index]; - struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback pcallbacks; if (skip_group(client, group)) return 0; - port = kzalloc(sizeof(*port), GFP_KERNEL); + struct snd_seq_port_info *port __free(kfree) = + kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; @@ -243,12 +243,12 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index) /* update the sequencer ports; called from notify_fb_change callback */ static void update_port_infos(struct seq_ump_client *client) { - struct snd_seq_port_info *old __free(kfree) = NULL; - struct snd_seq_port_info *new __free(kfree) = NULL; int i, err; - old = kzalloc(sizeof(*old), GFP_KERNEL); - new = kzalloc(sizeof(*new), GFP_KERNEL); + struct snd_seq_port_info *old __free(kfree) = + kzalloc(sizeof(*old), GFP_KERNEL); + struct snd_seq_port_info *new __free(kfree) = + kzalloc(sizeof(*new), GFP_KERNEL); if (!old || !new) return; @@ -278,12 +278,12 @@ static void update_port_infos(struct seq_ump_client *client) /* create a UMP Endpoint port */ static int create_ump_endpoint_port(struct seq_ump_client *client) { - struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback pcallbacks; unsigned int rawmidi_info = client->ump->core.info_flags; int err; - port = kzalloc(sizeof(*port), GFP_KERNEL); + struct snd_seq_port_info *port __free(kfree) = + kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 9e7fd4993a10..574493fbd50d 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -361,13 +361,13 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev) { int client; struct snd_seq_port_callback pcallbacks; - struct snd_seq_port_info *pinfo __free(kfree) = NULL; int err; if (rdev->client >= 0) return 0; - pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + struct snd_seq_port_info *pinfo __free(kfree) = + kzalloc(sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; From b1bf8ac5319010e0f73183bdb78c1daf5552c8cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:29 +0100 Subject: [PATCH 055/341] ALSA: timer: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: ed96f6394e1b ("ALSA: timer: Use automatic cleanup of kfree()") Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-8-tiwai@suse.de --- sound/core/timer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index d9fff5c87613..9a4a1748ff80 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1614,12 +1614,12 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) static int snd_timer_user_ginfo(struct file *file, struct snd_timer_ginfo __user *_ginfo) { - struct snd_timer_ginfo *ginfo __free(kfree) = NULL; struct snd_timer_id tid; struct snd_timer *t; struct list_head *p; + struct snd_timer_ginfo *ginfo __free(kfree) = + memdup_user(_ginfo, sizeof(*ginfo)); - ginfo = memdup_user(_ginfo, sizeof(*ginfo)); if (IS_ERR(ginfo)) return PTR_ERR(ginfo); @@ -1756,7 +1756,6 @@ static int snd_timer_user_info(struct file *file, struct snd_timer_info __user *_info) { struct snd_timer_user *tu; - struct snd_timer_info *info __free(kfree) = NULL; struct snd_timer *t; tu = file->private_data; @@ -1766,7 +1765,8 @@ static int snd_timer_user_info(struct file *file, if (!t) return -EBADFD; - info = kzalloc(sizeof(*info), GFP_KERNEL); + struct snd_timer_info *info __free(kfree) = + kzalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; info->card = t->card ? t->card->number : -1; @@ -2192,10 +2192,10 @@ static int snd_utimer_ioctl_create(struct file *file, struct snd_timer_uinfo __user *_utimer_info) { struct snd_utimer *utimer; - struct snd_timer_uinfo *utimer_info __free(kfree) = NULL; int err, timer_fd; + struct snd_timer_uinfo *utimer_info __free(kfree) = + memdup_user(_utimer_info, sizeof(*utimer_info)); - utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info)); if (IS_ERR(utimer_info)) return PTR_ERR(utimer_info); From 3b7c7bda39e1e48f926fb3d280a5f5d20a939857 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:30 +0100 Subject: [PATCH 056/341] ALSA: vmaster: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: fb9e197f3f27 ("ALSA: vmaster: Use automatic cleanup of kfree()") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-9-tiwai@suse.de --- sound/core/vmaster.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index c657659b236c..76cc64245f5d 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -56,10 +56,10 @@ struct link_follower { static int follower_update(struct link_follower *follower) { - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err, ch; + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return -ENOMEM; uctl->id = follower->follower.id; @@ -74,7 +74,6 @@ static int follower_update(struct link_follower *follower) /* get the follower ctl info and save the initial values */ static int follower_init(struct link_follower *follower) { - struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; int err; if (follower->info.count) { @@ -84,7 +83,8 @@ static int follower_init(struct link_follower *follower) return 0; } - uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); + struct snd_ctl_elem_info *uinfo __free(kfree) = + kmalloc(sizeof(*uinfo), GFP_KERNEL); if (!uinfo) return -ENOMEM; uinfo->id = follower->follower.id; @@ -341,9 +341,9 @@ static int master_get(struct snd_kcontrol *kcontrol, static int sync_followers(struct link_master *master, int old_val, int new_val) { struct link_follower *follower; - struct snd_ctl_elem_value *uval __free(kfree) = NULL; + struct snd_ctl_elem_value *uval __free(kfree) = + kmalloc(sizeof(*uval), GFP_KERNEL); - uval = kmalloc(sizeof(*uval), GFP_KERNEL); if (!uval) return -ENOMEM; list_for_each_entry(follower, &master->followers, list) { From 04c654624f41d3c3eee48e9837a52d8a2bbc7332 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:31 +0100 Subject: [PATCH 057/341] ALSA: hda: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: ee0b0f5d32fe ("ALSA: hda/generic: Use auto cleanup for temporary buffers") Fixes: 03c5c350e38d ("ALSA: hda/realtek: Add support for new HP G12 laptops") Fixes: b0550d4c2dd8 ("ALSA: hda/common: Use auto cleanup for temporary buffers") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-10-tiwai@suse.de --- sound/hda/codecs/generic.c | 4 ++-- sound/hda/codecs/realtek/alc269.c | 4 ++-- sound/hda/codecs/realtek/realtek.c | 5 +++-- sound/hda/common/codec.c | 4 ++-- sound/hda/common/sysfs.c | 5 +++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 7bcf9aef8275..443500a3518f 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -1984,15 +1984,15 @@ static int parse_output_paths(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg __free(kfree) = NULL; unsigned int val; int best_badness = INT_MAX; int badness; bool fill_hardwired = true, fill_mio_first = true; bool best_wired = true, best_mio = true; bool hp_spk_swapped = false; + struct auto_pin_cfg *best_cfg __free(kfree) = + kmalloc(sizeof(*best_cfg), GFP_KERNEL); - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM; *best_cfg = *cfg; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c8a9b9b15cb4..85e7b5d4c496 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -2904,7 +2904,6 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc) { struct device *dev = hda_codec_dev(cdc); struct acpi_device *adev; - struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; const char *bus = NULL; static const struct { const char *hid; @@ -2934,7 +2933,8 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc) bus = "spi"; } - fwnode = fwnode_handle_get(acpi_fwnode_handle(adev)); + struct fwnode_handle *fwnode __free(fwnode_handle) = + fwnode_handle_get(acpi_fwnode_handle(adev)); acpi_dev_put(adev); if (!bus) { diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index ca377a5adadb..efe20b450529 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -215,12 +215,13 @@ void alc_update_knob_master(struct hda_codec *codec, { unsigned int val; struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl __free(kfree) = NULL; kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); if (!kctl) return; - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + + struct snd_ctl_elem_value *uctl __free(kfree) = + kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return; val = snd_hda_codec_read(codec, jack->nid, 0, diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index c6d44168c7f9..ffe7c69d5a32 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1854,9 +1854,9 @@ static int check_follower_present(struct hda_codec *codec, /* call kctl->put with the given value(s) */ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) { - struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; + struct snd_ctl_elem_value *ucontrol __free(kfree) = + kzalloc(sizeof(*ucontrol), GFP_KERNEL); - ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); if (!ucontrol) return -ENOMEM; ucontrol->value.integer.value[0] = val; diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c index f8c8483fd5e5..bedf10b30885 100644 --- a/sound/hda/common/sysfs.c +++ b/sound/hda/common/sysfs.c @@ -299,7 +299,6 @@ static void remove_trail_spaces(char *str) static int parse_hints(struct hda_codec *codec, const char *buf) { - char *key __free(kfree) = NULL; char *val; struct hda_hint *hint; @@ -308,7 +307,9 @@ static int parse_hints(struct hda_codec *codec, const char *buf) return 0; if (*buf == '=') return -EINVAL; - key = kstrndup_noeol(buf, 1024); + + char *key __free(kfree) = + kstrndup_noeol(buf, 1024); if (!key) return -ENOMEM; /* extract key and val */ From 43cc944c8e28d26f152198278f81cf7f9955ff85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:32 +0100 Subject: [PATCH 058/341] ALSA: usx2y: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Fixes: 67afec157fe6 ("ALSA: usb-audio: us144mkii: Add MIDI support and mixer controlsj") Fixes: a2a2210f2c2e ("ALSA: usb-audio: us144mkii: Implement audio playback and feedback") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-11-tiwai@suse.de --- sound/usb/usx2y/us144mkii.c | 4 ++-- sound/usb/usx2y/us144mkii_controls.c | 4 ++-- sound/usb/usx2y/us144mkii_pcm.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index f6572a576c15..bc71968df8e2 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -412,7 +412,6 @@ static int tascam_probe(struct usb_interface *intf, struct snd_card *card; struct tascam_card *tascam; int err; - char *handshake_buf __free(kfree) = NULL; if (dev->speed != USB_SPEED_HIGH) dev_info( @@ -439,7 +438,8 @@ static int tascam_probe(struct usb_interface *intf, return -ENOENT; } - handshake_buf = kmalloc(1, GFP_KERNEL); + char *handshake_buf __free(kfree) = + kmalloc(1, GFP_KERNEL); if (!handshake_buf) return -ENOMEM; diff --git a/sound/usb/usx2y/us144mkii_controls.c b/sound/usb/usx2y/us144mkii_controls.c index 5d69441ef414..62055fb8e7ba 100644 --- a/sound/usb/usx2y/us144mkii_controls.c +++ b/sound/usb/usx2y/us144mkii_controls.c @@ -373,7 +373,6 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol, { struct tascam_card *tascam = (struct tascam_card *)snd_kcontrol_chip(kcontrol); - u8 *buf __free(kfree) = NULL; int err; u32 rate = 0; @@ -384,7 +383,8 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol, } } - buf = kmalloc(3, GFP_KERNEL); + u8 *buf __free(kfree) = + kmalloc(3, GFP_KERNEL); if (!buf) return -ENOMEM; diff --git a/sound/usb/usx2y/us144mkii_pcm.c b/sound/usb/usx2y/us144mkii_pcm.c index 0c84304d4624..03dfb1f38801 100644 --- a/sound/usb/usx2y/us144mkii_pcm.c +++ b/sound/usb/usx2y/us144mkii_pcm.c @@ -115,7 +115,6 @@ void process_capture_routing_us144mkii(struct tascam_card *tascam, int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) { struct usb_device *dev = tascam->dev; - u8 *rate_payload_buf __free(kfree) = NULL; u16 rate_vendor_wValue; int err = 0; const u8 *current_payload_src; @@ -148,7 +147,8 @@ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) return -EINVAL; } - rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL); + u8 *rate_payload_buf __free(kfree) = + kmemdup(current_payload_src, 3, GFP_KERNEL); if (!rate_payload_buf) return -ENOMEM; From 03f705b9ca58b91c6dffe64875ea3d9a38cad9b5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:06:33 +0100 Subject: [PATCH 059/341] ALSA: usb-audio: Relax __free() variable declarations We used to have a variable declaration with __free() initialized with NULL. This was to keep the old coding style rule, but recently it's relaxed and rather recommends to follow the new rule to declare in place of use for __free() -- which avoids potential deadlocks or UAFs with nested cleanups. Although the current code has no bug, per se, let's follow the new standard and move the declaration to the place of assignment (or directly assign the allocated result) instead of NULL initializations. Note that there are still a few remaining __free(kfree) with NULL initializations; they are because of the code complexity (the data size calculation). Fixes: 43d4940c944c ("ALSA: usb: scarlett2: Clean ups with guard() and __free()") Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Fixes: f7d306b47a24 ("ALSA: usb-audio: Fix a DMA to stack memory bug") Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216140634.171890-12-tiwai@suse.de --- sound/usb/fcp.c | 36 ++++++++++++++++++------------------ sound/usb/mixer_scarlett2.c | 21 ++++++++++----------- sound/usb/quirks.c | 13 ++++++------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 11e9a96b46ff..1f4595d1e217 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -182,10 +182,6 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, { struct fcp_data *private = mixer->private_data; struct usb_device *dev = mixer->chip->dev; - struct fcp_usb_packet *req __free(kfree) = NULL; - struct fcp_usb_packet *resp __free(kfree) = NULL; - size_t req_buf_size = struct_size(req, data, req_size); - size_t resp_buf_size = struct_size(resp, data, resp_size); int retries = 0; const int max_retries = 5; int err; @@ -193,10 +189,14 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, if (!mixer->urb) return -ENODEV; + struct fcp_usb_packet *req __free(kfree) = NULL; + size_t req_buf_size = struct_size(req, data, req_size); req = kmalloc(req_buf_size, GFP_KERNEL); if (!req) return -ENOMEM; + struct fcp_usb_packet *resp __free(kfree) = NULL; + size_t resp_buf_size = struct_size(resp, data, resp_size); resp = kmalloc(resp_buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -300,16 +300,17 @@ retry: static int fcp_reinit(struct usb_mixer_interface *mixer) { struct fcp_data *private = mixer->private_data; - void *step0_resp __free(kfree) = NULL; - void *step2_resp __free(kfree) = NULL; if (mixer->urb) return 0; - step0_resp = kmalloc(private->step0_resp_size, GFP_KERNEL); + void *step0_resp __free(kfree) = + kmalloc(private->step0_resp_size, GFP_KERNEL); if (!step0_resp) return -ENOMEM; - step2_resp = kmalloc(private->step2_resp_size, GFP_KERNEL); + + void *step2_resp __free(kfree) = + kmalloc(private->step2_resp_size, GFP_KERNEL); if (!step2_resp) return -ENOMEM; @@ -464,7 +465,6 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer, struct fcp_init init; struct usb_device *dev = mixer->chip->dev; struct fcp_data *private = mixer->private_data; - void *resp __free(kfree) = NULL; void *step2_resp; int err, buf_size; @@ -485,7 +485,8 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer, /* Allocate response buffer */ buf_size = init.step0_resp_size + init.step2_resp_size; - resp = kmalloc(buf_size, GFP_KERNEL); + void *resp __free(kfree) = + kmalloc(buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -619,7 +620,6 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer, { struct fcp_meter_map map; struct fcp_data *private = mixer->private_data; - s16 *tmp_map __free(kfree) = NULL; int err; if (copy_from_user(&map, arg, sizeof(map))) @@ -641,7 +641,8 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer, return -EINVAL; /* Allocate and copy the map data */ - tmp_map = memdup_array_user(arg->map, map.map_size, sizeof(s16)); + s16 *tmp_map __free(kfree) = + memdup_array_user(arg->map, map.map_size, sizeof(s16)); if (IS_ERR(tmp_map)) return PTR_ERR(tmp_map); @@ -651,17 +652,16 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer, /* If the control doesn't exist, create it */ if (!private->meter_ctl) { - s16 *new_map __free(kfree) = NULL; - __le32 *meter_levels __free(kfree) = NULL; - /* Allocate buffer for the map */ - new_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL); + s16 *new_map __free(kfree) = + kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL); if (!new_map) return -ENOMEM; /* Allocate buffer for reading meter levels */ - meter_levels = kmalloc_array(map.meter_slots, sizeof(__le32), - GFP_KERNEL); + __le32 *meter_levels __free(kfree) = + kmalloc_array(map.meter_slots, sizeof(__le32), + GFP_KERNEL); if (!meter_levels) return -ENOMEM; diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index f2446bf3982c..0d4d9a392234 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2377,18 +2377,18 @@ static int scarlett2_usb( { struct scarlett2_data *private = mixer->private_data; struct usb_device *dev = mixer->chip->dev; - struct scarlett2_usb_packet *req __free(kfree) = NULL; - struct scarlett2_usb_packet *resp __free(kfree) = NULL; - size_t req_buf_size = struct_size(req, data, req_size); - size_t resp_buf_size = struct_size(resp, data, resp_size); int retries = 0; const int max_retries = 5; int err; + struct scarlett2_usb_packet *req __free(kfree) = NULL; + size_t req_buf_size = struct_size(req, data, req_size); req = kmalloc(req_buf_size, GFP_KERNEL); if (!req) return -ENOMEM; + struct scarlett2_usb_packet *resp __free(kfree) = NULL; + size_t resp_buf_size = struct_size(resp, data, resp_size); resp = kmalloc(resp_buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -3919,9 +3919,9 @@ static int scarlett2_input_select_ctl_info( struct scarlett2_data *private = mixer->private_data; int inputs = private->info->gain_input_count; int i, err; - char **values __free(kfree) = NULL; + char **values __free(kfree) = + kcalloc(inputs, sizeof(char *), GFP_KERNEL); - values = kcalloc(inputs, sizeof(char *), GFP_KERNEL); if (!values) return -ENOMEM; @@ -9083,8 +9083,6 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw, __le32 len; } __packed req; - u8 *resp __free(kfree) = NULL; - /* Flash segment must first be selected */ if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED) return -EINVAL; @@ -9122,7 +9120,8 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw, req.offset = cpu_to_le32(*offset); req.len = cpu_to_le32(count); - resp = kzalloc(count, GFP_KERNEL); + u8 *resp __free(kfree) = + kzalloc(count, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -9267,7 +9266,6 @@ static ssize_t scarlett2_devmap_read( loff_t pos) { struct usb_mixer_interface *mixer = entry->private_data; - u8 *resp_buf __free(kfree) = NULL; const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE; size_t copied = 0; @@ -9277,7 +9275,8 @@ static ssize_t scarlett2_devmap_read( if (pos + count > entry->size) count = entry->size - pos; - resp_buf = kmalloc(block_size, GFP_KERNEL); + u8 *resp_buf __free(kfree) = + kmalloc(block_size, GFP_KERNEL); if (!resp_buf) return -ENOMEM; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index f38330b095e9..9a76f35d331c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -555,7 +555,6 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf) { struct usb_host_config *config = dev->actconfig; - struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL; int err; if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD || @@ -566,8 +565,8 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac 0x10, 0x43, 0x0001, 0x000a, NULL, 0); if (err < 0) dev_dbg(&dev->dev, "error sending boot message: %d\n", err); - - new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); + struct usb_device_descriptor *new_device_descriptor __free(kfree) = + kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); if (!new_device_descriptor) return -ENOMEM; err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, @@ -910,7 +909,6 @@ static void mbox2_setup_48_24_magic(struct usb_device *dev) static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; - struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL; int err; u8 bootresponse[0x12]; int fwsize; @@ -945,7 +943,8 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "device initialised!\n"); - new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); + struct usb_device_descriptor *new_device_descriptor __free(kfree) = + kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); if (!new_device_descriptor) return -ENOMEM; @@ -1267,7 +1266,6 @@ static void mbox3_setup_defaults(struct usb_device *dev) static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; - struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL; int err; int descriptor_size; @@ -1280,7 +1278,8 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "MBOX3: device initialised!\n"); - new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); + struct usb_device_descriptor *new_device_descriptor __free(kfree) = + kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL); if (!new_device_descriptor) return -ENOMEM; From 68970b53890c877c61686c86fe28cc3a142c1584 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 16 Dec 2025 14:47:20 +0800 Subject: [PATCH 060/341] ASoC: codecs: ES8326: Add a kcontrol for PGAGAIN set a kcontrol to control bit 7 of ES8326_PGAGAIN instead of setting a fixed value in the driver Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20251216064721.4622-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 05b13661c38c..25dcce15a1fe 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -245,6 +245,7 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { adc_vol_tlv), SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv), SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv), + SOC_SINGLE("ADC PGA SE Switch", ES8326_PGAGAIN, 7, 1, 0), SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate), SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, @@ -934,11 +935,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) dev_dbg(comp->dev, "Headset detected\n"); snd_soc_jack_report(es8326->jack, SND_JACK_HEADSET, SND_JACK_HEADSET); - regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, 0x08, 0x08); - regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, - 0x80, 0x80); regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00); regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00); regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, From 9a6bc0a406608e2520f18d996483c9d2e4a9fb27 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 16 Dec 2025 14:47:21 +0800 Subject: [PATCH 061/341] ASoC: codecs: ES8326: Add kcontrol for DRE Set up a kcontrol to control DRE Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20251216064721.4622-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 14 ++++++++++++++ sound/soc/codecs/es8326.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 25dcce15a1fe..55a65ef99208 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -194,6 +194,8 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gain_tlv, -9550, 400, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gate_tlv, -9600, 600, 0); static const char *const winsize[] = { "0.25db/2 LRCK", @@ -226,6 +228,8 @@ static const char *const hp_spkvol_switch[] = { static const struct soc_enum dacpol = SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); +static const struct soc_enum dre_winsize = + SOC_ENUM_SINGLE(ES8326_ADC_DRE, 0, 16, winsize); static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); static const struct soc_enum drc_winsize = @@ -247,6 +251,16 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv), SOC_SINGLE("ADC PGA SE Switch", ES8326_PGAGAIN, 7, 1, 0), SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate), + SOC_SINGLE("ADC4 DRE Switch", ES8326_ADC_DRE, 4, 1, 0), + SOC_SINGLE("ADC3 DRE Switch", ES8326_ADC_DRE, 5, 1, 0), + SOC_SINGLE("ADC2 DRE Switch", ES8326_ADC_DRE, 6, 1, 0), + SOC_SINGLE("ADC1 DRE Switch", ES8326_ADC_DRE, 7, 1, 0), + SOC_ENUM("DRE Winsize", dre_winsize), + SOC_SINGLE("DRE Gain Switch", ES8326_ADC_DRE_GAIN, 5, 1, 0), + SOC_SINGLE_TLV("DRE Gain Volume", ES8326_ADC_DRE_GAIN, + 0, 0x1F, 0, dre_gain_tlv), + SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE, + 4, 0x07, 0, dre_gate_tlv), SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, 0, 4, 0, drc_recovery_tlv), diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index c3e52e7bdef5..1c5b3ec70a1e 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -58,6 +58,9 @@ #define ES8326_ADC1_VOL 0x2c #define ES8326_ADC2_VOL 0x2d #define ES8326_ADC_RAMPRATE 0x2e +#define ES8326_ADC_DRE 0x2f +#define ES8326_ADC_DRE_GAIN 0x30 +#define ES8326_ADC_DRE_GATE 0x31 #define ES8326_ALC_RECOVERY 0x32 #define ES8326_ALC_LEVEL 0x33 #define ES8326_ADC_HPFS1 0x34 From 331786db1b464fae42c36f53d6901d1d54975e04 Mon Sep 17 00:00:00 2001 From: David Lin Date: Wed, 17 Dec 2025 19:04:31 +0800 Subject: [PATCH 062/341] ASoC: Intel: ti-common: support tas2563 amplifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement tas2563 support code in this common module so it could be shared between multiple SOF machine drivers. Signed-off-by: David Lin Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Link: https://patch.msgid.link/20251217110433.3558136-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi-intel-ssp-common.h | 4 + sound/soc/intel/boards/Kconfig | 3 + sound/soc/intel/boards/Makefile | 3 + sound/soc/intel/boards/sof_ti_common.c | 76 +++++++++++++++++++ sound/soc/intel/boards/sof_ti_common.h | 24 ++++++ .../intel/common/soc-acpi-intel-ssp-common.c | 3 + 6 files changed, 113 insertions(+) create mode 100644 sound/soc/intel/boards/sof_ti_common.c create mode 100644 sound/soc/intel/boards/sof_ti_common.h diff --git a/include/sound/soc-acpi-intel-ssp-common.h b/include/sound/soc-acpi-intel-ssp-common.h index b4597c8dac78..fdb2fce42115 100644 --- a/include/sound/soc-acpi-intel-ssp-common.h +++ b/include/sound/soc-acpi-intel-ssp-common.h @@ -37,6 +37,9 @@ #define RT5682_ACPI_HID "10EC5682" #define RT5682S_ACPI_HID "RTL5682" +/* Texas Instruments */ +#define TAS2563_ACPI_HID "TXNW2563" + enum snd_soc_acpi_intel_codec { CODEC_NONE, @@ -63,6 +66,7 @@ enum snd_soc_acpi_intel_codec { CODEC_RT1015P, CODEC_RT1019P, CODEC_RT1308, + CODEC_TAS2563, }; enum snd_soc_acpi_intel_codec diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c23fdb6aad4c..724064149906 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -41,6 +41,9 @@ config SND_SOC_INTEL_SOF_CIRRUS_COMMON config SND_SOC_INTEL_SOF_NUVOTON_COMMON tristate +config SND_SOC_INTEL_SOF_TI_COMMON + tristate + config SND_SOC_INTEL_SOF_BOARD_HELPERS select SND_SOC_ACPI_INTEL_MATCH tristate diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index fcd517d6c279..25a1a9066cbf 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -69,5 +69,8 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_CIRRUS_COMMON) += snd-soc-intel-sof-cirrus-common snd-soc-intel-sof-nuvoton-common-y += sof_nuvoton_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_NUVOTON_COMMON) += snd-soc-intel-sof-nuvoton-common.o +snd-soc-intel-sof-ti-common-y += sof_ti_common.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_TI_COMMON) += snd-soc-intel-sof-ti-common.o + snd-soc-intel-sof-board-helpers-y += sof_board_helpers.o obj-$(CONFIG_SND_SOC_INTEL_SOF_BOARD_HELPERS) += snd-soc-intel-sof-board-helpers.o diff --git a/sound/soc/intel/boards/sof_ti_common.c b/sound/soc/intel/boards/sof_ti_common.c new file mode 100644 index 000000000000..218c3536723c --- /dev/null +++ b/sound/soc/intel/boards/sof_ti_common.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2025 Intel Corporation +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/soc-intel-quirks.h" +#include "sof_ti_common.h" + +/* + * Texas Instruments TAS2563 just mount one device to manage multiple devices, + * so the kcontrols, widgets and routes just keep one item, respectively. + */ +static const struct snd_kcontrol_new tas2563_spk_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget tas2563_spk_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Spk", NULL), +}; + +static const struct snd_soc_dapm_route tas2563_spk_dapm_routes[] = { + { "Spk", NULL, "OUT" }, +}; + +static struct snd_soc_dai_link_component tas2563_dai_link_components[] = { + { + .name = TAS2563_DEV0_NAME, + .dai_name = TAS2563_CODEC_DAI, + }, +}; + +static int tas2563_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, tas2563_spk_dapm_widgets, + ARRAY_SIZE(tas2563_spk_dapm_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm widgets, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, tas2563_spk_kcontrols, + ARRAY_SIZE(tas2563_spk_kcontrols)); + if (ret) { + dev_err(rtd->dev, "unable to add controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, tas2563_spk_dapm_routes, + ARRAY_SIZE(tas2563_spk_dapm_routes)); + if (ret) + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); + + return ret; +} + +void sof_tas2563_dai_link(struct snd_soc_dai_link *link) +{ + link->codecs = tas2563_dai_link_components; + link->num_codecs = ARRAY_SIZE(tas2563_dai_link_components); + link->init = tas2563_init; +} +EXPORT_SYMBOL_NS(sof_tas2563_dai_link, "SND_SOC_INTEL_SOF_TI_COMMON"); + +MODULE_DESCRIPTION("ASoC Intel SOF Texas Instruments helpers"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_ti_common.h b/sound/soc/intel/boards/sof_ti_common.h new file mode 100644 index 000000000000..de15845aff0c --- /dev/null +++ b/sound/soc/intel/boards/sof_ti_common.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2025 Intel Corporation. + */ + +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with Texas Instruments Codecs. + */ +#ifndef __SOF_TI_COMMON_H +#define __SOF_TI_COMMON_H + +#include +#include + +/* + * Texas Instruments TAS2563 + */ +#define TAS2563_CODEC_DAI "tasdev_codec" +#define TAS2563_DEV0_NAME "i2c-" TAS2563_ACPI_HID ":00" + +void sof_tas2563_dai_link(struct snd_soc_dai_link *link); + +#endif /* __SOF_TI_COMMON_H */ diff --git a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c index f56f4bfa5187..a12b11f2cd7a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c +++ b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c @@ -65,6 +65,9 @@ static const struct codec_map amps[] = { CODEC_MAP_ENTRY("RT1019P", "rt1019", RT1019P_ACPI_HID, CODEC_RT1019P), CODEC_MAP_ENTRY("RT1308", "rt1308", RT1308_ACPI_HID, CODEC_RT1308), + /* Texas Instruments */ + CODEC_MAP_ENTRY("TAS2563", "tas2563", TAS2563_ACPI_HID, CODEC_TAS2563), + /* * Monolithic components * From 02e7af5b6423d2dbf82f852572f2fa8c00aafb19 Mon Sep 17 00:00:00 2001 From: David Lin Date: Wed, 17 Dec 2025 19:04:32 +0800 Subject: [PATCH 063/341] ASoC: Intel: sof_rt5682: add tas2563 speaker amp support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds tas2563 which supports the RT5682 headset codec and TAS2563 speaker amplifier combination on PantherLake platform. Signed-off-by: David Lin Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Link: https://patch.msgid.link/20251217110433.3558136-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 2 ++ sound/soc/intel/boards/sof_rt5682.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 724064149906..c5942b5655d3 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -330,11 +330,13 @@ config SND_SOC_INTEL_SOF_RT5682_MACH select SND_SOC_RT5645 select SND_SOC_RT5682_I2C select SND_SOC_RT5682S + select SND_SOC_TAS2781_I2C select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_BOARD_HELPERS select SND_SOC_INTEL_SOF_MAXIM_COMMON select SND_SOC_INTEL_SOF_REALTEK_COMMON + select SND_SOC_INTEL_SOF_TI_COMMON select SND_SOC_ACPI_INTEL_MATCH help This adds support for ASoC machine driver for SOF platforms diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 3d9d8a97d153..649378957b20 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -27,6 +27,7 @@ #include "sof_board_helpers.h" #include "sof_maxim_common.h" #include "sof_realtek_common.h" +#include "sof_ti_common.h" /* Driver-specific board quirks: from bit 0 to 7 */ #define SOF_RT5682_MCLK_EN BIT(0) @@ -620,6 +621,9 @@ sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, ctx->amp_link->init = rt5650_spk_init; ctx->amp_link->ops = &sof_rt5682_ops; break; + case CODEC_TAS2563: + sof_tas2563_dai_link(ctx->amp_link); + break; default: dev_err(dev, "invalid amp type %d\n", ctx->amp_type); return -EINVAL; @@ -767,6 +771,7 @@ static int sof_audio_probe(struct platform_device *pdev) case CODEC_MAX98360A: case CODEC_RT1019P: case CODEC_RT5650: + case CODEC_TAS2563: case CODEC_NONE: /* no codec conf required */ break; @@ -934,3 +939,4 @@ MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS"); MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON"); MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON"); +MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_TI_COMMON"); From 3a4e4e0003678a87600e82d02c9522af282a5c0b Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Wed, 17 Dec 2025 16:13:58 +0530 Subject: [PATCH 064/341] ASoC: codec: rt298: Use devm_request_threaded_irq to manage IRQ lifetime and fix smatch warning Replace manual "request_threaded_irq()" with the device managed "devm_request_threaded_irq" to manage the IRQ lifetime and also it removes the smatch reported warning. Remove the manual "free_irq()" in the "remove" function as free_irq is tied to device teardown. Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251217104356.60839-3-hariconscious@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 02247593513a..7d532a5a5f73 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1282,7 +1282,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c) rt298->is_hp_in = -1; if (rt298->i2c->irq) { - ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq, + ret = devm_request_threaded_irq(&rt298->i2c->dev, rt298->i2c->irq, NULL, rt298_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); if (ret != 0) { dev_err(&i2c->dev, @@ -1298,22 +1298,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c) return ret; } -static void rt298_i2c_remove(struct i2c_client *i2c) -{ - struct rt298_priv *rt298 = i2c_get_clientdata(i2c); - - if (i2c->irq) - free_irq(i2c->irq, rt298); -} - - static struct i2c_driver rt298_i2c_driver = { .driver = { .name = "rt298", .acpi_match_table = ACPI_PTR(rt298_acpi_match), }, .probe = rt298_i2c_probe, - .remove = rt298_i2c_remove, .id_table = rt298_i2c_id, }; From da1682d5e8b53a51072552844c551b1b784e52c2 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 16 Dec 2025 17:06:16 +0800 Subject: [PATCH 065/341] ASoC: rt1320: support calibration and temperature/r0 loading This patch adds the functions/controls to support the calibration. The mixer controls could trigger a calibration and load temperature/r0 value. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20251216090616.3955293-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 1059 ++++++++++++++++++++++++++++++++- sound/soc/codecs/rt1320-sdw.h | 66 ++ 2 files changed, 1118 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index e3f9b03df3aa..d67002002bee 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -502,12 +502,8 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) case 0x2000301c: case 0x2000900f: case 0x20009018: - case 0x3fc29d80 ... 0x3fc29d83: - case 0x3fe2e000 ... 0x3fe2e003: - case 0x3fc2ab80 ... 0x3fc2abd4: - case 0x3fc2bfc0 ... 0x3fc2bfc8: - case 0x3fc2d300 ... 0x3fc2d354: - case 0x3fc2dfc0 ... 0x3fc2dfc8: + case 0x3fc000c0 ... 0x3fc2dfc8: + case 0x3fe00000 ... 0x3fe36fff: /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01): @@ -557,6 +553,8 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0xc48c ... 0xc48f: case 0xc560: case 0xc5b5 ... 0xc5b7: + case 0xc5c3: + case 0xc5c8: case 0xc5fc ... 0xc5ff: case 0xc680 ... 0xc683: case 0xc820: @@ -590,6 +588,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0xdd0c ... 0xdd13: case 0xde02: case 0xdf14 ... 0xdf1b: + case 0xe80b: case 0xe83c ... 0xe847: case 0xf01e: case 0xf717 ... 0xf719: @@ -602,7 +601,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0x2000301c: case 0x2000900f: case 0x20009018: - case 0x3fc2ab80 ... 0x3fc2abd4: + case 0x3fc2ab80 ... 0x3fc2ac4c: case 0x3fc2b780: case 0x3fc2bf80 ... 0x3fc2bf83: case 0x3fc2bfc0 ... 0x3fc2bfc8: @@ -720,6 +719,13 @@ static int rt1320_read_prop(struct sdw_slave *slave) j++; } + prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop), GFP_KERNEL); + if (!prop->dp0_prop) + return -ENOMEM; + + prop->dp0_prop->simple_ch_prep_sm = true; + prop->dp0_prop->ch_prep_timeout = 10; + /* set the timeout values */ prop->clk_stop_timeout = 64; @@ -754,6 +760,515 @@ static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned return 0; } +static void rt1320_data_rw(struct rt1320_sdw_priv *rt1320, unsigned int start, + unsigned char *data, unsigned int size, enum rt1320_rw_type rw) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int tmp; + int ret = -1; + int i, j; + + pm_runtime_set_autosuspend_delay(dev, 20000); + pm_runtime_mark_last_busy(dev); + + switch (rw) { + case RT1320_BRA_WRITE: + case RT1320_BRA_READ: + ret = sdw_bpt_send_sync(rt1320->sdw_slave->bus, rt1320->sdw_slave, &rt1320->bra_msg); + if (ret < 0) + dev_err(dev, "%s: Failed to send BRA message: %d\n", __func__, ret); + fallthrough; + case RT1320_PARAM_WRITE: + case RT1320_PARAM_READ: + if (ret < 0) { + /* if BRA fails, we try to access by the control word */ + if (rw == RT1320_BRA_WRITE || rw == RT1320_BRA_READ) { + for (i = 0; i < rt1320->bra_msg.sections; i++) { + pm_runtime_mark_last_busy(dev); + for (j = 0; j < rt1320->bra_msg.sec[i].len; j++) { + if (rw == RT1320_BRA_WRITE) { + regmap_write(rt1320->regmap, + rt1320->bra_msg.sec[i].addr + j, rt1320->bra_msg.sec[i].buf[j]); + } else { + regmap_read(rt1320->regmap, rt1320->bra_msg.sec[i].addr + j, &tmp); + rt1320->bra_msg.sec[i].buf[j] = tmp; + } + } + } + } else { + for (i = 0; i < size; i++) { + if (rw == RT1320_PARAM_WRITE) + regmap_write(rt1320->regmap, start + i, data[i]); + else { + regmap_read(rt1320->regmap, start + i, &tmp); + data[i] = tmp; + } + } + } + } + break; + } + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_mark_last_busy(dev); +} + +static unsigned long long rt1320_rsgain_to_rsratio(struct rt1320_sdw_priv *rt1320, unsigned int rsgain) +{ + unsigned long long base = 1000000000U; + unsigned long long step = 1960784U; + unsigned long long tmp, result; + + if (rsgain == 0 || rsgain == 0x1ff) + result = 1000000000; + else if (rsgain & 0x100) { + tmp = 0xff - (rsgain & 0xff); + tmp = tmp * step; + result = base + tmp; + } else { + tmp = (rsgain & 0xff); + tmp = tmp * step; + result = base - tmp; + } + + return result; +} + +static void rt1320_pr_read(struct rt1320_sdw_priv *rt1320, unsigned int reg, unsigned int *val) +{ + unsigned int byte3, byte2, byte1, byte0; + + regmap_write(rt1320->regmap, 0xc483, 0x80); + regmap_write(rt1320->regmap, 0xc482, 0x40); + regmap_write(rt1320->regmap, 0xc481, 0x0c); + regmap_write(rt1320->regmap, 0xc480, 0x10); + + regmap_write(rt1320->regmap, 0xc487, ((reg & 0xff000000) >> 24)); + regmap_write(rt1320->regmap, 0xc486, ((reg & 0x00ff0000) >> 16)); + regmap_write(rt1320->regmap, 0xc485, ((reg & 0x0000ff00) >> 8)); + regmap_write(rt1320->regmap, 0xc484, (reg & 0x000000ff)); + + regmap_write(rt1320->regmap, 0xc482, 0xc0); + + regmap_read(rt1320->regmap, 0xc48f, &byte3); + regmap_read(rt1320->regmap, 0xc48e, &byte2); + regmap_read(rt1320->regmap, 0xc48d, &byte1); + regmap_read(rt1320->regmap, 0xc48c, &byte0); + + *val = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; +} + +static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int tmp, retry = 0; + unsigned int cmd_addr; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + cmd_addr = RT1320_CMD_ID; + break; + case RT1321_DEV_ID: + cmd_addr = RT1321_CMD_ID; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + pm_runtime_mark_last_busy(dev); + /* check the value of cmd_addr becomes to zero */ + while (retry < 500) { + regmap_read(rt1320->regmap, cmd_addr, &tmp); + if (tmp == 0) + break; + usleep_range(1000, 1100); + retry++; + } + if (retry == 500) { + dev_warn(dev, "%s FW is NOT ready!", __func__); + return -ETIMEDOUT; + } + + 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; + unsigned int retry = 0, tmp; + + pm_runtime_mark_last_busy(dev); + while (retry < 200) { + regmap_read(rt1320->regmap, RT1320_POWER_STATE, &tmp); + dev_dbg(dev, "%s, RT1320_POWER_STATE=0x%x\n", __func__, tmp); + if (tmp >= ps) + break; + usleep_range(1000, 1500); + retry++; + } + if (retry == 200) { + dev_warn(dev, "%s FW Power State is NOT ready!", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int rt1320_process_fw_param(struct rt1320_sdw_priv *rt1320, unsigned char *buf, unsigned int buf_size) +{ + struct device *dev = &rt1320->sdw_slave->dev; + struct rt1320_paramcmd *paramhr = (struct rt1320_paramcmd *)buf; + unsigned char moudleid = paramhr->moudleid; + unsigned char cmdtype = paramhr->commandtype; + unsigned int fw_param_addr; + unsigned int start_addr; + int ret = 0; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + fw_param_addr = RT1320_FW_PARAM_ADDR; + start_addr = RT1320_CMD_PARAM_ADDR; + break; + case RT1321_DEV_ID: + fw_param_addr = RT1321_FW_PARAM_ADDR; + start_addr = RT1321_CMD_PARAM_ADDR; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + ret = rt1320_check_fw_ready(rt1320); + if (ret < 0) + goto _timeout_; + + /* don't set offset 0x0/0x1, it will be set later*/ + paramhr->moudleid = 0; + paramhr->commandtype = 0; + rt1320_data_rw(rt1320, fw_param_addr, buf, buf_size, RT1320_PARAM_WRITE); + + dev_dbg(dev, "%s, moudleid=%d, cmdtype=%d, paramid=%d, paramlength=%d\n", __func__, + moudleid, cmdtype, paramhr->paramid, paramhr->paramlength); + + if (cmdtype == RT1320_SET_PARAM) { + regmap_write(rt1320->regmap, fw_param_addr, moudleid); + regmap_write(rt1320->regmap, fw_param_addr + 1, 0x01); + } + if (cmdtype == RT1320_GET_PARAM) { + regmap_write(rt1320->regmap, fw_param_addr, moudleid); + regmap_write(rt1320->regmap, fw_param_addr + 1, 0x02); + ret = rt1320_check_fw_ready(rt1320); + if (ret < 0) + goto _timeout_; + + rt1320_data_rw(rt1320, start_addr, buf + 0x10, paramhr->commandlength, RT1320_PARAM_READ); + } + return 0; + +_timeout_: + dev_err(&rt1320->sdw_slave->dev, "%s: FW is NOT ready for SET/GET_PARAM\n", __func__); + return ret; +} + +static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_fw_cmdid cmdid, + unsigned int paramid, void *parambuf, unsigned int paramsize) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned char *tempbuf = NULL; + struct rt1320_paramcmd paramhr; + int ret = 0; + + tempbuf = kzalloc(sizeof(paramhr) + paramsize, GFP_KERNEL); + if (!tempbuf) + return -ENOMEM; + + paramhr.moudleid = 1; + paramhr.commandtype = cmdid; + /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ + paramhr.commandlength = 8 + paramsize; + paramhr.paramid = paramid; + paramhr.paramlength = paramsize; + + memcpy(tempbuf, ¶mhr, sizeof(paramhr)); + if (cmdid == RT1320_SET_PARAM) + memcpy(tempbuf + sizeof(paramhr), parambuf, paramsize); + + ret = rt1320_process_fw_param(rt1320, tempbuf, sizeof(paramhr) + paramsize); + if (ret < 0) { + dev_err(dev, "%s: process_fw_param failed\n", __func__); + goto _finish_; + } + + if (cmdid == RT1320_GET_PARAM) + memcpy(parambuf, tempbuf + sizeof(paramhr), paramsize); + +_finish_: + kfree(tempbuf); + return ret; +} + +static void rt1320_set_advancemode(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + struct rt1320_datafixpoint r0_data[2]; + unsigned short l_advancegain, r_advancegain; + int ret; + + /* Get advance gain/r0 */ + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); + l_advancegain = r0_data[0].advancegain; + r_advancegain = r0_data[1].advancegain; + dev_dbg(dev, "%s, LR advanceGain=0x%x 0x%x\n", __func__, l_advancegain, r_advancegain); + + /* set R0 and enable protection by SetParameter id 6, 7 */ + r0_data[0].silencedetect = 0; + r0_data[0].r0 = rt1320->r0_l_reg; + r0_data[1].silencedetect = 0; + r0_data[1].r0 = rt1320->r0_r_reg; + dev_dbg(dev, "%s, write LR r0=%d, %d\n", __func__, r0_data[0].r0, r0_data[1].r0); + + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); + ret = rt1320_check_fw_ready(rt1320); + if (ret < 0) + dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__); + + if (l_advancegain != 0 && r_advancegain != 0) { + regmap_write(rt1320->regmap, 0xdd0b, (l_advancegain & 0xff00) >> 8); + regmap_write(rt1320->regmap, 0xdd0a, (l_advancegain & 0xff)); + regmap_write(rt1320->regmap, 0xdd09, (r_advancegain & 0xff00) >> 8); + regmap_write(rt1320->regmap, 0xdd08, (r_advancegain & 0xff)); + dev_dbg(dev, "%s, set Advance mode gain\n", __func__); + } +} + +static int rt1320_invrs_load(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned long long l_rsratio, r_rsratio; + unsigned int pr_1058, pr_1059, pr_105a; + unsigned long long l_invrs, r_invrs; + unsigned long long factor = (1 << 28); + unsigned int l_rsgain, r_rsgain; + struct rt1320_datafixpoint r0_data[2]; + int ret; + + /* read L/Rch Rs Gain - it uses for compensating the R0 value */ + rt1320_pr_read(rt1320, 0x1058, &pr_1058); + rt1320_pr_read(rt1320, 0x1059, &pr_1059); + rt1320_pr_read(rt1320, 0x105a, &pr_105a); + l_rsgain = ((pr_1059 & 0x7f) << 2) | ((pr_105a & 0xc0) >> 6); + r_rsgain = ((pr_1058 & 0xff) << 1) | ((pr_1059 & 0x80) >> 7); + dev_dbg(dev, "%s, LR rsgain=0x%x, 0x%x\n", __func__, l_rsgain, r_rsgain); + + l_rsratio = rt1320_rsgain_to_rsratio(rt1320, l_rsgain); + r_rsratio = rt1320_rsgain_to_rsratio(rt1320, r_rsgain); + dev_dbg(dev, "%s, LR rsratio=%lld, %lld\n", __func__, l_rsratio, r_rsratio); + + l_invrs = (l_rsratio * factor) / 1000000000U; + r_invrs = (r_rsratio * factor) / 1000000000U; + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); + + r0_data[0].invrs = l_invrs; + r0_data[1].invrs = r_invrs; + dev_dbg(dev, "%s, write DSP LR invrs=0x%x, 0x%x\n", __func__, r0_data[0].invrs, r0_data[1].invrs); + + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); + ret = rt1320_check_fw_ready(rt1320); + if (ret < 0) + dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__); + + return ret; +} + +static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned long long l_calir0, r_calir0; + const unsigned int factor = (1 << 27); + + l_calir0 = (rt1320->r0_l_reg * 1000) / factor; + r_calir0 = (rt1320->r0_r_reg * 1000) / factor; + + dev_dbg(dev, "%s, l_calir0=%lld.%03lld ohm, r_calir0=%lld.%03lld ohm\n", __func__, + l_calir0 / 1000, l_calir0 % 1000, + r_calir0 / 1000, r_calir0 % 1000); +} + +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 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 */ + + 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]); + 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); + + 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); + if (ret < 0) { + dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__); + goto _finish_; + } + + regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); + fw_ready &= 0x1; + if (!fw_ready) { + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + goto _finish_; + } + + ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE); + if (ret < 0) { + dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__); + goto _finish_; + } + + if (rt1320->dev_id == RT1320_DEV_ID) + regmap_write(rt1320->regmap, 0xc5fb, 0x00); + regmap_write(rt1320->regmap, 0xc570, 0x0b); + regmap_write(rt1320->regmap, 0xcd00, 0xc5); + + /* disable silence detection */ + regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0x00); + dev_dbg(dev, "%s, disable silence detection\n", __func__); + + ret = rt1320_check_power_state_ready(rt1320, RT1320_K_R0_STATE); + if (ret < 0) { + dev_dbg(dev, "%s, check class D status before k r0\n", __func__); + goto _finish_; + } + + for (tmp = 0; tmp < delay_s; tmp++) { + msleep(1000); + pm_runtime_mark_last_busy(dev); + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re)); + + dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]); + dev_dbg(dev, "%s, waiting for calibration R0...%d seconds\n", __func__, tmp + 1); + } + + /* Get Calibration data */ + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re)); + dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]); + + /* Get advance gain/mean r0 */ + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &audfixpoint[0], sizeof(struct rt1320_datafixpoint)); + l_meanr0 = audfixpoint[0].meanr0; + l_advancegain = audfixpoint[0].advancegain; + l_meanr0 = ((l_meanr0 * 1000U) / factor); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &audfixpoint[1], sizeof(struct rt1320_datafixpoint)); + r_meanr0 = audfixpoint[1].meanr0; + r_advancegain = audfixpoint[1].advancegain; + r_meanr0 = ((r_meanr0 * 1000U) / factor); + dev_dbg(dev, "%s, LR meanr0=%lld, %lld\n", __func__, l_meanr0, r_meanr0); + dev_dbg(dev, "%s, LR advanceGain=0x%x, 0x%x\n", __func__, l_advancegain, r_advancegain); + dev_dbg(dev, "%s, LR invrs=0x%x, 0x%x\n", __func__, audfixpoint[0].invrs, audfixpoint[1].invrs); + + /* enable silence detection */ + regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0xe0); + dev_dbg(dev, "%s, enable silence detection\n", __func__); + + regmap_write(rt1320->regmap, 0xc5fb, reg_c5fb); + regmap_write(rt1320->regmap, 0xc570, reg_c570); + regmap_write(rt1320->regmap, 0xcd00, reg_cd00); + + rt1320->r0_l_reg = l_re[4]; + rt1320->r0_r_reg = r_re[4]; + rt1320->cali_done = true; + rt1320_calc_r0(rt1320); + +_finish_: + regmap_write(rt1320->regmap, + 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); + + /* 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]); + regmap_write(rt1320->regmap, 0xdd09, vol_reg[1]); + regmap_write(rt1320->regmap, 0xdd08, vol_reg[0]); +} + +static int rt1320_r0_cali_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->cali_done; + return 0; +} + +static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + int ret; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + rt1320->cali_done = false; + snd_soc_dapm_mutex_lock(dapm); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + ucontrol->value.integer.value[0]) { + rt1320_calibrate(rt1320); + } + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + /* * The 'patch code' is written to the patch code area. * The patch code area is used for SDCA register expansion flexibility. @@ -844,6 +1359,301 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) } } +static int 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; + int l_t0_data[38], r_t0_data[38]; + unsigned int fw_status_addr; + int ret; + + 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); + 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) { + dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _exit_; + } + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data)); + + l_t0_data[37] = l_t0 * factor; + r_t0_data[37] = r_t0 * factor; + + dev_dbg(dev, "%s, write LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]); + + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data)); + rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data)); + ret = rt1320_check_fw_ready(rt1320); + if (ret < 0) + dev_err(dev, "%s: Failed to set FW param 3,4!\n", __func__); + + rt1320->temp_l_calib = l_t0; + rt1320->temp_r_calib = r_t0; + + memset(&l_t0_data[0], 0x00, sizeof(l_t0_data)); + memset(&r_t0_data[0], 0x00, sizeof(r_t0_data)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data)); + dev_dbg(dev, "%s, read after writing LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]); + +_exit_: + regmap_write(rt1320->regmap, + 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); + + return ret; +} + +static void rt1320_dspfw_load_code(struct rt1320_sdw_priv *rt1320) +{ +struct rt1320_imageinfo { + unsigned int addr; + unsigned int size; +}; + +struct rt1320_dspfwheader { + unsigned int sync; + short num; + short crc; +}; + + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt1320->component); + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int val, i, fw_offset, fw_ready; + unsigned int fw_status_addr; + struct rt1320_dspfwheader *fwheader; + struct rt1320_imageinfo *ptr_img; + struct sdw_bpt_section sec[10]; + const struct firmware *fw = NULL; + unsigned char *fw_data; + bool dev_fw_match = false; + static const char hdr_sig[] = "AFX"; + unsigned int hdr_size = 0; + const char *dmi_vendor, *dmi_product, *dmi_sku; + char vendor[128], product[128], sku[128]; + char *ptr_vendor, *ptr_product, *ptr_sku; + char filename[128]; + + 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); + + if (dmi_vendor && dmi_product && dmi_sku) { + strscpy(vendor, dmi_vendor); + strscpy(product, dmi_product); + strscpy(sku, dmi_sku); + ptr_vendor = &vendor[0]; + ptr_product = &product[0]; + ptr_sku = &sku[0]; + ptr_vendor = strsep(&ptr_vendor, " "); + ptr_product = strsep(&ptr_product, " "); + ptr_sku = strsep(&ptr_sku, " "); + + dev_dbg(dev, "%s: DMI vendor=%s, product=%s, sku=%s\n", __func__, + vendor, product, sku); + + snprintf(filename, sizeof(filename), + "realtek/rt1320/rt1320_%s_%s_%s.dat", vendor, product, sku); + dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename); + } else if (rt1320->dspfw_name) { + snprintf(filename, sizeof(filename), "rt1320_%s.dat", + rt1320->dspfw_name); + dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename); + } else { + dev_warn(dev, "%s: Can't find proper FW file name\n", __func__); + return; + } + + snd_soc_dapm_mutex_lock(dapm); + 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) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + rt1320->fw_load_done = true; + 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; + dev_dbg(dev, "%s, fw sync = 0x%x, num=%d, crc=0x%x\n", __func__, + fwheader->sync, fwheader->num, fwheader->crc); + + if (fwheader->sync != 0x0a1c5679) { + dev_err(dev, "%s: FW sync error\n", __func__); + release_firmware(fw); + goto _exit_; + } + + fw_offset = sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * fwheader->num); + dev_dbg(dev, "%s, fw_offset = 0x%x\n", __func__, fw_offset); + + regcache_cache_bypass(rt1320->regmap, true); + + for (i = 0; i < fwheader->num; i++) { + ptr_img = (struct rt1320_imageinfo *)&fw->data[sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * i)]; + + dev_dbg(dev, "%s, fw_offset=0x%x, load fw addr=0x%x, size=%d\n", __func__, + fw_offset, ptr_img->addr, ptr_img->size); + + fw_data = (unsigned char *)&fw->data[fw_offset]; + + /* The binary file has a header of 64 bytes */ + if (memcmp(fw_data, hdr_sig, sizeof(hdr_sig)) == 0) + hdr_size = 64; + else + hdr_size = 0; + + sec[i].addr = ptr_img->addr; + sec[i].len = ptr_img->size - hdr_size; + sec[i].buf = fw_data + hdr_size; + + dev_dbg(dev, "%s, hdr_size=%d, sec[%d].buf[0]=0x%x\n", + __func__, hdr_size, i, sec[i].buf[0]); + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (ptr_img->addr == 0x3fc29d80) + if (fw_data[9] == '0') + dev_fw_match = true; + break; + case RT1321_DEV_ID: + if (ptr_img->addr == 0x3fc00000) + if (fw_data[9] == '1') + dev_fw_match = true; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + goto _exit_; + } + + fw_offset += ptr_img->size; + } + + if (dev_fw_match) { + dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); + rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; + rt1320->bra_msg.flags = SDW_MSG_FLAG_WRITE; + rt1320->bra_msg.sections = fwheader->num; + rt1320->bra_msg.sec = &sec[0]; + rt1320_data_rw(rt1320, 0, NULL, 0, RT1320_BRA_WRITE); + dev_dbg(dev, "%s, BRA downloading FW done..\n", __func__); + } + + 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__); + goto _exit_; + } + } else { + dev_err(dev, "%s: Failed to load %s firmware\n", __func__, filename); + 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: + 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: + regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); + break; + } + + /* enable DSP FW */ + regmap_write(rt1320->regmap, 0xc081, 0xfc); + regmap_update_bits(rt1320->regmap, 0xf01e, 0x1, 0x0); + + /* RsRatio should restore into DSP FW when FW was ready */ + rt1320_invrs_load(rt1320); + + /* DSP clock switches to PLL */ + regmap_write(rt1320->regmap, 0xc081, 0xfc); + /* pass DSP settings */ + regmap_write(rt1320->regmap, 0xc5c3, 0xf3); + regmap_write(rt1320->regmap, 0xc5c8, 0x05); + + rt1320->fw_load_done = true; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_mark_last_busy(dev); + +_exit_: + regmap_write(rt1320->regmap, + 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); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt1320_load_dspfw_work(struct work_struct *work) +{ + struct rt1320_sdw_priv *rt1320 = + container_of(work, struct rt1320_sdw_priv, load_dspfw_work); + int ret; + + ret = pm_runtime_resume(rt1320->component->dev); + if (ret < 0 && ret != -EACCES) + return; + + dev_dbg(&rt1320->sdw_slave->dev, "%s, Starting to reload DSP FW", __func__); + rt1320_dspfw_load_code(rt1320); +} + static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) { struct sdw_slave *slave = rt1320->sdw_slave; @@ -956,6 +1766,10 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), FUNCTION_NEEDS_INITIALIZATION); + + /* reload DSP FW */ + if (rt1320->fw_load_done) + schedule_work(&rt1320->load_dspfw_work); } if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) { regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, @@ -1358,6 +2172,189 @@ static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum, static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); 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 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); + if (ret < 0) { + dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__); + goto _timeout_; + } + + regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); + fw_ready &= 0x1; + if (!fw_ready) { + dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _timeout_; + } + + ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE); + if (ret < 0) { + dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__); + goto _timeout_; + } + + rt1320_set_advancemode(rt1320); + +_timeout_: + regmap_write(rt1320->regmap, + 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); + + return ret; +} + +static int rt1320_r0_load_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->r0_l_reg; + ucontrol->value.integer.value[1] = rt1320->r0_r_reg; + + return 0; +} + +static int rt1320_r0_load_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + int ret; + + if (!rt1320->hw_init) + return 0; + + if (ucontrol->value.integer.value[0] == 0 || + ucontrol->value.integer.value[1] == 0) + return -EINVAL; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + snd_soc_dapm_mutex_lock(dapm); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + rt1320->r0_l_reg = ucontrol->value.integer.value[0]; + rt1320->r0_r_reg = ucontrol->value.integer.value[1]; + rt1320_calc_r0(rt1320); + rt1320_r0_load(rt1320); + } + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static int rt1320_t0_r0_load_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.max = kcontrol->private_value; + + return 0; +} + +#define RT1320_T0_R0_LOAD(xname, xmax, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt1320_t0_r0_load_info, \ + .get = xhandler_get, \ + .put = xhandler_put, \ + .private_value = xmax, \ +} + +static int rt1320_dspfw_load_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->fw_load_done; + return 0; +} + +static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + ucontrol->value.integer.value[0]) + rt1320_dspfw_load_code(rt1320); + + if (!ucontrol->value.integer.value[0]) + rt1320->fw_load_done = false; + + return 0; +} + +static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->temp_l_calib; + ucontrol->value.integer.value[1] = rt1320->temp_r_calib; + return 0; +} + +static int rt1320_r0_temperature_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + int ret; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + snd_soc_dapm_mutex_lock(dapm); + if ((snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) && + ucontrol->value.integer.value[0] && ucontrol->value.integer.value[1]) + rt1320_t0_load(rt1320, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]); + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + static const struct snd_kcontrol_new rt1320_snd_controls[] = { SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume", SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), @@ -1371,6 +2368,15 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = { RT_SDCA_EXT_TLV("FU Capture Volume", SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01), rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info), + + SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0, + rt1320_r0_cali_get, rt1320_r0_cali_put), + SOC_SINGLE_EXT("DSP FW Update", SND_SOC_NOPM, 0, 1, 0, + rt1320_dspfw_load_get, rt1320_dspfw_load_put), + RT1320_T0_R0_LOAD("R0 Load Mode", 0xffffffff, + rt1320_r0_load_mode_get, rt1320_r0_load_mode_put), + RT1320_T0_R0_LOAD("R0 Temperature", 0xff, + rt1320_r0_temperature_get, rt1320_r0_temperature_put), }; static const struct snd_kcontrol_new rt1320_spk_l_dac = @@ -1606,6 +2612,18 @@ static int rt1320_sdw_component_probe(struct snd_soc_component *component) if (ret < 0 && ret != -EACCES) return ret; + /* Apply temperature and calibration data from device property */ + if ((rt1320->temp_l_calib <= 0xff) && (rt1320->temp_l_calib > 0) && + (rt1320->temp_r_calib <= 0xff) && (rt1320->temp_r_calib > 0)) + rt1320_t0_load(rt1320, rt1320->temp_l_calib, rt1320->temp_r_calib); + + if (rt1320->r0_l_calib && rt1320->r0_r_calib) { + rt1320->r0_l_reg = rt1320->r0_l_calib; + rt1320->r0_r_reg = rt1320->r0_r_calib; + rt1320_calc_r0(rt1320); + rt1320_r0_load(rt1320); + } + return 0; } @@ -1667,6 +2685,26 @@ static struct snd_soc_dai_driver rt1320_sdw_dai[] = { }, }; +static int rt1320_parse_dp(struct rt1320_sdw_priv *rt1320, struct device *dev) +{ + device_property_read_u32(dev, "realtek,temperature_l_calib", + &rt1320->temp_l_calib); + device_property_read_u32(dev, "realtek,temperature_r_calib", + &rt1320->temp_r_calib); + device_property_read_u32(dev, "realtek,r0_l_calib", + &rt1320->r0_l_calib); + device_property_read_u32(dev, "realtek,r0_r_calib", + &rt1320->r0_r_calib); + device_property_read_string(dev, "realtek,dspfw-name", + &rt1320->dspfw_name); + + dev_dbg(dev, "%s: temp_l_calib: %d temp_r_calib: %d r0_l_calib: %d, r0_r_calib: %d", + __func__, rt1320->temp_l_calib, rt1320->temp_r_calib, rt1320->r0_l_calib, rt1320->r0_r_calib); + dev_dbg(dev, "%s: dspfw_name: %s", __func__, rt1320->dspfw_name); + + return 0; +} + static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave) { @@ -1685,6 +2723,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, regcache_cache_only(rt1320->regmap, true); regcache_cache_only(rt1320->mbq_regmap, true); + rt1320_parse_dp(rt1320, dev); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -1696,6 +2736,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] = rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true; + INIT_WORK(&rt1320->load_dspfw_work, rt1320_load_dspfw_work); + ret = devm_snd_soc_register_component(dev, &soc_component_sdw_rt1320, rt1320_sdw_dai, @@ -1742,6 +2784,9 @@ static int rt1320_sdw_probe(struct sdw_slave *slave, static int rt1320_sdw_remove(struct sdw_slave *slave) { + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); + + cancel_work_sync(&rt1320->load_dspfw_work); pm_runtime_disable(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index a6d90e259dc9..5a9f496dd848 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -13,6 +13,7 @@ #include #include #include +#include "../../../drivers/soundwire/bus.h" #define RT1320_DEV_ID 0x6981 #define RT1321_DEV_ID 0x7045 @@ -22,6 +23,8 @@ #define RT1320_DEV_ID_1 0xc405 #define RT1320_DEV_ID_0 0xc406 +#define RT1320_POWER_STATE 0xc560 + #define RT1321_PATCH_MAIN_VER 0x1000cffe #define RT1321_PATCH_BETA_VER 0x1000cfff @@ -96,6 +99,57 @@ enum rt1320_version_id { #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" #define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin" +#define RT1320_FW_PARAM_ADDR 0x3fc2ab80 +#define RT1320_CMD_ID 0x3fc2ab81 +#define RT1320_CMD_PARAM_ADDR 0x3fc2ab90 +#define RT1320_DSPFW_STATUS_ADDR 0x3fc2bfc4 + +#define RT1321_FW_PARAM_ADDR 0x3fc2d300 +#define RT1321_CMD_ID 0x3fc2d301 +#define RT1321_CMD_PARAM_ADDR 0x3fc2d310 +#define RT1321_DSPFW_STATUS_ADDR 0x3fc2dfc4 + +/* FW parameter id 6, 7 */ +struct rt1320_datafixpoint { + int silencedetect; + int r0; + int meanr0; + int advancegain; + int ts; + int re; + int t; + int invrs; +}; + +struct rt1320_paramcmd { + unsigned char moudleid; + unsigned char commandtype; + unsigned short reserved1; + unsigned int commandlength; + long long reserved2; + unsigned int paramid; + unsigned int paramlength; +}; + +enum rt1320_fw_cmdid { + RT1320_FW_READY, + RT1320_SET_PARAM, + RT1320_GET_PARAM, + RT1320_GET_POOLSIZE, +}; + +enum rt1320_power_state { + RT1320_NORMAL_STATE = 0x18, + RT1320_K_R0_STATE = 0x1b, +}; + +enum rt1320_rw_type { + RT1320_BRA_WRITE = 0, + RT1320_BRA_READ = 1, + RT1320_PARAM_WRITE = 2, + RT1320_PARAM_READ = 3, +}; + struct rt1320_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; @@ -108,6 +162,18 @@ struct rt1320_sdw_priv { unsigned int dev_id; bool fu_dapm_mute; bool fu_mixer_mute[4]; + unsigned long long r0_l_reg; + unsigned long long r0_r_reg; + unsigned int r0_l_calib; + unsigned int r0_r_calib; + unsigned int temp_l_calib; + unsigned int temp_r_calib; + const char *dspfw_name; + bool cali_done; + bool fw_load_done; + bool rae_update_done; + struct work_struct load_dspfw_work; + struct sdw_bpt_msg bra_msg; }; #endif /* __RT1320_SDW_H__ */ From 87783532d34050e2bff6749a4fe9860e624a0540 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 16 Dec 2025 14:22:04 +0000 Subject: [PATCH 066/341] ASoC: SDCA: Allow sample width wild cards in set_usage() The SDCA spec allows the sample rate and width to be wild cards, but the current implementation of set_usage() only checked for a wild card of the sample rate. Fixes: 4ed357f72a0e ("ASoC: SDCA: Add hw_params() helper function") Signed-off-by: Simon Trimmer Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251216142204.183958-1-simont@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 2d328bbb95b9..498aba9df5d9 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -1478,7 +1478,7 @@ static int set_usage(struct device *dev, struct regmap *regmap, unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i); unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i); - if ((!rate || rate == target_rate) && width == target_width) { + if ((!rate || rate == target_rate) && (!width || width == target_width)) { unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i); unsigned int reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0); From 22937af75abb3260c2d563739181f4b61ddac2c1 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 16 Dec 2025 17:06:30 +0800 Subject: [PATCH 067/341] ASoC: rt1320: support RAE parameters loading This patch supports the RAE parameters loading. The RAE parameters is related to EQ/DRC parameters for each endpoint. The driver could load these parameters from the binary file. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20251216090630.3955323-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 188 ++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index d67002002bee..e31d0dcb5b75 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1423,6 +1423,158 @@ _exit_: return ret; } +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; + unsigned int fw_offset; + unsigned char *fw_data; + unsigned char *param_data; + unsigned int addr, size; + unsigned int func, value; + const char *dmi_vendor, *dmi_product, *dmi_sku; + char vendor[128], product[128], sku[128]; + char *ptr_vendor, *ptr_product, *ptr_sku; + char rae_filename[128]; + char tag[5]; + int ret = 0; + int retry = 200; + + 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); + + if (dmi_vendor && dmi_product && dmi_sku) { + strscpy(vendor, dmi_vendor); + strscpy(product, dmi_product); + strscpy(sku, dmi_sku); + ptr_vendor = &vendor[0]; + ptr_product = &product[0]; + ptr_sku = &sku[0]; + ptr_vendor = strsep(&ptr_vendor, " "); + ptr_product = strsep(&ptr_product, " "); + ptr_sku = strsep(&ptr_sku, " "); + + dev_dbg(dev, "%s: DMI vendor=%s, product=%s, sku=%s\n", __func__, + vendor, product, sku); + + snprintf(rae_filename, sizeof(rae_filename), + "realtek/rt1320/rt1320_RAE_%s_%s_%s.dat", vendor, product, sku); + dev_dbg(dev, "%s: try to load RAE file %s\n", __func__, rae_filename); + } else { + dev_warn(dev, "%s: Can't find proper RAE file name\n", __func__); + return -EINVAL; + } + + 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); + + 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__); + return -ETIMEDOUT; + } + + dev_dbg(dev, "%s, rae_fw size=0x%lx\n", __func__, rae_fw->size); + regcache_cache_bypass(rt1320->regmap, true); + for (fw_offset = 0; fw_offset < rae_fw->size;) { + + dev_dbg(dev, "%s, fw_offset=0x%x\n", __func__, fw_offset); + + fw_data = (unsigned char *)&rae_fw->data[fw_offset]; + + memcpy(tag, fw_data, 4); + tag[4] = '\0'; + dev_dbg(dev, "%s, tag=%s\n", __func__, tag); + if (strcmp(tag, xu_tag) == 0) { + dev_dbg(dev, "%s: This is a XU tag", __func__); + memcpy(&addr, (fw_data + 4), 4); + memcpy(&size, (fw_data + 8), 4); + param_data = (unsigned char *)(fw_data + 12); + + dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size); + + /* + * UI register ranges from 0x1000d000 to 0x1000d7ff + * UI registers should be accessed by tuning tool. + * So, there registers should be cached. + */ + if (addr <= 0x1000d7ff && addr >= 0x1000d000) + regcache_cache_bypass(rt1320->regmap, false); + + rt1320_data_rw(rt1320, addr, param_data, size, RT1320_PARAM_WRITE); + + regcache_cache_bypass(rt1320->regmap, true); + + fw_offset += (size + 12); + } else if (strcmp(tag, func_tag) == 0) { + dev_err(dev, "%s: This is a FUNC tag", __func__); + + memcpy(&func, (fw_data + 4), 4); + memcpy(&value, (fw_data + 8), 4); + + dev_dbg(dev, "%s: func=0x%x, value=0x%x\n", __func__, func, value); + if (func == 1) //DelayMs + msleep(value); + + fw_offset += 12; + } else { + dev_err(dev, "%s: This is NOT a XU file (wrong tag)", __func__); + break; + } + } + + regcache_cache_bypass(rt1320->regmap, false); + release_firmware(rae_fw); + + } else { + dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); + ret = -EINVAL; + 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); + + rt1320->rae_update_done = true; + +_exit_: + regmap_write(rt1320->regmap, + 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); + + return ret; +} + static void rt1320_dspfw_load_code(struct rt1320_sdw_priv *rt1320) { struct rt1320_imageinfo { @@ -2320,6 +2472,40 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, return 0; } +static int rt1320_rae_update_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->rae_update_done; + return 0; +} + +static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + ucontrol->value.integer.value[0] && rt1320->fw_load_done) + rt1320_rae_load(rt1320); + + if (!ucontrol->value.integer.value[0]) + rt1320->rae_update_done = false; + + return 0; +} + static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2377,6 +2563,8 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = { rt1320_r0_load_mode_get, rt1320_r0_load_mode_put), RT1320_T0_R0_LOAD("R0 Temperature", 0xff, rt1320_r0_temperature_get, rt1320_r0_temperature_put), + SOC_SINGLE_EXT("RAE Update", SND_SOC_NOPM, 0, 1, 0, + rt1320_rae_update_get, rt1320_rae_update_put), }; static const struct snd_kcontrol_new rt1320_spk_l_dac = From 9a123f222e1889d020d873aa6e0799098d22cdb1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Dec 2025 12:39:06 +0000 Subject: [PATCH 068/341] ASoC: cs-amp-lib: Replace __free(kfree) with normal kfree() cleanup Replace the use of __free(kfree) in _cs_amp_set_efi_calibration_data() with normal kfree() at the end of the function. Krzysztof Kozlowski pointed out that __free() can be dangerous. It can introduce new cleanup bugs. These are more subtle and difficult to spot than a missing goto in traditional cleanup, because they are triggered by writing regular idiomatic C code instead of using C++ conventions. As it's regular C style it's more likely to be missed because the code is as would be expected for C. The traditional goto also more obviously flags to anyone changing the code in the future that they must be careful about the cleanup. Arguably the traditional approach is more readable, and it avoids the manipulation of __free() pointers when the buffer is reallocated for resizing. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251201123906.86876-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index d8f8b0259cd1..b4d183e7501d 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -452,7 +452,7 @@ static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, i { u64 cal_target = cs_amp_cal_target_u64(in_data); unsigned long num_entries; - struct cirrus_amp_efi_data *data __free(kfree) = NULL; + struct cirrus_amp_efi_data *data; efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME; efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID; u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR; @@ -515,28 +515,33 @@ alloc_new: num_entries = max(num_amps, amp_index + 1); if (!data || (data->count < num_entries)) { - struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data); + struct cirrus_amp_efi_data *new_data; unsigned int new_data_size = struct_size(data, data, num_entries); - data = kzalloc(new_data_size, GFP_KERNEL); - if (!data) - return -ENOMEM; + new_data = kzalloc(new_data_size, GFP_KERNEL); + if (!new_data) { + ret = -ENOMEM; + goto err; + } - if (old_data) - memcpy(data, old_data, struct_size(old_data, data, old_data->count)); + if (data) { + memcpy(new_data, data, struct_size(data, data, data->count)); + kfree(data); + } + data = new_data; data->count = num_entries; data->size = new_data_size; } data->data[amp_index] = *in_data; ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data); - if (ret) { + if (ret) dev_err(dev, "Failed writing calibration to EFI: %d\n", ret); - return ret; - } +err: + kfree(data); - return 0; + return ret; } /** From 0db76f5b2235ab456814ee8e4e2cdf0cef09dd6b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Dec 2025 10:46:03 +0100 Subject: [PATCH 069/341] ASoC: qcom: audioreach: Add support for Speaker Protection module Speaker Protection is capability of ADSP to adjust the gain during playback to different speakers and their temperature. This allows good playback without blowing the speakers up. Implement parsing MODULE_ID_SPEAKER_PROTECTION from Audioreach topology and sending it as command to the ADSP. Reviewed-by: Srinivas Kandagatla Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251217094602.55117-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 13 +++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index f3fa0a5b4095..c32a5ee801e7 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1192,6 +1192,15 @@ static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_modu return q6apm_send_cmd_sync(graph->apm, pkt, 0); } +static int audioreach_speaker_protection(struct q6apm_graph *graph, + struct audioreach_module *module, + uint32_t operation_mode) +{ + return audioreach_send_u32_param(graph, module, PARAM_ID_SP_OP_MODE, + operation_mode); +} + + int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, struct audioreach_module_config *cfg) { @@ -1241,6 +1250,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_GAPLESS: rc = audioreach_gapless_set_media_format(graph, module, cfg); break; + case MODULE_ID_SPEAKER_PROTECTION: + rc = audioreach_speaker_protection(graph, module, + PARAM_ID_SP_OP_MODE_NORMAL); + break; default: rc = 0; } diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index d1b60b36468a..19828b4accce 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -31,6 +31,7 @@ struct q6apm_graph; #define MODULE_ID_MP3_DECODE 0x0700103B #define MODULE_ID_GAPLESS 0x0700104D #define MODULE_ID_DISPLAY_PORT_SINK 0x07001069 +#define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 #define MODULE_ID_OPUS_DEC 0x07001174 #define APM_CMD_GET_SPF_STATE 0x01001021 @@ -559,6 +560,17 @@ struct data_logging_config { uint32_t mode; } __packed; +/* Speaker Protection */ +#define PARAM_ID_SP_OP_MODE 0x080011e9 +#define PARAM_ID_SP_OP_MODE_NORMAL 0 +#define PARAM_ID_SP_OP_MODE_CALIBRATION 1 +#define PARAM_ID_SP_OP_MODE_FACTORY_TEST 2 +#define PARAM_ID_SP_OP_MODE_VALIDATION 3 + +struct param_id_sp_op_mode { + uint32_t operation_mode; +} __packed; + #define PARAM_ID_SAL_OUTPUT_CFG 0x08001016 struct param_id_sal_output_config { uint32_t bits_per_sample; From 3e43a8c033c3187e0f441ed5570a0fb5dcc9dafb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Dec 2025 10:46:04 +0100 Subject: [PATCH 070/341] ASoC: qcom: audioreach: Add support for VI Sense module VI Sense module in ADSP is responsible for feedback loop for measuring current and voltage of amplifiers, necessary for proper calibration of Speaker Protection algorightms. Implement parsing MODULE_ID_SPEAKER_PROTECTION_VI from Audioreach topology and sending it as command to the ADSP. Reviewed-by: Srinivas Kandagatla Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251217094602.55117-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 107 ++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 27 ++++++++ 2 files changed, 134 insertions(+) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index c32a5ee801e7..b28451558974 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -202,6 +202,31 @@ struct apm_display_port_module_intf_cfg { } __packed; #define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8) +struct apm_module_sp_vi_op_mode_cfg { + struct apm_module_param_data param_data; + struct param_id_sp_vi_op_mode_cfg cfg; +} __packed; + +#define APM_SP_VI_OP_MODE_CFG_PSIZE(ch) ALIGN( \ + sizeof(struct apm_module_sp_vi_op_mode_cfg) + \ + (ch) * sizeof(uint32_t), 8) + +struct apm_module_sp_vi_ex_mode_cfg { + struct apm_module_param_data param_data; + struct param_id_sp_vi_ex_mode_cfg cfg; +} __packed; + +#define APM_SP_VI_EX_MODE_CFG_PSIZE ALIGN(sizeof(struct apm_module_sp_vi_ex_mode_cfg), 8) + +struct apm_module_sp_vi_channel_map_cfg { + struct apm_module_param_data param_data; + struct param_id_sp_vi_channel_map_cfg cfg; +} __packed; + +#define APM_SP_VI_CH_MAP_CFG_PSIZE(ch) ALIGN( \ + sizeof(struct apm_module_sp_vi_channel_map_cfg) + \ + (ch) * sizeof(uint32_t), 8) + static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr) { @@ -1200,6 +1225,84 @@ static int audioreach_speaker_protection(struct q6apm_graph *graph, operation_mode); } +static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, + struct audioreach_module *module, + struct audioreach_module_config *mcfg) +{ + u32 num_channels = mcfg->num_channels; + struct apm_module_sp_vi_op_mode_cfg *op_cfg; + struct apm_module_sp_vi_channel_map_cfg *cm_cfg; + 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; + struct gpr_pkt *pkt; + void *p; + + if (num_channels > 2) { + dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); + return -EINVAL; + } + + op_sz = APM_SP_VI_OP_MODE_CFG_PSIZE(num_channels); + /* Channel mapping for Isense and Vsense, thus twice number of speakers. */ + cm_sz = APM_SP_VI_CH_MAP_CFG_PSIZE(num_channels * 2); + ex_sz = APM_SP_VI_EX_MODE_CFG_PSIZE; + + payload_size = op_sz + cm_sz + ex_sz; + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + + op_cfg = p; + param_data = &op_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_SP_VI_OP_MODE_CFG; + param_data->param_size = op_sz - APM_MODULE_PARAM_DATA_SIZE; + + op_cfg->cfg.num_channels = num_channels; + op_cfg->cfg.operation_mode = PARAM_ID_SP_VI_OP_MODE_NORMAL; + p += op_sz; + + cm_cfg = p; + param_data = &cm_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_SP_VI_CHANNEL_MAP_CFG; + 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++) { + /* + * 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; + } + + p += cm_sz; + + ex_cfg = p; + param_data = &ex_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_SP_VI_EX_MODE_CFG; + param_data->param_size = ex_sz - APM_MODULE_PARAM_DATA_SIZE; + + ex_cfg->cfg.factory_mode = 0; + + rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); + + kfree(pkt); + + return rc; +} int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, struct audioreach_module_config *cfg) @@ -1254,6 +1357,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod rc = audioreach_speaker_protection(graph, module, PARAM_ID_SP_OP_MODE_NORMAL); break; + case MODULE_ID_SPEAKER_PROTECTION_VI: + rc = audioreach_speaker_protection_vi(graph, module, cfg); + break; + default: rc = 0; } diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 19828b4accce..03cfd32f1d0c 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -32,6 +32,7 @@ struct q6apm_graph; #define MODULE_ID_GAPLESS 0x0700104D #define MODULE_ID_DISPLAY_PORT_SINK 0x07001069 #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 +#define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 #define MODULE_ID_OPUS_DEC 0x07001174 #define APM_CMD_GET_SPF_STATE 0x01001021 @@ -571,6 +572,32 @@ struct param_id_sp_op_mode { uint32_t operation_mode; } __packed; +/* Speaker Protection VI */ + +#define PARAM_ID_SP_VI_OP_MODE_CFG 0x080011f4 +#define PARAM_ID_SP_VI_OP_MODE_NORMAL 0 +#define PARAM_ID_SP_VI_OP_MODE_CALIBRATION 1 +#define PARAM_ID_SP_VI_OP_MODE_FACTORY_TEST 2 +#define PARAM_ID_SP_VI_OP_MODE_VALIDATION 3 +struct param_id_sp_vi_op_mode_cfg { + uint32_t num_channels; + uint32_t operation_mode; + uint32_t quick_calibration; + uint32_t r0_t0_selection[]; +} __packed; + +#define PARAM_ID_SP_VI_EX_MODE_CFG 0x080011ff +struct param_id_sp_vi_ex_mode_cfg { + uint32_t factory_mode; +} __packed; + +#define PARAM_ID_SP_VI_CHANNEL_MAP_CFG 0x08001203 +struct param_id_sp_vi_channel_map_cfg { + uint32_t num_channels; + /* [ Vsense of ch 1, Isense of ch 1, Vsense of ch 2, Isense of ch 2, ... ] */ + uint32_t channel_mapping[]; +} __packed; + #define PARAM_ID_SAL_OUTPUT_CFG 0x08001016 struct param_id_sal_output_config { uint32_t bits_per_sample; From f764645cb85a8b8f58067289cdfed28f6c1cdf49 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Dec 2025 06:24:32 +0000 Subject: [PATCH 071/341] ASoC: codecs: tas2780: tidyup format check in tas2780_set_fmt() Current code is using messy code to check format. Let's cleanup it by using switch(). Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5trrljz.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2780.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index a1963415c931..cf3f6abd7e7b 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -319,25 +319,22 @@ static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) goto err; } - if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) - || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) - == SND_SOC_DAIFMT_DSP_A)){ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: iface = TAS2780_TDM_CFG2_SCFG_I2S; tdm_rx_start_slot = 1; - } else { - if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) - == SND_SOC_DAIFMT_DSP_B) - || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) - == SND_SOC_DAIFMT_LEFT_J)) { - iface = TAS2780_TDM_CFG2_SCFG_LEFT_J; - tdm_rx_start_slot = 0; - } else { - dev_err(tas2780->dev, - "%s:DAI Format is not found, fmt=0x%x\n", - __func__, fmt); - ret = -EINVAL; - goto err; - } + break; + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: + iface = TAS2780_TDM_CFG2_SCFG_LEFT_J; + tdm_rx_start_slot = 0; + break; + default: + dev_err(tas2780->dev, + "%s:DAI Format is not found, fmt=0x%x\n", __func__, fmt); + ret = -EINVAL; + goto err; } ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1, TAS2780_TDM_CFG1_MASK, From 2fa74713744dc5e908fff851c20f5f89fd665fb7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:38 +0200 Subject: [PATCH 072/341] ASoC: SOF: ipc4-control: If there is no data do not send bytes update When the bytes control have no data (payload) then there is no need to send an IPC message as there is nothing to send. Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 976a4794d610..0a05f66ec7d9 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -412,8 +412,16 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, int ret = 0; /* Send the new data to the firmware only if it is powered up */ - if (set && !pm_runtime_active(sdev->dev)) - return 0; + if (set) { + if (!pm_runtime_active(sdev->dev)) + return 0; + + if (!data->size) { + dev_dbg(sdev->dev, "%s: No data to be sent.\n", + scontrol->name); + return 0; + } + } msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); From a653820700b81c9e6f05ac23b7969ecec1a18e85 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:39 +0200 Subject: [PATCH 073/341] ASoC: SOF: ipc4-topology: Correct the allocation size for bytes controls The size of the data behind of scontrol->ipc_control_data for bytes controls is: [1] sizeof(struct sof_ipc4_control_data) + // kernel only struct [2] sizeof(struct sof_abi_hdr)) + payload The max_size specifies the size of [2] and it is coming from topology. Change the function to take this into account and allocate adequate amount of memory behind scontrol->ipc_control_data. With the change we will allocate [1] amount more memory to be able to hold the full size of data. Fixes: a382082ff74b ("ASoC: SOF: ipc4-topology: Add support for TPLG_CTL_BYTES") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 221e9d4052b8..4272d84679ac 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2855,22 +2855,41 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ struct sof_ipc4_msg *msg; int ret; - if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) { - dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n", + /* + * The max_size is coming from topology and indicates the maximum size + * of sof_abi_hdr plus the payload, which excludes the local only + * 'struct sof_ipc4_control_data' + */ + if (scontrol->max_size < sizeof(struct sof_abi_hdr)) { + dev_err(sdev->dev, + "insufficient maximum size for a bytes control %s: %zu.\n", scontrol->name, scontrol->max_size); return -EINVAL; } - if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) { - dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n", - scontrol->name, scontrol->priv_size, - scontrol->max_size - sizeof(*control_data)); + if (scontrol->priv_size > scontrol->max_size) { + dev_err(sdev->dev, + "bytes control %s initial data size %zu exceeds max %zu.\n", + scontrol->name, scontrol->priv_size, scontrol->max_size); return -EINVAL; } - scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size; + if (scontrol->priv_size < sizeof(struct sof_abi_hdr)) { + dev_err(sdev->dev, + "bytes control %s initial data size %zu is insufficient.\n", + scontrol->name, scontrol->priv_size); + return -EINVAL; + } - scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); + /* + * The used size behind the cdata pointer, which can be smaller than + * the maximum size + */ + scontrol->size = sizeof(*control_data) + scontrol->priv_size; + + /* Allocate the cdata: local struct size + maximum payload size */ + scontrol->ipc_control_data = kzalloc(sizeof(*control_data) + scontrol->max_size, + GFP_KERNEL); if (!scontrol->ipc_control_data) return -ENOMEM; From c1876fc33c5976837e4c73719c7582617efc6919 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:40 +0200 Subject: [PATCH 074/341] ASoC: SOF: ipc4-control: Use the correct size for scontrol->ipc_control_data The size of the data behind scontrol->ipc_control_data is stored in scontrol->size, use this when copying data for backup/restore. Fixes: db38d86d0c54 ("ASoC: sof: Improve sof_ipc4_bytes_ext_put function") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 0a05f66ec7d9..80111672c179 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -66,7 +66,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, * configuration */ memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, - scontrol->max_size); + scontrol->size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; /* Send the last known good configuration to firmware */ @@ -567,7 +567,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, if (!scontrol->old_ipc_control_data) { /* Create a backup of the current, valid bytes control */ scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, - scontrol->max_size, GFP_KERNEL); + scontrol->size, GFP_KERNEL); if (!scontrol->old_ipc_control_data) return -ENOMEM; } @@ -575,7 +575,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, /* Copy the whole binary data which includes the ABI header and the payload */ if (copy_from_user(data, tlvd->tlv, header.length)) { memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, - scontrol->max_size); + scontrol->size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; return -EFAULT; From ebcfdbe4add923dfb690e6fb9d158da87ae0b6bf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:41 +0200 Subject: [PATCH 075/341] ASoC: SOF: ipc4-control: Keep the payload size up to date When the bytes data is read from the firmware, the size of the payload can be different than what it was previously. For example when the topology did not contained payload data at all for the control, the data size was 0. For get operation allow maximum size of payload to be read and then update the sizes according to the completed message. Similarly, keep the size in sync when updating the data in firmware. With the change we will be able to read data from firmware for bytes controls which did not had initial payload defined in topology. Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 80111672c179..453ed1643b89 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -426,13 +426,21 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); msg->data_ptr = data->data; - msg->data_size = data->size; + if (set) + msg->data_size = data->size; + else + msg->data_size = scontrol->max_size - sizeof(*data); ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "Failed to %s for %s\n", set ? "set bytes update" : "get bytes", scontrol->name); + } else if (!set) { + /* Update the sizes according to the received payload data */ + data->size = msg->data_size; + scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; + } msg->data_ptr = NULL; msg->data_size = 0; @@ -448,6 +456,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; size_t size; + int ret; if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -469,9 +478,12 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); - sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + if (!ret) + /* Update the cdata size */ + scontrol->size = sizeof(*cdata) + size; - return 0; + return ret; } static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol, @@ -581,6 +593,9 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, return -EFAULT; } + /* Update the cdata size */ + scontrol->size = sizeof(*cdata) + header.length; + return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); } From 2fdde18a2cb1631c01e4ab87d949564c7d134dd8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:42 +0200 Subject: [PATCH 076/341] ASoC: SOF: ipc4-topology: Set initial param_id for bytes control type Set the param_id in extension based on the information we got from the topology. If the payload did not present then the param_id will remain 0. Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 4272d84679ac..d64e498c6985 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2924,6 +2924,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(control_data->data->type); return 0; From d96cb0b86d6e8bbbbfa425771606f6c1aebc318e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:43 +0200 Subject: [PATCH 077/341] ASoC: SOF: ipc4: Support for sending payload along with LARGE_CONFIG_GET There are message types when we would need to send a payload along with the LARGE_CONFIG_GET message to provide information to the firmware on what data is requested. Such cases are the ALSA Kcontrol related messages when the high level param_id tells only the type of the control, but the ID/index of the exact control is specified in the payload area. The caller must place the payload for TX before calling the set_get_data() and this payload will be sent alongside with the message to the firmware. The data area will be overwritten by the received data from firmware. Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index a4a090e6724a..20d723f48fff 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -15,6 +15,7 @@ #include "sof-audio.h" #include "ipc4-fw-reg.h" #include "ipc4-priv.h" +#include "ipc4-topology.h" #include "ipc4-telemetry.h" #include "ops.h" @@ -433,6 +434,23 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ return ret; } +static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx) +{ + /* + * Messages that require TX payload with LARGE_CONFIG_GET. + * The TX payload is placed into the IPC message data section by caller, + * which needs to be copied to temporary buffer since the received data + * will overwrite it. + */ + switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) { + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID): + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID): + return true; + default: + return false; + } +} + static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, size_t payload_bytes, bool set) { @@ -444,6 +462,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, struct sof_ipc4_msg tx = {{ 0 }}; struct sof_ipc4_msg rx = {{ 0 }}; size_t remaining = payload_bytes; + void *tx_payload_for_get = NULL; + size_t tx_data_size = 0; size_t offset = 0; size_t chunk_size; int ret; @@ -469,10 +489,20 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); + if (sof_ipc4_tx_payload_for_get_data(&tx)) { + tx_data_size = min(ipc4_msg->data_size, payload_limit); + tx_payload_for_get = kmemdup(ipc4_msg->data_ptr, tx_data_size, + GFP_KERNEL); + if (!tx_payload_for_get) + return -ENOMEM; + } + /* ensure the DSP is in D0i0 before sending IPC */ ret = snd_sof_dsp_set_power_state(sdev, &target_state); - if (ret < 0) + if (ret < 0) { + kfree(tx_payload_for_get); return ret; + } /* Serialise IPC TX */ mutex_lock(&sdev->ipc->tx_mutex); @@ -506,7 +536,15 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, rx.data_size = chunk_size; rx.data_ptr = ipc4_msg->data_ptr + offset; - tx_size = 0; + if (tx_payload_for_get) { + tx_size = tx_data_size; + tx.data_size = tx_size; + tx.data_ptr = tx_payload_for_get; + } else { + tx_size = 0; + tx.data_size = 0; + tx.data_ptr = NULL; + } rx_size = chunk_size; } @@ -553,6 +591,8 @@ out: mutex_unlock(&sdev->ipc->tx_mutex); + kfree(tx_payload_for_get); + return ret; } From 7fd8c216c422c5d42addc3e46d5d26630ff646d1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:44 +0200 Subject: [PATCH 078/341] ASoC: SOF: ipc4: Add definition for generic bytes control Currently IPC4 only supports module specific custom bytes controls, where each control's param_id is module specific. These bytes controls cannot be handled in a generic way, there is no clean way to support for example notifications from firmware when their data has been changed. Add definition for generic bytes control which should be handled in a similar way as the enum/switch controls. The generic param id for BYTES is selected to be 202 Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.h | 9 +++++++-- sound/soc/sof/ipc4.c | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 191b51d97993..9a028a59c553 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -368,19 +368,24 @@ struct sof_ipc4_control_data { #define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200 #define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201 +#define SOF_IPC4_BYTES_CONTROL_PARAM_ID 202 /** * struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters * @id: unique id of the control - * @num_elems: Number of elements in the chanv array + * @num_elems: Number of elements in the chanv array or number of bytes in data * @reserved: reserved for future use, must be set to 0 * @chanv: channel ID and value array + * @data: binary payload */ struct sof_ipc4_control_msg_payload { uint16_t id; uint16_t num_elems; uint32_t reserved[4]; - DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); + union { + DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); + DECLARE_FLEX_ARRAY(uint8_t, data); + }; } __packed; /** diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 20d723f48fff..2be8d8ce487a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -445,6 +445,7 @@ static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx) switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) { case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID): case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID): + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_BYTES_CONTROL_PARAM_ID): return true; default: return false; From 2a28b5240f2b328495c6565d277f438dbc583d61 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Dec 2025 16:39:45 +0200 Subject: [PATCH 079/341] ASoC: SOF: ipc4-control: Add support for generic bytes control The generic byte control can be used in cases when the bytes data can be changed by the firmware and it sends a notification about the change, similarly to the enum and switch controls. The generic control support is needed as from the param_id itself it is not possible to know which control has changed. The needed information is only available via generic control change notification. Generic bytes controls use param_id 202 and their change notification can contain payload with the change embedded or just the header message as notification. Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20251217143945.2667-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 154 ++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 453ed1643b89..0500b690f9a3 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -284,6 +284,105 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol) kfree(data); } +static int +sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc4_control_msg_payload *msg_data; + struct sof_abi_hdr *data = cdata->data; + struct sof_ipc4_msg *msg = &cdata->msg; + size_t data_size; + int ret; + + data_size = struct_size(msg_data, data, data->size); + msg_data = kzalloc(data_size, GFP_KERNEL); + if (!msg_data) + return -ENOMEM; + + msg_data->id = cdata->index; + msg_data->num_elems = data->size; + memcpy(msg_data->data, data->data, data->size); + + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); + + msg->data_ptr = msg_data; + msg->data_size = data_size; + + ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock); + msg->data_ptr = NULL; + msg->data_size = 0; + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to set control update for %s\n", + __func__, scontrol->name); + + kfree(msg_data); + + return ret; +} + +static int +sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc4_control_msg_payload *msg_data; + struct sof_abi_hdr *data = cdata->data; + struct sof_ipc4_msg *msg = &cdata->msg; + size_t data_size; + int ret = 0; + + if (!scontrol->comp_data_dirty) + return 0; + + if (!pm_runtime_active(scomp->dev)) + return 0; + + data_size = scontrol->max_size - sizeof(*data); + if (data_size < sizeof(*msg_data)) + data_size = sizeof(*msg_data); + + msg_data = kzalloc(data_size, GFP_KERNEL); + if (!msg_data) + return -ENOMEM; + + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); + + msg_data->id = cdata->index; + msg_data->num_elems = 0; /* ignored for bytes */ + + msg->data_ptr = msg_data; + msg->data_size = data_size; + + scontrol->comp_data_dirty = false; + ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock); + if (!ret) { + if (msg->data_size > scontrol->max_size - sizeof(*data)) { + dev_err(scomp->dev, + "%s: no space for data in %s (%zu, %zu)\n", + __func__, scontrol->name, msg->data_size, + scontrol->max_size - sizeof(*data)); + goto out; + } + + data->size = msg->data_size; + scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; + memcpy(data->data, msg->data_ptr, data->size); + } else { + dev_err(scomp->dev, "Failed to read control data for %s\n", + scontrol->name); + scontrol->comp_data_dirty = true; + } + +out: + msg->data_ptr = NULL; + msg->data_size = 0; + + kfree(msg_data); + + return ret; +} + static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol) { @@ -423,6 +522,13 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, } } + if (data->type == SOF_IPC4_BYTES_CONTROL_PARAM_ID) { + if (set) + return sof_ipc4_set_bytes_control_data(scontrol, lock); + else + return sof_ipc4_refresh_bytes_control(scontrol, lock); + } + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); msg->data_ptr = data->data; @@ -507,6 +613,8 @@ static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol, return -EINVAL; } + sof_ipc4_refresh_bytes_control(scontrol, true); + size = data->size + sizeof(*data); /* copy back to kcontrol */ @@ -661,6 +769,8 @@ static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, const unsigned int __user *binary_data, unsigned int size) { + sof_ipc4_refresh_bytes_control(scontrol, true); + return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false); } @@ -714,6 +824,9 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) case SOF_IPC4_ENUM_CONTROL_PARAM_ID: type = SND_SOC_TPLG_TYPE_ENUM; break; + case SOF_IPC4_BYTES_CONTROL_PARAM_ID: + type = SND_SOC_TPLG_TYPE_BYTES; + break; default: dev_err(sdev->dev, "%s: Invalid control type for module %u.%u: %u\n", @@ -764,23 +877,38 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) * The message includes the updated value/data, update the * control's local cache using the received notification */ - for (i = 0; i < msg_data->num_elems; i++) { - u32 channel = msg_data->chanv[i].channel; + if (type == SND_SOC_TPLG_TYPE_BYTES) { + struct sof_abi_hdr *data = cdata->data; - if (channel >= scontrol->num_channels) { + if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) { dev_warn(sdev->dev, - "Invalid channel index for %s: %u\n", - scontrol->name, i); - - /* - * Mark the scontrol as dirty to force a refresh - * on next read - */ - scontrol->comp_data_dirty = true; - break; + "%s: no space for data in %s (%u, %zu)\n", + __func__, scontrol->name, msg_data->num_elems, + scontrol->max_size - sizeof(*data)); + } else { + memcpy(data->data, msg_data->data, msg_data->num_elems); + data->size = msg_data->num_elems; + scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; } + } else { + for (i = 0; i < msg_data->num_elems; i++) { + u32 channel = msg_data->chanv[i].channel; - cdata->chanv[channel].value = msg_data->chanv[i].value; + if (channel >= scontrol->num_channels) { + dev_warn(sdev->dev, + "Invalid channel index for %s: %u\n", + scontrol->name, i); + + /* + * Mark the scontrol as dirty to force a refresh + * on next read + */ + scontrol->comp_data_dirty = true; + break; + } + + cdata->chanv[channel].value = msg_data->chanv[i].value; + } } } else { /* From 5965df0670d9f4a092aa111a01c62a450e689c8a Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:12 +0530 Subject: [PATCH 080/341] ASoC: tas2783A: sdw_utils: support ch 3 & 4 Currently the machine driver for tas2783A can only support 2 channels. This patch adds support for 2 channel playback with 4 device setup. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_ti_amp.c | 4 ++++ sound/soc/sdw_utils/soc_sdw_utils.c | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_ti_amp.c b/sound/soc/sdw_utils/soc_sdw_ti_amp.c index cbd60faecd09..488ef2ef45d4 100644 --- a/sound/soc/sdw_utils/soc_sdw_ti_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_ti_amp.c @@ -58,6 +58,10 @@ int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, strscpy(speaker, "Left Spk", sizeof(speaker)); } else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) { strscpy(speaker, "Right Spk", sizeof(speaker)); + } else if (!strncmp(prefix, "tas2783-3", strlen("tas2783-3"))) { + strscpy(speaker, "Left Spk2", sizeof(speaker)); + } else if (!strncmp(prefix, "tas2783-4", strlen("tas2783-4"))) { + strscpy(speaker, "Right Spk2", sizeof(speaker)); } else { ret = -EINVAL; dev_err(card->dev, "unhandled prefix %s", prefix); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 6c656b2f7f3a..e699a713cf81 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -40,11 +40,25 @@ static const struct snd_soc_dapm_widget lr_spk_widgets[] = { SND_SOC_DAPM_SPK("Right Spk", NULL), }; +static const struct snd_soc_dapm_widget lr_4spk_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Left Spk2", NULL), + SND_SOC_DAPM_SPK("Right Spk2", NULL), +}; + static const struct snd_kcontrol_new lr_spk_controls[] = { SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), }; +static const struct snd_kcontrol_new lr_4spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Left Spk2"), + SOC_DAPM_PIN_SWITCH("Right Spk2"), +}; + static const struct snd_soc_dapm_widget rt700_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), @@ -69,10 +83,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_ti_amp_init, .rtd_init = asoc_sdw_ti_spk_rtd_init, - .controls = lr_spk_controls, - .num_controls = ARRAY_SIZE(lr_spk_controls), - .widgets = lr_spk_widgets, - .num_widgets = ARRAY_SIZE(lr_spk_widgets), + .controls = lr_4spk_controls, + .num_controls = ARRAY_SIZE(lr_4spk_controls), + .widgets = lr_4spk_widgets, + .num_widgets = ARRAY_SIZE(lr_4spk_widgets), }, }, .dai_num = 1, From a3b0cd63f212686dd57eacd5d685ac259631248f Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:13 +0530 Subject: [PATCH 081/341] ASoC: tas2783A: use custom firmware Use the firmware version same as in Windows projects. The firmware contains algorithm parameters and some device configuration writes which are part of the same firmware file. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-2-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 145 +++++++++++++++++++-------------- sound/soc/codecs/tas2783.h | 1 + 2 files changed, 84 insertions(+), 62 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 43b779873b93..6a0644670a0b 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -35,8 +35,8 @@ #include "tas2783.h" #define TIMEOUT_FW_DL_MS (3000) -#define FW_DL_OFFSET 36 -#define FW_FL_HDR 12 +#define FW_DL_OFFSET 84 /* binary file information */ +#define FW_FL_HDR 20 /* minimum number of bytes in one chunk */ #define TAS2783_PROBE_TIMEOUT 5000 #define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92) @@ -49,11 +49,22 @@ static const u32 tas2783_cali_reg[] = { TAS2783_CAL_TLIM, }; -struct bin_header_t { - u16 vendor_id; - u16 version; +struct tas_fw_hdr { + u32 size; + u32 version_offset; + u32 plt_id; + u32 ppc3_ver; + u32 timestamp; + u8 ddc_name[64]; +}; + +struct tas_fw_file { + u32 vendor_id; u32 file_id; + u32 version; u32 length; + u32 dest_addr; + u8 *fw_data; }; struct calibration_data { @@ -735,13 +746,28 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) return ret; } -static s32 read_header(const u8 *data, struct bin_header_t *hdr) +static s32 tas_fw_read_hdr(const u8 *data, struct tas_fw_hdr *hdr) { - hdr->vendor_id = get_unaligned_le16(&data[0]); - hdr->file_id = get_unaligned_le32(&data[2]); - hdr->version = get_unaligned_le16(&data[6]); - hdr->length = get_unaligned_le32(&data[8]); - return 12; + hdr->size = get_unaligned_le32(data); + hdr->version_offset = get_unaligned_le32(&data[4]); + hdr->plt_id = get_unaligned_le32(&data[8]); + hdr->ppc3_ver = get_unaligned_le32(&data[12]); + memcpy(hdr->ddc_name, &data[16], 64); + hdr->timestamp = get_unaligned_le32(&data[80]); + + return 84; +} + +static s32 tas_fw_get_next_file(const u8 *data, struct tas_fw_file *file) +{ + file->vendor_id = get_unaligned_le32(&data[0]); + file->file_id = get_unaligned_le32(&data[4]); + file->version = get_unaligned_le32(&data[8]); + file->length = get_unaligned_le32(&data[12]); + file->dest_addr = get_unaligned_le32(&data[16]); + file->fw_data = (u8 *)&data[20]; + + return file->length + sizeof(u32) * 5; } static void tas2783_fw_ready(const struct firmware *fmw, void *context) @@ -749,13 +775,20 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) struct tas2783_prv *tas_dev = (struct tas2783_prv *)context; const u8 *buf = NULL; - s32 offset = 0, img_sz, file_blk_size, ret; - struct bin_header_t hdr; + s32 img_sz, ret = 0, cur_file = 0; + s32 offset = 0; + + struct tas_fw_hdr *hdr __free(kfree) = kzalloc(sizeof(*hdr), GFP_KERNEL); + struct tas_fw_file *file __free(kfree) = kzalloc(sizeof(*file), GFP_KERNEL); + if (!file || !hdr) { + ret = -ENOMEM; + goto out; + } if (!fmw || !fmw->data) { - /* No firmware binary, devices will work in ROM mode. */ + /* firmware binary not found*/ dev_err(tas_dev->dev, - "Failed to read %s, no side-effect on driver running\n", + "Failed to read fw binary %s\n", tas_dev->rca_binaryname); ret = -EINVAL; goto out; @@ -763,67 +796,47 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) img_sz = fmw->size; buf = fmw->data; - offset += FW_DL_OFFSET; - if (offset >= (img_sz - FW_FL_HDR)) { - dev_err(tas_dev->dev, - "firmware is too small"); + offset += tas_fw_read_hdr(buf, hdr); + if (hdr->size != img_sz) { ret = -EINVAL; + dev_err(tas_dev->dev, "firmware size mismatch with header"); + goto out; + } + + if (img_sz < FW_DL_OFFSET) { + ret = -EINVAL; + dev_err(tas_dev->dev, "unexpected size, size is too small"); goto out; } mutex_lock(&tas_dev->pde_lock); while (offset < (img_sz - FW_FL_HDR)) { - memset(&hdr, 0, sizeof(hdr)); - offset += read_header(&buf[offset], &hdr); + offset += tas_fw_get_next_file(&buf[offset], file); dev_dbg(tas_dev->dev, - "vndr=%d, file=%d, version=%d, len=%d, off=%d\n", - hdr.vendor_id, hdr.file_id, hdr.version, - hdr.length, offset); - /* size also includes the header */ - file_blk_size = hdr.length - FW_FL_HDR; + "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p", + file->vendor_id, file->file_id, + file->version, file->length, + file->dest_addr, file->fw_data); - /* make sure that enough data is there */ - if (offset + file_blk_size > img_sz) { - ret = -EINVAL; + ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, + file->dest_addr, + file->length, + file->fw_data); + if (ret < 0) { dev_err(tas_dev->dev, - "corrupt firmware file"); + "FW download failed: %d", ret); break; } - - switch (hdr.file_id) { - case 0: - ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, - PRAM_ADDR_START, file_blk_size, - &buf[offset]); - if (ret < 0) - dev_err(tas_dev->dev, - "PRAM update failed: %d", ret); - break; - - case 1: - ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, - YRAM_ADDR_START, file_blk_size, - &buf[offset]); - if (ret < 0) - dev_err(tas_dev->dev, - "YRAM update failed: %d", ret); - - break; - - default: - ret = -EINVAL; - dev_err(tas_dev->dev, "Unsupported file"); - break; - } - - if (ret == 0) - offset += file_blk_size; - else - break; + cur_file++; } mutex_unlock(&tas_dev->pde_lock); - if (!ret) + + if (cur_file == 0) { + dev_err(tas_dev->dev, "fw with no files"); + ret = -EINVAL; + } else { tas2783_update_calibdata(tas_dev); + } out: if (!ret) @@ -1211,6 +1224,14 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) tas_dev->fw_dl_task_done = false; tas_dev->fw_dl_success = false; + + ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); + if (ret) { + dev_err(dev, "sw reset failed, err=%d", ret); + return ret; + } + usleep_range(2000, 2200); + scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname), "tas2783-%01x.bin", unique_id); diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h index 794333e0a350..bf34319c9a9f 100644 --- a/sound/soc/codecs/tas2783.h +++ b/sound/soc/codecs/tas2783.h @@ -28,6 +28,7 @@ #define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \ 0x800000 + ((page) * 128) + (reg)) +#define TAS2783_SW_RESET TASDEV_REG_SDW(0x0, 0x00, 0x01) /* Volume control */ #define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A) #define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03) From f8f1f0d8f0255d1e7c758dba9e3deb5a58a51aa1 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:14 +0530 Subject: [PATCH 082/341] ASoC: tas2783A: update default init writes Remove unwanted initialistaion writes to the device which will now be part of the either firmware or acpi table. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-3-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 54 +--------------------------------- 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 6a0644670a0b..bfa925dd601e 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -297,7 +297,7 @@ static const struct reg_default tas2783_reg_default[] = { }; static const struct reg_sequence tas2783_init_seq[] = { - REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04), + REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x01), REG_SEQ0(0x00800418, 0x00), REG_SEQ0(0x00800419, 0x00), REG_SEQ0(0x0080041a, 0x00), @@ -307,60 +307,19 @@ static const struct reg_sequence tas2783_init_seq[] = { REG_SEQ0(0x0080042a, 0x00), REG_SEQ0(0x0080042b, 0x00), REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00), - REG_SEQ0(0x0080005c, 0xD9), - REG_SEQ0(0x00800082, 0x20), - REG_SEQ0(0x008000a1, 0x00), - REG_SEQ0(0x00800097, 0xc8), - REG_SEQ0(0x00800099, 0x20), - REG_SEQ0(0x008000c7, 0xaa), - REG_SEQ0(0x008000b5, 0x74), - REG_SEQ0(0x00800082, 0x20), - REG_SEQ0(0x00807e8d, 0x0d), - REG_SEQ0(0x00807eb9, 0x53), - REG_SEQ0(0x00807ebe, 0x42), - REG_SEQ0(0x00807ec5, 0x37), - REG_SEQ0(0x00800066, 0x92), - REG_SEQ0(0x00800003, 0x28), REG_SEQ0(0x00800004, 0x21), REG_SEQ0(0x00800005, 0x41), REG_SEQ0(0x00800006, 0x00), REG_SEQ0(0x00800007, 0x20), - REG_SEQ0(0x0080000c, 0x10), - REG_SEQ0(0x00800013, 0x08), REG_SEQ0(0x00800015, 0x00), - REG_SEQ0(0x00800017, 0x80), - REG_SEQ0(0x0080001a, 0x00), - REG_SEQ0(0x0080001b, 0x22), - REG_SEQ0(0x0080001c, 0x36), - REG_SEQ0(0x0080001d, 0x01), - REG_SEQ0(0x0080001f, 0x00), - REG_SEQ0(0x00800020, 0x2e), - REG_SEQ0(0x00800034, 0x06), - REG_SEQ0(0x00800035, 0xb9), REG_SEQ0(0x00800036, 0xad), REG_SEQ0(0x00800037, 0xa8), - REG_SEQ0(0x00800038, 0x00), - REG_SEQ0(0x0080003b, 0xfc), - REG_SEQ0(0x0080003d, 0xdd), - REG_SEQ0(0x00800040, 0xf6), - REG_SEQ0(0x00800041, 0x14), - REG_SEQ0(0x0080005c, 0x19), - REG_SEQ0(0x0080005d, 0x80), - REG_SEQ0(0x00800063, 0x48), - REG_SEQ0(0x00800065, 0x08), - REG_SEQ0(0x00800067, 0x00), - REG_SEQ0(0x0080006a, 0x12), REG_SEQ0(0x0080006b, 0x7b), REG_SEQ0(0x0080006c, 0x00), REG_SEQ0(0x0080006d, 0x00), REG_SEQ0(0x0080006e, 0x1a), REG_SEQ0(0x0080006f, 0x00), - REG_SEQ0(0x00800070, 0x96), REG_SEQ0(0x00800071, 0x02), - REG_SEQ0(0x00800073, 0x08), - REG_SEQ0(0x00800075, 0xe0), - REG_SEQ0(0x0080007a, 0x60), - REG_SEQ0(0x008000bd, 0x00), REG_SEQ0(0x008000be, 0x00), REG_SEQ0(0x008000bf, 0x00), REG_SEQ0(0x008000c0, 0x00), @@ -368,17 +327,6 @@ static const struct reg_sequence tas2783_init_seq[] = { REG_SEQ0(0x008000c2, 0x00), REG_SEQ0(0x008000c3, 0x00), REG_SEQ0(0x008000c4, 0x00), - REG_SEQ0(0x008000c5, 0x00), - REG_SEQ0(0x00800008, 0x49), - REG_SEQ0(0x00800009, 0x02), - REG_SEQ0(0x0080000a, 0x1a), - REG_SEQ0(0x0080000d, 0x93), - REG_SEQ0(0x0080000e, 0x82), - REG_SEQ0(0x0080000f, 0x42), - REG_SEQ0(0x00800010, 0x84), - REG_SEQ0(0x00800014, 0x0a), - REG_SEQ0(0x00800016, 0x00), - REG_SEQ0(0x00800060, 0x21), }; static int tas2783_sdca_mbq_size(struct device *dev, u32 reg) From ce65a90222e94eec1e5b7d0224b4d647af644cdc Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:15 +0530 Subject: [PATCH 083/341] ASoC: tas2783A: fix error log for calibration data Currently when the calibration is not found, it is wrongly logged as device is not found. Fix this error message to indicate that calibration data is not valid instead. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-4-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index bfa925dd601e..397442cd67ff 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -635,7 +635,8 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32 } if (device_num == dev_count) - dev_err(tas_dev->dev, "device not found\n"); + dev_err(tas_dev->dev, + "unique id not found in the calib data\n"); else dev_dbg(tas_dev->dev, "calib data update done\n"); } From 2bc4b4f77cb70df3ef05b80d0cb19edba17f04a6 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:16 +0530 Subject: [PATCH 084/341] ASoc: tas2783A: fw name based on system details The firmware file for tas2783A contains the device and algorithm settings. So the firmware files are unique for a system and driver should have the ability to distinctly identify and pick the right firmware. This commit adds the method to uniquely identify the firmware for a system based on the below format. --.bin * Subsystem is the PCI device subsystem-id * Link is the SoundWire link id on which the device recides. * Unique is the SoundWire slave unique id in the system. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-5-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 397442cd67ff..adfbccedbf98 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1162,8 +1163,21 @@ static const struct dev_pm_ops tas2783_sdca_pm = { RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL) }; +static struct pci_dev *tas_get_pci_dev(struct sdw_slave *peripheral) +{ + struct device *dev = &peripheral->dev; + + for (; dev; dev = dev->parent) + if (dev->bus == &pci_bus_type) + return to_pci_dev(dev); + + return NULL; +} + static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) { + struct pci_dev *pci; + struct sdw_bus *bus; struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; @@ -1171,6 +1185,13 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) if (tas_dev->hw_init) return 0; + pci = tas_get_pci_dev(slave); + if (!pci) { + dev_err(dev, "pci device id can't be read"); + return -EINVAL; + } + + bus = slave->bus; tas_dev->fw_dl_task_done = false; tas_dev->fw_dl_success = false; @@ -1181,8 +1202,10 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) } usleep_range(2000, 2200); + /* subsystem_id-link_id-unique_id */ scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname), - "tas2783-%01x.bin", unique_id); + "%04X-%1X-%1X.bin", pci->subsystem_device, bus->link_id, + unique_id); ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, From a6b5629e131c76c4ab8f2036f09a05f976f7eb73 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:17 +0530 Subject: [PATCH 085/341] ASoc: tas2783A: acpi match for 4 channel for mtl Add changes to support 4 tas2783A devices on mtl platform. The supported unique IDs are updated to 9, a, c, d, where c and d are configured to play left channels and 9 and a are configured to play right channel. Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20251215153219.810-6-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-mtl-match.c | 14 +++++++++++++- 1 file changed, 13 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 ec9fd8486c05..f12d42986a75 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -950,7 +950,7 @@ static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = { static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = { { - .adr = 0x0000380102000001ull, + .adr = 0x00003c0102000001ull, .num_endpoints = 1, .endpoints = &spk_l_endpoint, .name_prefix = "tas2783-1" @@ -960,6 +960,18 @@ static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = { .num_endpoints = 1, .endpoints = &spk_r_endpoint, .name_prefix = "tas2783-2" + }, + { + .adr = 0x00003d0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-3" + }, + { + .adr = 0x00003a0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-4" } }; From ca8f3611dcf85489cae315f4844cccf858bbe9b3 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:18 +0530 Subject: [PATCH 086/341] ASoC: tas2783A: use acpi initialisation table This patch adds support for parsing the initilisation data from ACPI table. This table is required to configure each device correctly so that correct channel's data is selected during playback. Signed-off-by: Niranjan H Y Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251215153219.810-7-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/tas2783-sdw.c | 54 ++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061791e61907..93a266a12a3b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2146,6 +2146,7 @@ config SND_SOC_TAS2781_I2C config SND_SOC_TAS2783_SDW tristate "Texas Instruments TAS2783 speaker amplifier (sdw)" depends on SOUNDWIRE + depends on SND_SOC_SDCA depends on EFI select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index adfbccedbf98..679fb5cb194e 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "tas2783.h" @@ -78,6 +80,7 @@ struct tas2783_prv { struct snd_soc_component *component; struct calibration_data cali_data; struct sdw_slave *sdw_peripheral; + struct sdca_function_data *sa_func_data; enum sdw_slave_status status; /* calibration */ struct mutex calib_lock; @@ -1223,9 +1226,18 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) dev_err(tas_dev->dev, "fw request, wait_event timeout\n"); ret = -EAGAIN; } else { - ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq, - ARRAY_SIZE(tas2783_init_seq)); - tas_dev->hw_init = true; + if (tas_dev->sa_func_data) + ret = sdca_regmap_write_init(dev, tas_dev->regmap, + tas_dev->sa_func_data); + else + ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq, + ARRAY_SIZE(tas2783_init_seq)); + + if (ret) + dev_err(tas_dev->dev, + "init writes failed, err=%d", ret); + else + tas_dev->hw_init = true; } return ret; @@ -1275,12 +1287,47 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, struct regmap *regmap; struct device *dev = &peripheral->dev; struct tas2783_prv *tas_dev; + struct sdca_function_data *function_data = NULL; + int ret, i; tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL); if (!tas_dev) return dev_err_probe(dev, -ENOMEM, "Failed devm_kzalloc"); + i = -1; + /* check if we have any SDCA function data available */ + if (peripheral->sdca_data.num_functions > 0) { + dev_dbg(dev, "SDCA functions found: %d", peripheral->sdca_data.num_functions); + + /* Look for Smart Amp function type */ + for (i = 0; i < peripheral->sdca_data.num_functions; i++) { + if (peripheral->sdca_data.function[i].type == + SDCA_FUNCTION_TYPE_SMART_AMP) { + dev_info(dev, "Found Smart Amp function at index %d", i); + break; + } + } + } + + if (i >= 0 && i < peripheral->sdca_data.num_functions) { + /* Allocate memory for function data */ + function_data = devm_kzalloc(dev, sizeof(*function_data), + GFP_KERNEL); + if (!function_data) + return dev_err_probe(dev, -ENOMEM, + "failed to parse sdca functions"); + + /* Parse the function */ + ret = sdca_parse_function(dev, peripheral, + &peripheral->sdca_data.function[i], + function_data); + if (!ret) + tas_dev->sa_func_data = function_data; + else + dev_warn(dev, "smartamp function parse failed:err%d, using defaults", ret); + } + tas_dev->dev = dev; tas_dev->sdw_peripheral = peripheral; tas_dev->hw_init = false; @@ -1335,6 +1382,7 @@ static struct sdw_driver tas_sdw_driver = { }; module_sdw_driver(tas_sdw_driver); +MODULE_IMPORT_NS("SND_SOC_SDCA"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver"); MODULE_LICENSE("GPL"); From 961f20faa4b950c449dc98fa95a056ef368a24fc Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Mon, 15 Dec 2025 21:02:19 +0530 Subject: [PATCH 087/341] ASoC: tas2783A: read slave properties from acpi table Currently device is using hardcoded slave properties using the .read_prop callback from "struct sdw_slave_ops". This patch removes this and uses the sdw_slave_read_prop API to read the data directly from the ACPI table. Signed-off-by: Niranjan H Y Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251215153219.810-8-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 66 +++------------------------------- 1 file changed, 5 insertions(+), 61 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 679fb5cb194e..af812f95a4ba 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1060,66 +1060,6 @@ static s32 tas_init(struct tas2783_prv *tas_dev) return ret; } -static s32 tas_read_prop(struct sdw_slave *slave) -{ - struct sdw_slave_prop *prop = &slave->prop; - s32 nval; - s32 i, j; - u32 bit; - unsigned long addr; - struct sdw_dpn_prop *dpn; - - prop->scp_int1_mask = - SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; - - prop->paging_support = true; - - /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x04; /* BITMAP: 00000100 */ - prop->sink_ports = 0x2; /* BITMAP: 00000010 */ - - nval = hweight32(prop->source_ports); - prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), GFP_KERNEL); - if (!prop->src_dpn_prop) - return -ENOMEM; - - i = 0; - dpn = prop->src_dpn_prop; - addr = prop->source_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].type = SDW_DPN_FULL; - dpn[i].simple_ch_prep_sm = false; - dpn[i].ch_prep_timeout = 10; - i++; - } - - /* do this again for sink now */ - nval = hweight32(prop->sink_ports); - prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), GFP_KERNEL); - if (!prop->sink_dpn_prop) - return -ENOMEM; - - j = 0; - dpn = prop->sink_dpn_prop; - addr = prop->sink_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[j].num = bit; - dpn[j].type = SDW_DPN_FULL; - dpn[j].simple_ch_prep_sm = false; - dpn[j].ch_prep_timeout = 10; - j++; - } - - /* set the timeout values */ - prop->clk_stop_timeout = 200; - - return 0; -} - static s32 tas2783_sdca_dev_suspend(struct device *dev) { struct tas2783_prv *tas_dev = dev_get_drvdata(dev); @@ -1272,7 +1212,6 @@ static s32 tas_update_status(struct sdw_slave *slave, } static const struct sdw_slave_ops tas_sdw_ops = { - .read_prop = tas_read_prop, .update_status = tas_update_status, }; @@ -1290,6 +1229,11 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, struct sdca_function_data *function_data = NULL; int ret, i; + ret = sdw_slave_read_prop(peripheral); + if (ret) + return dev_err_probe(dev, ret, + "slave property read failed"); + tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL); if (!tas_dev) return dev_err_probe(dev, -ENOMEM, From 3622dc47a4b13e0ec86358c7b54a0b33bfcaa03c Mon Sep 17 00:00:00 2001 From: HariKrishna Sagala Date: Wed, 17 Dec 2025 11:14:59 +0530 Subject: [PATCH 088/341] ASoC: codec: rt286: Use devm_request_threaded_irq to manage IRQ lifetime and fix smatch warning Replace manual "request_threaded_irq()" with the device managed "devm_request_threaded_irq" to manage the IRQ lifetime and also it removes the smatch reported warning. Remove the manual "free_irq()" in the "remove" function as free_irq is tied to device teardown. Signed-off-by: HariKrishna Sagala Link: https://patch.msgid.link/20251217054458.38257-2-hariconscious@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt286.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 2fbb5860c421..195658f626cc 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1236,7 +1236,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c) } if (rt286->i2c->irq) { - ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq, + ret = devm_request_threaded_irq(&rt286->i2c->dev, rt286->i2c->irq, NULL, rt286_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); if (ret != 0) { dev_err(&i2c->dev, @@ -1252,22 +1252,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c) return ret; } -static void rt286_i2c_remove(struct i2c_client *i2c) -{ - struct rt286_priv *rt286 = i2c_get_clientdata(i2c); - - if (i2c->irq) - free_irq(i2c->irq, rt286); -} - - static struct i2c_driver rt286_i2c_driver = { .driver = { .name = "rt286", .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, .probe = rt286_i2c_probe, - .remove = rt286_i2c_remove, .id_table = rt286_i2c_id, }; From 7a8447fc71a09000cee5a2372b6efde45735d2c8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Dec 2025 10:22:59 +0100 Subject: [PATCH 089/341] ASoC: codecs: wcd939x-sdw: use devres for regmap allocation Components are bound inside a new devres group so that any resources allocated can be released on bind failure and on unbind without affecting anything else. Switch to using device managed regmap allocation for consistency while dropping the misleading comment claiming that devres cannot be used. Cc: Neil Armstrong Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251201092259.11761-1-johan@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x-sdw.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index da342a0c95a5..399dfba79aa2 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -1384,12 +1384,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) } if (wcd->is_tx) { - /* - * Do not use devres here since devres_release_group() could - * be called by component_unbind() id the aggregate device - * fails to bind. - */ - wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config); + wcd->regmap = devm_regmap_init_sdw(pdev, &wcd939x_regmap_config); if (IS_ERR(wcd->regmap)) return dev_err_probe(dev, PTR_ERR(wcd->regmap), "Regmap init failed\n"); @@ -1400,30 +1395,20 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) ret = component_add(dev, &wcd_sdw_component_ops); if (ret) - goto err_free_regmap; + return ret; /* Set suspended until aggregate device is bind */ pm_runtime_set_suspended(dev); return 0; - -err_free_regmap: - if (wcd->regmap) - regmap_exit(wcd->regmap); - - return ret; } static int wcd9390_remove(struct sdw_slave *pdev) { struct device *dev = &pdev->dev; - struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); component_del(dev, &wcd_sdw_component_ops); - if (wcd->regmap) - regmap_exit(wcd->regmap); - return 0; } From 86af3c229245fe1e59f428fc6abe19127ce15f5f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 30 Nov 2025 10:40:23 +0100 Subject: [PATCH 090/341] ASoC: qcom: Constify APR callback response data APR bus driver calls each APR client callback with pointer to the APR response packet. The callbacks are not suppose to modify that response packet, so make it a pointer to const to document that expectation explicitly. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251130-asoc-apr-const-v1-1-d0833f3ed423@oss.qualcomm.com Signed-off-by: Mark Brown --- include/linux/soc/qcom/apr.h | 2 +- sound/soc/qcom/qdsp6/q6adm.c | 4 ++-- sound/soc/qcom/qdsp6/q6afe.c | 4 ++-- sound/soc/qcom/qdsp6/q6asm.c | 8 ++++---- sound/soc/qcom/qdsp6/q6core.c | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index a532d1e4b1f4..35f44cd868cb 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -155,7 +155,7 @@ struct apr_driver { int (*probe)(struct apr_device *sl); void (*remove)(struct apr_device *sl); int (*callback)(struct apr_device *a, - struct apr_resp_pkt *d); + const struct apr_resp_pkt *d); int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op); struct device_driver driver; const struct apr_device_id *id_table; diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 0b8d06ec8b26..608ca0e41539 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -186,11 +186,11 @@ static void q6adm_free_copp(struct kref *ref) kfree(c); } -static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data) +static int q6adm_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { struct aprv2_ibasic_rsp_result_t *result = data->payload; int port_idx, copp_idx; - struct apr_hdr *hdr = &data->hdr; + const struct apr_hdr *hdr = &data->hdr; struct q6copp *copp; struct q6adm *adm = dev_get_drvdata(&adev->dev); diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 0b01fc9e13a7..a9f8b7d68a96 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -958,11 +958,11 @@ static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token) return ret; } -static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) +static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { struct q6afe *afe = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *res; - struct apr_hdr *hdr = &data->hdr; + const struct apr_hdr *hdr = &data->hdr; struct q6afe_port *port; if (!data->payload_size) diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index e7295b7b2461..df183b7a4019 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -599,12 +599,12 @@ int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir) EXPORT_SYMBOL_GPL(q6asm_get_hw_pointer); static int32_t q6asm_stream_callback(struct apr_device *adev, - struct apr_resp_pkt *data, + const struct apr_resp_pkt *data, int session_id) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *result; - struct apr_hdr *hdr = &data->hdr; + const struct apr_hdr *hdr = &data->hdr; struct audio_port_data *port; struct audio_client *ac; uint32_t client_event = 0; @@ -744,13 +744,13 @@ done: } static int q6asm_srvc_callback(struct apr_device *adev, - struct apr_resp_pkt *data) + const struct apr_resp_pkt *data) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *result; struct audio_port_data *port; struct audio_client *ac = NULL; - struct apr_hdr *hdr = &data->hdr; + const struct apr_hdr *hdr = &data->hdr; struct q6asm *a; uint32_t sid = 0; uint32_t dir = 0; diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c index 49cfb32cd209..51398199bff3 100644 --- a/sound/soc/qcom/qdsp6/q6core.c +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -67,11 +67,11 @@ struct q6core { static struct q6core *g_core; -static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data) +static int q6core_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { struct q6core *core = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *result; - struct apr_hdr *hdr = &data->hdr; + const struct apr_hdr *hdr = &data->hdr; result = data->payload; switch (hdr->opcode) { From c66cea195d76c7c396c4c565b967d3e2a709e762 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 30 Nov 2025 10:40:24 +0100 Subject: [PATCH 091/341] soc: qcom: apr: Use typedef for GPR callback member There is already a typedef for GPR callback used in 'struct pkt_router_svc', so use it also in 'struct apr_driver', because it is the same type - one is assigned to another in apr_device_probe(). Signed-off-by: Krzysztof Kozlowski Acked-by: Bjorn Andersson Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251130-asoc-apr-const-v1-2-d0833f3ed423@oss.qualcomm.com Signed-off-by: Mark Brown --- include/linux/soc/qcom/apr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 35f44cd868cb..b16530f319ad 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -156,7 +156,7 @@ struct apr_driver { void (*remove)(struct apr_device *sl); int (*callback)(struct apr_device *a, const struct apr_resp_pkt *d); - int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op); + gpr_port_cb gpr_callback; struct device_driver driver; const struct apr_device_id *id_table; }; From f3a86870c5938fe82ce02c29235326d417010ffb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 30 Nov 2025 10:40:25 +0100 Subject: [PATCH 092/341] ASoC: qcom: Constify GPR callback response data GPR bus driver calls each GPR client callback with pointer to the GPR response packet. The callbacks are not suppose to modify that response packet, so make it a pointer to const to document that expectation explicitly. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251130-asoc-apr-const-v1-3-d0833f3ed423@oss.qualcomm.com Signed-off-by: Mark Brown --- include/linux/soc/qcom/apr.h | 2 +- sound/soc/qcom/qdsp6/q6apm.c | 8 ++++---- sound/soc/qcom/qdsp6/q6prm.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index b16530f319ad..6e1b1202e818 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -122,7 +122,7 @@ struct gpr_ibasic_rsp_accepted_t { #define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF) #define APR_SVC_MINOR_VERSION(v) (v & 0xFF) -typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op); +typedef int (*gpr_port_cb) (const struct gpr_resp_pkt *d, void *priv, int op); struct packet_router; struct pkt_router_svc { struct device *dev; diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 94cc6376a367..cec135c53b99 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -487,14 +487,14 @@ int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir) } EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer); -static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) +static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done; struct apm_cmd_rsp_shared_mem_map_regions *rsp; struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; - struct gpr_hdr *hdr = &data->hdr; + const struct gpr_hdr *hdr = &data->hdr; struct device *dev = graph->dev; uint32_t client_event; phys_addr_t phys; @@ -761,13 +761,13 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui } -static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op) +static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; struct q6apm *apm = dev_get_drvdata(&gdev->dev); struct device *dev = &gdev->dev; struct gpr_ibasic_rsp_result_t *result; - struct gpr_hdr *hdr = &data->hdr; + const struct gpr_hdr *hdr = &data->hdr; result = data->payload; diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 0b8fad0bc832..eaec6d211cf8 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -175,12 +175,12 @@ int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_ } EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock); -static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op) +static int prm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; struct q6prm *prm = dev_get_drvdata(&gdev->dev); struct gpr_ibasic_rsp_result_t *result; - struct gpr_hdr *hdr = &data->hdr; + const struct gpr_hdr *hdr = &data->hdr; switch (hdr->opcode) { case PRM_CMD_RSP_REQUEST_HW_RSC: From a2a631830deb382a3d27b6f52b2d654a3e6bb427 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 30 Nov 2025 10:40:26 +0100 Subject: [PATCH 093/341] ASoC: qcom: Constify APR/GPR result structs APR and GPR callbacks receive pointer to const response packet which holds the response result. That result should not be modified by callback, so make it pointer to const for code safety. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251130-asoc-apr-const-v1-4-d0833f3ed423@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6adm.c | 2 +- sound/soc/qcom/qdsp6/q6afe.c | 2 +- sound/soc/qcom/qdsp6/q6apm.c | 2 +- sound/soc/qcom/qdsp6/q6asm.c | 2 +- sound/soc/qcom/qdsp6/q6core.c | 2 +- sound/soc/qcom/qdsp6/q6prm.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 608ca0e41539..c415b3003030 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -188,7 +188,7 @@ static void q6adm_free_copp(struct kref *ref) static int q6adm_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { - struct aprv2_ibasic_rsp_result_t *result = data->payload; + const struct aprv2_ibasic_rsp_result_t *result = data->payload; int port_idx, copp_idx; const struct apr_hdr *hdr = &data->hdr; struct q6copp *copp; diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index a9f8b7d68a96..e8e3e3bbacd9 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -961,7 +961,7 @@ static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token) static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { struct q6afe *afe = dev_get_drvdata(&adev->dev); - struct aprv2_ibasic_rsp_result_t *res; + const struct aprv2_ibasic_rsp_result_t *res; const struct apr_hdr *hdr = &data->hdr; struct q6afe_port *port; diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index cec135c53b99..3b504e2a9173 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -492,7 +492,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done; struct apm_cmd_rsp_shared_mem_map_regions *rsp; - struct gpr_ibasic_rsp_result_t *result; + const struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; const struct gpr_hdr *hdr = &data->hdr; struct device *dev = graph->dev; diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index df183b7a4019..6f1667895262 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -603,7 +603,7 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, int session_id) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); - struct aprv2_ibasic_rsp_result_t *result; + const struct aprv2_ibasic_rsp_result_t *result; const struct apr_hdr *hdr = &data->hdr; struct audio_port_data *port; struct audio_client *ac; diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c index 51398199bff3..f4939302b88a 100644 --- a/sound/soc/qcom/qdsp6/q6core.c +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -70,7 +70,7 @@ static struct q6core *g_core; static int q6core_callback(struct apr_device *adev, const struct apr_resp_pkt *data) { struct q6core *core = dev_get_drvdata(&adev->dev); - struct aprv2_ibasic_rsp_result_t *result; + const struct aprv2_ibasic_rsp_result_t *result; const struct apr_hdr *hdr = &data->hdr; result = data->payload; diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index eaec6d211cf8..6d9834b5d16b 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -179,7 +179,7 @@ static int prm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; struct q6prm *prm = dev_get_drvdata(&gdev->dev); - struct gpr_ibasic_rsp_result_t *result; + const struct gpr_ibasic_rsp_result_t *result; const struct gpr_hdr *hdr = &data->hdr; switch (hdr->opcode) { From cf077db587a9fa1839c9a2c7e44694d09edc8334 Mon Sep 17 00:00:00 2001 From: Leo Tsai Date: Thu, 18 Dec 2025 13:51:36 +0800 Subject: [PATCH 094/341] ALSA: hda/cm9825: Add GENE_TWL7 support for AAEON The GENE_TWL7 project is an AAEON platform with a fixed audio configuration consisting of line-out, line-in, 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/20251218055136.3875-1-antivirus621@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/cm9825.c | 290 +++++++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 7 deletions(-) diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c index 5c474ce44348..52ee431f2e2c 100644 --- a/sound/hda/codecs/cm9825.c +++ b/sound/hda/codecs/cm9825.c @@ -13,6 +13,11 @@ #include "hda_jack.h" #include "generic.h" +enum { + QUIRK_CM_STD = 0x0, + QUIRK_GENE_TWL7_SSID = 0x160dc000 +}; + /* CM9825 Offset Definitions */ #define CM9825_VERB_SET_HPF_1 0x781 @@ -25,6 +30,7 @@ #define CM9825_VERB_SET_VNEG 0x7a8 #define CM9825_VERB_SET_D2S 0x7a9 #define CM9825_VERB_SET_DACTRL 0x7aa +#define CM9825_VERB_SET_P3BCP 0x7ab #define CM9825_VERB_SET_PDNEG 0x7ac #define CM9825_VERB_SET_VDO 0x7ad #define CM9825_VERB_SET_CDALR 0x7b0 @@ -42,7 +48,12 @@ struct cmi_spec { const struct hda_verb *chip_hp_present_verbs; const struct hda_verb *chip_hp_remove_verbs; struct hda_codec *codec; + struct delayed_work unsol_inputs_work; + struct delayed_work unsol_lineout_work; struct delayed_work unsol_hp_work; + hda_nid_t jd_cap_hp; + hda_nid_t jd_cap_lineout; + hda_nid_t jd_cap_inputs[AUTO_CFG_MAX_INS]; int quirk; }; @@ -111,6 +122,121 @@ static const struct hda_verb cm9825_hp_remove_verbs[] = { {} }; +/* + * To save power, AD/CLK is turned off. + */ +static const struct hda_verb cm9825_gene_twl7_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_MBIAS, 0x00}, + {0x43, CM9825_VERB_SET_VNEG, 0x50}, + {0x43, CM9825_VERB_SET_PDNEG, 0x04}, + {0x43, CM9825_VERB_SET_CDALR, 0xf6}, + {0x43, CM9825_VERB_SET_OTP, 0xcd}, + {} +}; + +/* + * These settings are required to properly enable the PLL, clock, ADC and + * DAC paths, and to select the correct analog input routing. Without + * these explicit configurations, the ADC does not start correctly and + * recording does not work reliably on this hardware. + * + * D0 configuration: enable PLL/CLK/ADC/DAC and optimize performance + */ +static const struct hda_verb cm9825_gene_twl7_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, 0xcf}, + {0x43, CM9825_VERB_SET_DACL, 0xaa}, + {0x43, CM9825_VERB_SET_MBIAS, 0x1c}, + {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}, + {0x43, CM9825_VERB_SET_HPF_1, 0x40}, + {0x43, CM9825_VERB_SET_HPF_2, 0x40}, + {0x40, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x3d, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x46, CM9825_VERB_SET_P3BCP, 0x20}, + {} +}; + +/* + * Enable DAC to start playback. + */ +static const struct hda_verb cm9825_gene_twl7_playback_start_verbs[] = { + {0x43, CM9825_VERB_SET_D2S, 0xf2}, + {0x43, CM9825_VERB_SET_VDO, 0xd4}, + {0x43, CM9825_VERB_SET_SNR, 0x30}, + {} +}; + +/* + * Disable DAC and enable de-pop noise mechanism. + */ +static const struct hda_verb cm9825_gene_twl7_playback_stop_verbs[] = { + {0x43, CM9825_VERB_SET_VDO, 0xc0}, + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_VDO, 0xd0}, + {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; + bool jack_plugin; + struct hda_jack_tbl *jack; + + jack_plugin = snd_hda_jack_detect(spec->codec, nid); + jack = snd_hda_jack_tbl_get(spec->codec, nid); + if (jack) { + jack->block_report = 0; + snd_hda_jack_report_sync(spec->codec); + } + + codec_dbg(spec->codec, + "%s, jack_plugin %d, nid 0x%X, line%d\n", + __func__, (int)jack_plugin, nid, __LINE__); +} + +static void cm9825_unsol_inputs_delayed(struct work_struct *work) +{ + struct cmi_spec *spec = + container_of(to_delayed_work(work), struct cmi_spec, + unsol_inputs_work); + int i; + + for (i = 0; i < spec->gen.autocfg.num_inputs; i++) { + if (!spec->jd_cap_inputs[i]) + continue; + + cm9825_update_jk_plug_status(spec->codec, + spec->gen.autocfg.inputs[i].pin); + } +} + +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); + + cm9825_update_jk_plug_status(spec->codec, + spec->gen.autocfg.line_out_pins[0]); +} + static void cm9825_unsol_hp_delayed(struct work_struct *work) { struct cmi_spec *spec = @@ -159,16 +285,93 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) tbl = snd_hda_jack_tbl_get(codec, cb->nid); if (tbl) tbl->block_report = 1; - schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200)); + + if (cb->nid == spec->jd_cap_hp) + schedule_delayed_work(&spec->unsol_hp_work, + msecs_to_jiffies(200)); + else if (cb->nid == spec->jd_cap_lineout) + schedule_delayed_work(&spec->unsol_lineout_work, + msecs_to_jiffies(200)); + + for (int i = 0; i < spec->gen.autocfg.num_inputs; i++) { + if (cb->nid == spec->jd_cap_inputs[i]) + schedule_delayed_work(&spec->unsol_inputs_work, + msecs_to_jiffies(200)); + } } static void cm9825_setup_unsol(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; + int i; hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback); + hda_nid_t lineout_pin = spec->gen.autocfg.line_out_pins[0]; + + if (hp_pin != 0) { + if (is_jack_detectable(codec, hp_pin)) { + spec->jd_cap_hp = hp_pin; + snd_hda_jack_detect_enable_callback(codec, hp_pin, + hp_callback); + } else + spec->jd_cap_hp = 0; + } else + spec->jd_cap_hp = 0; + + if (lineout_pin != 0) { + if (is_jack_detectable(codec, lineout_pin)) { + spec->jd_cap_lineout = lineout_pin; + snd_hda_jack_detect_enable_callback(codec, lineout_pin, + hp_callback); + } else + spec->jd_cap_lineout = 0; + } else + spec->jd_cap_lineout = 0; + + codec_dbg(codec, + "%s, jd_cap_hp 0x%02X, jd_cap_lineout 0x%02X, line%d\n", + __func__, spec->jd_cap_hp, spec->jd_cap_lineout, __LINE__); + + for (i = 0; i < spec->gen.autocfg.num_inputs; i++) { + if (spec->gen.autocfg.inputs[i].pin != 0) { + if (is_jack_detectable + (codec, spec->gen.autocfg.inputs[i].pin)) { + spec->jd_cap_inputs[i] = + spec->gen.autocfg.inputs[i].pin; + snd_hda_jack_detect_enable_callback(codec, + spec->gen.autocfg.inputs[i].pin, + hp_callback); + } else + spec->jd_cap_inputs[i] = 0; + } else + spec->jd_cap_inputs[i] = 0; + + codec_dbg(codec, + "%s, input jd_cap_inputs[%d] 0x%02X, line%d\n", + __func__, i, spec->jd_cap_inputs[i], __LINE__); + } +} + +static void cm9825_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct cmi_spec *spec = codec->spec; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + snd_hda_sequence_write(spec->codec, + cm9825_gene_twl7_playback_start_verbs); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + snd_hda_sequence_write(spec->codec, + cm9825_gene_twl7_playback_stop_verbs); + break; + default: + return; + } } static int cm9825_init(struct hda_codec *codec) @@ -182,23 +385,48 @@ static int cm9825_init(struct hda_codec *codec) static void cm9825_remove(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; + int i; + + if (spec->jd_cap_hp) + cancel_delayed_work_sync(&spec->unsol_hp_work); + + if (spec->jd_cap_lineout) + cancel_delayed_work_sync(&spec->unsol_lineout_work); + + for (i = 0; i < spec->gen.autocfg.num_inputs; i++) { + if (spec->jd_cap_inputs[i]) { + cancel_delayed_work_sync(&spec->unsol_inputs_work); + break; + } + } - cancel_delayed_work_sync(&spec->unsol_hp_work); snd_hda_gen_remove(codec); } static int cm9825_suspend(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; + int i; - cancel_delayed_work_sync(&spec->unsol_hp_work); + if (spec->jd_cap_hp) + cancel_delayed_work_sync(&spec->unsol_hp_work); + + if (spec->jd_cap_lineout) + cancel_delayed_work_sync(&spec->unsol_lineout_work); + + for (i = 0; i < spec->gen.autocfg.num_inputs; i++) { + if (spec->jd_cap_inputs[i]) { + cancel_delayed_work_sync(&spec->unsol_inputs_work); + break; + } + } snd_hda_sequence_write(codec, spec->chip_d3_verbs); return 0; } -static int cm9825_resume(struct hda_codec *codec) +static int cm9825_cm_std_resume(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; hda_nid_t hp_pin = 0; @@ -215,6 +443,8 @@ static int cm9825_resume(struct hda_codec *codec) snd_hda_codec_init(codec); + snd_hda_sequence_write(codec, spec->chip_d0_verbs); + hp_pin = spec->gen.autocfg.hp_pins[0]; hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); @@ -232,6 +462,20 @@ static int cm9825_resume(struct hda_codec *codec) snd_hda_sequence_write(codec, cm9825_hp_remove_verbs); } + return 0; +} + +static int cm9825_resume(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + if (codec->core.subsystem_id == QUIRK_CM_STD) + cm9825_cm_std_resume(codec); + else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID) { + snd_hda_codec_init(codec); + snd_hda_sequence_write(codec, spec->chip_d0_verbs); + } + snd_hda_regmap_sync(codec); hda_call_check_power_status(codec, 0x01); @@ -242,13 +486,15 @@ 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; + int err = 0; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; - INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); + codec_dbg(codec, "chip_name: %s, ssid: 0x%X\n", + codec->core.chip_name, codec->core.subsystem_id); + codec->spec = spec; spec->codec = codec; cfg = &spec->gen.autocfg; @@ -258,6 +504,36 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->chip_hp_present_verbs = cm9825_hp_present_verbs; spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; + INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); + INIT_DELAYED_WORK(&spec->unsol_inputs_work, + cm9825_unsol_inputs_delayed); + INIT_DELAYED_WORK(&spec->unsol_lineout_work, + cm9825_unsol_lineout_delayed); + + switch (codec->core.subsystem_id) { + case QUIRK_CM_STD: + snd_hda_codec_set_name(codec, "CM9825 STD"); + spec->chip_d0_verbs = cm9825_std_d0_verbs; + spec->chip_d3_verbs = cm9825_std_d3_verbs; + spec->chip_hp_present_verbs = cm9825_hp_present_verbs; + spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; + break; + case QUIRK_GENE_TWL7_SSID: + snd_hda_codec_set_name(codec, "CM9825 GENE_TWL7"); + 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; + /* Internal fixed device, Rear, Mic-in, 3.5mm */ + snd_hda_codec_set_pincfg(codec, 0x37, 0x24A70100); + break; + default: + err = -ENXIO; + break; + } + + if (err < 0) + goto error; + snd_hda_sequence_write(codec, spec->chip_d0_verbs); err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); From 3addd63d1fba8d9013e00b06d9420e39271c0c4e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 15 Dec 2025 15:36:47 +0000 Subject: [PATCH 095/341] ASoC: SDCA: Factor out jack handling into new c file The jack code is perhaps a bit large for being in the interrupt code directly. Improve the encapsulation by factoring out the jack handling code into a new c file, as is already done for HID and FDL. Whilst doing so also add a jack_state structure to hold the jack state for improved expandability in the future. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20251215153650.3913117-2-ckeepax@opensource.cirrus.com Reviewed-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/sdca_jack.h | 27 ++++++ sound/soc/sdca/Makefile | 2 +- sound/soc/sdca/sdca_interrupts.c | 83 ++---------------- sound/soc/sdca/sdca_jack.c | 140 +++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 77 deletions(-) create mode 100644 include/sound/sdca_jack.h create mode 100644 sound/soc/sdca/sdca_jack.c diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h new file mode 100644 index 000000000000..9fad5f22cbb9 --- /dev/null +++ b/include/sound/sdca_jack.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright (C) 2025 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __SDCA_JACK_H__ +#define __SDCA_JACK_H__ + +struct sdca_interrupt; +struct snd_kcontrol; + +/** + * struct jack_state - Jack state structure to keep data between interrupts + * @kctl: Pointer to the ALSA control attached to this jack + */ +struct jack_state { + struct snd_kcontrol *kctl; +}; + +int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); +int sdca_jack_process(struct sdca_interrupt *interrupt); + +#endif // __SDCA_JACK_H__ diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index f6b73275d964..b3b0f5d94c8d 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -3,7 +3,7 @@ snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_function_device.o \ sdca_regmap.o sdca_asoc.o sdca_ump.o snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o -snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o +snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o sdca_jack.o snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o snd-soc-sdca-class-y := sdca_class.o diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 8f6a2adfb6fb..ff3a7e405fdc 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -155,14 +156,7 @@ static irqreturn_t detected_mode_handler(int irq, void *data) { struct sdca_interrupt *interrupt = data; struct device *dev = interrupt->dev; - struct snd_soc_component *component = interrupt->component; - struct snd_soc_card *card = component->card; - struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; - struct snd_kcontrol *kctl = interrupt->priv; - struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; - struct soc_enum *soc_enum; irqreturn_t irqret = IRQ_NONE; - unsigned int reg, val; int ret; ret = pm_runtime_get_sync(dev); @@ -171,76 +165,9 @@ static irqreturn_t detected_mode_handler(int irq, void *data) goto error; } - if (!kctl) { - const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", - interrupt->entity->label, - SDCA_CTL_SELECTED_MODE_NAME); - - if (!name) - goto error; - - kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) { - dev_dbg(dev, "control not found: %s\n", name); - goto error; - } - - interrupt->priv = kctl; - } - - soc_enum = (struct soc_enum *)kctl->private_value; - - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, - interrupt->control->sel, 0); - - ret = regmap_read(interrupt->function_regmap, reg, &val); - if (ret < 0) { - dev_err(dev, "failed to read detected mode: %d\n", ret); + ret = sdca_jack_process(interrupt); + if (ret) goto error; - } - - switch (val) { - case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS: - case SDCA_DETECTED_MODE_JACK_UNKNOWN: - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, - interrupt->entity->id, - SDCA_CTL_GE_SELECTED_MODE, 0); - - /* - * Selected mode is not normally marked as volatile register - * (RW), but here force a read from the hardware. If the - * detected mode is unknown we need to see what the device - * selected as a "safe" option. - */ - regcache_drop_region(interrupt->function_regmap, reg, reg); - - ret = regmap_read(interrupt->function_regmap, reg, &val); - if (ret) { - dev_err(dev, "failed to re-check selected mode: %d\n", ret); - goto error; - } - break; - default: - break; - } - - dev_dbg(dev, "%s: %#x\n", interrupt->name, val); - - ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); - if (!ucontrol) - goto error; - - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); - - down_write(rwsem); - ret = kctl->put(kctl, ucontrol); - up_write(rwsem); - if (ret < 0) { - dev_err(dev, "failed to update selected mode: %d\n", ret); - goto error; - } - - snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); irqret = IRQ_HANDLED; error: @@ -536,6 +463,10 @@ int sdca_irq_populate(struct sdca_function_data *function, handler = function_status_handler; break; case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + ret = sdca_jack_alloc_state(interrupt); + if (ret) + return ret; + handler = detected_mode_handler; break; case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c new file mode 100644 index 000000000000..83b2b9cc81f0 --- /dev/null +++ b/sound/soc/sdca/sdca_jack.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * sdca_jack_process - Process an SDCA jack event + * @interrupt: SDCA interrupt structure + * + * Return: Zero on success or a negative error code. + */ +int sdca_jack_process(struct sdca_interrupt *interrupt) +{ + struct device *dev = interrupt->dev; + struct snd_soc_component *component = interrupt->component; + struct snd_soc_card *card = component->card; + struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; + struct jack_state *state = interrupt->priv; + struct snd_kcontrol *kctl = state->kctl; + struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; + struct soc_enum *soc_enum; + unsigned int reg, val; + int ret; + + if (!kctl) { + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", + interrupt->entity->label, + SDCA_CTL_SELECTED_MODE_NAME); + + if (!name) + return -ENOMEM; + + kctl = snd_soc_component_get_kcontrol(component, name); + if (!kctl) { + dev_dbg(dev, "control not found: %s\n", name); + return -ENOENT; + } + + state->kctl = kctl; + } + + soc_enum = (struct soc_enum *)kctl->private_value; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + interrupt->control->sel, 0); + + ret = regmap_read(interrupt->function_regmap, reg, &val); + if (ret < 0) { + dev_err(dev, "failed to read detected mode: %d\n", ret); + return ret; + } + + switch (val) { + case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS: + case SDCA_DETECTED_MODE_JACK_UNKNOWN: + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, + interrupt->entity->id, + SDCA_CTL_GE_SELECTED_MODE, 0); + + /* + * Selected mode is not normally marked as volatile register + * (RW), but here force a read from the hardware. If the + * detected mode is unknown we need to see what the device + * selected as a "safe" option. + */ + regcache_drop_region(interrupt->function_regmap, reg, reg); + + ret = regmap_read(interrupt->function_regmap, reg, &val); + if (ret) { + dev_err(dev, "failed to re-check selected mode: %d\n", ret); + return ret; + } + break; + default: + break; + } + + dev_dbg(dev, "%s: %#x\n", interrupt->name, val); + + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return -ENOMEM; + + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + + down_write(rwsem); + ret = kctl->put(kctl, ucontrol); + up_write(rwsem); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return ret; + } + + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); + +/** + * sdca_jack_alloc_state - allocate state for a jack interrupt + * @interrupt: SDCA interrupt structure. + * + * Return: Zero on success or a negative error code. + */ +int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) +{ + struct device *dev = interrupt->dev; + struct jack_state *jack_state; + + jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL); + if (!jack_state) + return -ENOMEM; + + interrupt->priv = jack_state; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA"); From 82e12800f563baf663277ef0017f40a335b8e84c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 15 Dec 2025 15:36:48 +0000 Subject: [PATCH 096/341] ASoC: SDCA: Add ability to connect SDCA jacks to ASoC jacks Add handling for the ASoC jack API to SDCA to allow user-space to be hooked up normally. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20251215153650.3913117-3-ckeepax@opensource.cirrus.com Reviewed-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/sdca_jack.h | 5 ++ sound/soc/sdca/sdca_jack.c | 106 ++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 9fad5f22cbb9..3ec22046d3eb 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -12,16 +12,21 @@ struct sdca_interrupt; struct snd_kcontrol; +struct snd_soc_jack; /** * struct jack_state - Jack state structure to keep data between interrupts * @kctl: Pointer to the ALSA control attached to this jack + * @jack: Pointer to the ASoC jack struct for this jack */ struct jack_state { struct snd_kcontrol *kctl; + struct snd_soc_jack *jack; }; int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); int sdca_jack_process(struct sdca_interrupt *interrupt); +int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack); +int sdca_jack_report(struct sdca_interrupt *interrupt); #endif // __SDCA_JACK_H__ diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index 83b2b9cc81f0..5b9cf69cbcd6 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -17,11 +17,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include /** @@ -114,7 +116,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - return 0; + return sdca_jack_report(interrupt); } EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); @@ -138,3 +140,105 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) return 0; } EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA"); + +/** + * sdca_jack_set_jack - attach an ASoC jack to SDCA + * @info: SDCA interrupt information. + * @jack: ASoC jack to be attached. + * + * Return: Zero on success or a negative error code. + */ +int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack) +{ + int i, ret; + + guard(mutex)(&info->irq_lock); + + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + struct sdca_interrupt *interrupt = &info->irqs[i]; + struct sdca_control *control = interrupt->control; + struct sdca_entity *entity = interrupt->entity; + struct jack_state *jack_state; + + if (!interrupt->irq) + continue; + + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + jack_state = interrupt->priv; + jack_state->jack = jack; + + /* Report initial state in case IRQ was already handled */ + ret = sdca_jack_report(interrupt); + if (ret) + return ret; + break; + default: + break; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA"); + +int sdca_jack_report(struct sdca_interrupt *interrupt) +{ + struct jack_state *jack_state = interrupt->priv; + struct sdca_control_range *range; + enum sdca_terminal_type type; + unsigned int report = 0; + unsigned int reg, val; + int ret; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + SDCA_CTL_GE_SELECTED_MODE, 0); + + ret = regmap_read(interrupt->function_regmap, reg, &val); + if (ret) { + dev_err(interrupt->dev, "failed to read selected mode: %d\n", ret); + return ret; + } + + range = sdca_selector_find_range(interrupt->dev, interrupt->entity, + SDCA_CTL_GE_SELECTED_MODE, + SDCA_SELECTED_MODE_NCOLS, 0); + if (!range) + return -EINVAL; + + type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX, + val, SDCA_SELECTED_MODE_TERM_TYPE); + + switch (type) { + case SDCA_TERM_TYPE_LINEIN_STEREO: + case SDCA_TERM_TYPE_LINEIN_FRONT_LR: + case SDCA_TERM_TYPE_LINEIN_CENTER_LFE: + case SDCA_TERM_TYPE_LINEIN_SURROUND_LR: + case SDCA_TERM_TYPE_LINEIN_REAR_LR: + report = SND_JACK_LINEIN; + break; + case SDCA_TERM_TYPE_LINEOUT_STEREO: + case SDCA_TERM_TYPE_LINEOUT_FRONT_LR: + case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE: + case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR: + case SDCA_TERM_TYPE_LINEOUT_REAR_LR: + report = SND_JACK_LINEOUT; + break; + case SDCA_TERM_TYPE_MIC_JACK: + report = SND_JACK_MICROPHONE; + break; + case SDCA_TERM_TYPE_HEADPHONE_JACK: + report = SND_JACK_HEADPHONE; + break; + case SDCA_TERM_TYPE_HEADSET_JACK: + report = SND_JACK_HEADSET; + break; + default: + break; + } + + snd_soc_jack_report(jack_state->jack, report, 0xFFFF); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA"); From 99a3ef1e81cd1775bc1f8cc2ad188b1fc755d5cd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 15 Dec 2025 15:36:49 +0000 Subject: [PATCH 097/341] ASoC: SDCA: Add ASoC jack hookup in class driver Add the necessary calls to the class driver to connect the ASoC jack from the machine driver. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20251215153650.3913117-4-ckeepax@opensource.cirrus.com Reviewed-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 0028482a1e75..416948cfb5cb 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,15 @@ static int class_function_component_probe(struct snd_soc_component *component) return sdca_irq_populate(drv->function, component, core->irq_info); } +static int class_function_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) +{ + struct class_function_drv *drv = snd_soc_component_get_drvdata(component); + struct sdca_class_drv *core = drv->core; + + return sdca_jack_set_jack(core->irq_info, jack); +} + static const struct snd_soc_component_driver class_function_component_drv = { .probe = class_function_component_probe, .endianness = 1, @@ -351,6 +361,9 @@ static int class_function_probe(struct auxiliary_device *auxdev, return dev_err_probe(dev, PTR_ERR(drv->regmap), "failed to create regmap"); + if (desc->type == SDCA_FUNCTION_TYPE_UAJ) + cmp_drv->set_jack = class_function_set_jack; + ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv, &dais, &num_dais, &class_function_sdw_ops); From c4be067a7b67f616b9ec85423a20be6be9b8ec37 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 19 Dec 2025 12:45:44 +0100 Subject: [PATCH 098/341] ASoC: qcom: topology: Constify pointed topology and vendor structs Several functions in topology.c receive pointers to 'struct snd_soc_tplg_vendor_array' and 'struct snd_soc_tplg_private', and do not modify their contents. Constify the pointers for self-explanatory code (pointed memory is not modified by the function) and a bit safer code. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251219-b4-container-of-const-asoc-qcom-v2-1-05fd2ecc06fe@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 70 ++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 5ce6edf3305e..062eb01fc7a7 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -206,15 +206,15 @@ static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm, return mod; } -static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array( - struct snd_soc_tplg_private *private) +static const struct snd_soc_tplg_vendor_array * +audioreach_get_sg_array(const struct snd_soc_tplg_private *private) { - struct snd_soc_tplg_vendor_array *sg_array = NULL; + const struct snd_soc_tplg_vendor_array *sg_array = NULL; bool found = false; int sz; for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - struct snd_soc_tplg_vendor_value_elem *sg_elem; + const struct snd_soc_tplg_vendor_value_elem *sg_elem; int tkn_count = 0; sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); @@ -239,15 +239,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array( return NULL; } -static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array( - struct snd_soc_tplg_private *private) +static const struct snd_soc_tplg_vendor_array * +audioreach_get_cont_array(const struct snd_soc_tplg_private *private) { - struct snd_soc_tplg_vendor_array *cont_array = NULL; + const struct snd_soc_tplg_vendor_array *cont_array = NULL; bool found = false; int sz; for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - struct snd_soc_tplg_vendor_value_elem *cont_elem; + const struct snd_soc_tplg_vendor_value_elem *cont_elem; int tkn_count = 0; cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); @@ -272,15 +272,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array( return NULL; } -static struct snd_soc_tplg_vendor_array *audioreach_get_module_array( - struct snd_soc_tplg_private *private) +static const struct snd_soc_tplg_vendor_array * +audioreach_get_module_array(const struct snd_soc_tplg_private *private) { - struct snd_soc_tplg_vendor_array *mod_array = NULL; + const struct snd_soc_tplg_vendor_array *mod_array = NULL; bool found = false; int sz = 0; for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; int tkn_count = 0; mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); @@ -305,13 +305,13 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_module_array( return NULL; } -static struct audioreach_module_priv_data *audioreach_get_module_priv_data( - struct snd_soc_tplg_private *private) +static struct audioreach_module_priv_data * +audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private) { int sz; for (sz = 0; sz < le32_to_cpu(private->size); ) { - struct snd_soc_tplg_vendor_array *mod_array; + const struct snd_soc_tplg_vendor_array *mod_array; mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) { @@ -334,10 +334,10 @@ static struct audioreach_module_priv_data *audioreach_get_module_priv_data( } static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm, - struct snd_soc_tplg_private *private) + const struct snd_soc_tplg_private *private) { - struct snd_soc_tplg_vendor_value_elem *sg_elem; - struct snd_soc_tplg_vendor_array *sg_array; + const struct snd_soc_tplg_vendor_value_elem *sg_elem; + const struct snd_soc_tplg_vendor_array *sg_array; struct audioreach_graph_info *info = NULL; int graph_id, sub_graph_id, tkn_count = 0; struct audioreach_sub_graph *sg; @@ -392,10 +392,10 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm, struct audioreach_sub_graph *sg, - struct snd_soc_tplg_private *private) + const struct snd_soc_tplg_private *private) { - struct snd_soc_tplg_vendor_value_elem *cont_elem; - struct snd_soc_tplg_vendor_array *cont_array; + const struct snd_soc_tplg_vendor_value_elem *cont_elem; + const struct snd_soc_tplg_vendor_array *cont_array; struct audioreach_container *cont; int container_id, tkn_count = 0; bool found = false; @@ -437,7 +437,7 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm, struct audioreach_container *cont, - struct snd_soc_tplg_private *private, + const struct snd_soc_tplg_private *private, struct snd_soc_dapm_widget *w) { uint32_t max_ip_port = 0, max_op_port = 0; @@ -447,8 +447,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap uint32_t src_mod_inst_id = 0; int module_id = 0, instance_id = 0, tkn_count = 0; - struct snd_soc_tplg_vendor_value_elem *mod_elem; - struct snd_soc_tplg_vendor_array *mod_array; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_array *mod_array; struct audioreach_module *mod = NULL; uint32_t token; bool found; @@ -622,8 +622,8 @@ static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *componen int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; - struct snd_soc_tplg_vendor_array *mod_array; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_array *mod_array; struct audioreach_module *mod; struct snd_soc_dobj *dobj; int tkn_count = 0; @@ -660,9 +660,9 @@ static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *componen } static int audioreach_widget_log_module_load(struct audioreach_module *mod, - struct snd_soc_tplg_vendor_array *mod_array) + const struct snd_soc_tplg_vendor_array *mod_array) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; int tkn_count = 0; mod_elem = mod_array->value; @@ -690,9 +690,9 @@ static int audioreach_widget_log_module_load(struct audioreach_module *mod, } static int audioreach_widget_dma_module_load(struct audioreach_module *mod, - struct snd_soc_tplg_vendor_array *mod_array) + const struct snd_soc_tplg_vendor_array *mod_array) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; int tkn_count = 0; mod_elem = mod_array->value; @@ -719,9 +719,9 @@ static int audioreach_widget_dma_module_load(struct audioreach_module *mod, } static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, - struct snd_soc_tplg_vendor_array *mod_array) + const struct snd_soc_tplg_vendor_array *mod_array) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; int tkn_count = 0; mod_elem = mod_array->value; @@ -754,9 +754,9 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, } static int audioreach_widget_dp_module_load(struct audioreach_module *mod, - struct snd_soc_tplg_vendor_array *mod_array) + const struct snd_soc_tplg_vendor_array *mod_array) { - struct snd_soc_tplg_vendor_value_elem *mod_elem; + const struct snd_soc_tplg_vendor_value_elem *mod_elem; int tkn_count = 0; mod_elem = mod_array->value; @@ -780,7 +780,7 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - struct snd_soc_tplg_vendor_array *mod_array; + const struct snd_soc_tplg_vendor_array *mod_array; struct audioreach_module *mod; struct snd_soc_dobj *dobj; int ret; From 5e357c7e5e0920bd806a4e7c446c83715315f923 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 19 Dec 2025 12:45:45 +0100 Subject: [PATCH 099/341] ASoC: qcom: topology: Constify pointed ar control structs audioreach_route_load() does not modify the pointed 'struct audioreach_module' and functions for connecting subgraphs do not change pointed 'struct snd_ar_control'. Constify the pointers for self-explanatory code (pointed memory is not modified by the function) and a bit safer code. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251219-b4-container-of-const-asoc-qcom-v2-2-05fd2ecc06fe@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 062eb01fc7a7..9bde799146fa 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -1032,7 +1032,7 @@ static struct audioreach_module *audioreach_find_module(struct snd_soc_component static int audioreach_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) { - struct audioreach_module *src_module, *sink_module; + const struct audioreach_module *src_module, *sink_module; struct snd_ar_control *control; struct snd_soc_dapm_widget *w; int i; @@ -1098,8 +1098,8 @@ static int audioreach_link_load(struct snd_soc_component *component, int index, } static void audioreach_connect_sub_graphs(struct q6apm *apm, - struct snd_ar_control *m1, - struct snd_ar_control *m2, + const struct snd_ar_control *m1, + const struct snd_ar_control *m2, bool connect) { struct audioreach_graph_info *info; @@ -1123,10 +1123,10 @@ static void audioreach_connect_sub_graphs(struct q6apm *apm, } static bool audioreach_is_vmixer_connected(struct q6apm *apm, - struct snd_ar_control *m1, - struct snd_ar_control *m2) + const struct snd_ar_control *m1, + const struct snd_ar_control *m2) { - struct audioreach_graph_info *info; + const struct audioreach_graph_info *info; mutex_lock(&apm->lock); info = idr_find(&apm->graph_info_idr, m2->graph_id); From 61fc95c4e3b26b3d1259a87124bdc25e4f71a8df Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 19 Dec 2025 12:45:46 +0100 Subject: [PATCH 100/341] ASoC: qcom: topology: Constify pointed DAPM widget structs Few functions do not modify the pointed 'struct struct snd_soc_dapm_widget', so the pointers can be made as pointers to const for self-explanatory code (pointed memory is not modified by the function) and a bit safer code. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251219-b4-container-of-const-asoc-qcom-v2-3-05fd2ecc06fe@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 9bde799146fa..fbba543bd976 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -947,7 +947,7 @@ static int audioreach_widget_ready(struct snd_soc_component *component, static int audioreach_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj); + const struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj); struct q6apm *apm = dev_get_drvdata(scomp->dev); struct audioreach_container *cont; struct audioreach_module *mod; @@ -1144,10 +1144,10 @@ static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); - struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol); + const struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol); struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); - struct snd_ar_control *dapm_scontrol = dw->dobj.private; - struct snd_ar_control *scontrol = mc->dobj.private; + const struct snd_ar_control *dapm_scontrol = dw->dobj.private; + const struct snd_ar_control *scontrol = mc->dobj.private; struct q6apm *data = dev_get_drvdata(c->dev); bool connected; @@ -1167,8 +1167,8 @@ static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol); struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); - struct snd_ar_control *dapm_scontrol = dw->dobj.private; - struct snd_ar_control *scontrol = mc->dobj.private; + const struct snd_ar_control *dapm_scontrol = dw->dobj.private; + const struct snd_ar_control *scontrol = mc->dobj.private; struct q6apm *data = dev_get_drvdata(c->dev); if (ucontrol->value.integer.value[0]) { @@ -1206,14 +1206,14 @@ static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol, static int audioreach_control_load_mix(struct snd_soc_component *scomp, struct snd_ar_control *scontrol, struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) + const struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_soc_tplg_vendor_value_elem *c_elem; - struct snd_soc_tplg_vendor_array *c_array; - struct snd_soc_tplg_mixer_control *mc; + const struct snd_soc_tplg_vendor_value_elem *c_elem; + const struct snd_soc_tplg_vendor_array *c_array; + const struct snd_soc_tplg_mixer_control *mc; int tkn_count = 0; - mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); + mc = container_of_const(hdr, struct snd_soc_tplg_mixer_control, hdr); c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data; c_elem = c_array->value; From 49675f5e750a2a5d530c56d130017d0337eed18f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 19 Dec 2025 12:45:47 +0100 Subject: [PATCH 101/341] ASoC: qcom: topology: Constify pointed snd_soc_tplg_dapm_widget Several functions in topology.c receive pointers to 'struct snd_soc_tplg_dapm_widget' and do not modify their contents. Constify the pointers for self-explanatory code (pointed memory is not modified by the function) and a bit safer code. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251219-b4-container-of-const-asoc-qcom-v2-4-05fd2ecc06fe@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index fbba543bd976..2e71eaa90441 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -590,7 +590,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap static int audioreach_widget_load_module_common(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + const struct snd_soc_tplg_dapm_widget *tplg_w) { struct q6apm *apm = dev_get_drvdata(component->dev); struct audioreach_container *cont; @@ -620,7 +620,7 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + const struct snd_soc_tplg_dapm_widget *tplg_w) { const struct snd_soc_tplg_vendor_value_elem *mod_elem; const struct snd_soc_tplg_vendor_array *mod_array; @@ -778,7 +778,7 @@ static int audioreach_widget_dp_module_load(struct audioreach_module *mod, static int audioreach_widget_load_buffer(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + const struct snd_soc_tplg_dapm_widget *tplg_w) { const struct snd_soc_tplg_vendor_array *mod_array; struct audioreach_module *mod; @@ -818,10 +818,10 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, static int audioreach_widget_load_mixer(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + const struct snd_soc_tplg_dapm_widget *tplg_w) { - struct snd_soc_tplg_vendor_value_elem *w_elem; - struct snd_soc_tplg_vendor_array *w_array; + const struct snd_soc_tplg_vendor_value_elem *w_elem; + const struct snd_soc_tplg_vendor_array *w_array; struct snd_ar_control *scontrol; struct q6apm *data = dev_get_drvdata(component->dev); struct snd_soc_dobj *dobj; @@ -886,7 +886,7 @@ static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = { static int audioreach_widget_load_pga(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + const struct snd_soc_tplg_dapm_widget *tplg_w) { struct audioreach_module *mod; struct snd_soc_dobj *dobj; From 4ab48cc63e15cb619d641d1edf9a15a0a98875b2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 19 Dec 2025 12:45:48 +0100 Subject: [PATCH 102/341] ASoC: qcom: audioreach: Constify function arguments Several functions receive pointers to parsed Audioreach topology (e.g. 'struct audioreach_container', 'struct audioreach_module') and they do not modify their contents, but copy their data to send to the ADSP. Constify the pointers for self-explanatory code (pointed memory is not modified by the function) and a bit safer code. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251219-b4-container-of-const-asoc-qcom-v2-5-05fd2ecc06fe@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 87 +++++++++++++++++-------------- sound/soc/qcom/qdsp6/audioreach.h | 16 +++--- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index b28451558974..241c3b4479c6 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -309,7 +309,7 @@ void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels) EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping); static void apm_populate_container_config(struct apm_container_obj *cfg, - struct audioreach_container *cont) + const struct audioreach_container *cont) { /* Container Config */ @@ -339,7 +339,7 @@ static void apm_populate_container_config(struct apm_container_obj *cfg, } static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg, - struct audioreach_sub_graph *sg) + const struct audioreach_sub_graph *sg) { cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id; cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP; @@ -361,7 +361,7 @@ static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg, } static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj, - struct audioreach_module *module) + const struct audioreach_module *module) { obj->instance_id = module->instance_id; @@ -373,7 +373,7 @@ static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj, } static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj, - struct audioreach_container *container, + const struct audioreach_container *container, int sub_graph_id) { struct audioreach_module *module; @@ -390,9 +390,10 @@ static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj, } } -static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph_info *info, +static void audioreach_populate_graph(struct q6apm *apm, + const struct audioreach_graph_info *info, struct apm_graph_open_params *open, - struct list_head *sg_list, + const struct list_head *sg_list, int num_sub_graphs) { struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data; @@ -464,7 +465,8 @@ static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph } } -void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info *info) +void *audioreach_alloc_graph_pkt(struct q6apm *apm, + const struct audioreach_graph_info *info) { int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz; struct apm_module_param_data *param_data; @@ -477,7 +479,7 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info struct audioreach_module *module; struct audioreach_sub_graph *sgs; struct apm_mod_list_obj *mlobj; - struct list_head *sg_list; + const struct list_head *sg_list; int num_connections = 0; int num_containers = 0; int num_sub_graphs = 0; @@ -630,8 +632,8 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync); static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { struct apm_display_port_module_intf_cfg *intf_cfg; struct apm_module_frame_size_factor_cfg *fs_cfg; @@ -687,8 +689,8 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, /* LPASS Codec DMA port Module Media Format Setup */ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { struct apm_codec_dma_module_intf_cfg *intf_cfg; struct apm_module_frame_size_factor_cfg *fs_cfg; @@ -753,7 +755,8 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, return q6apm_send_cmd_sync(graph->apm, pkt, 0); } -int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module, +int audioreach_send_u32_param(struct q6apm_graph *graph, + const struct audioreach_module *module, uint32_t param_id, uint32_t param_val) { struct apm_module_param_data *param_data; @@ -782,36 +785,37 @@ int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_modul EXPORT_SYMBOL_GPL(audioreach_send_u32_param); static int audioreach_sal_limiter_enable(struct q6apm_graph *graph, - struct audioreach_module *module, bool enable) + const struct audioreach_module *module, + bool enable) { return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable); } static int audioreach_sal_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width); } static int audioreach_module_enable(struct q6apm_graph *graph, - struct audioreach_module *module, + const struct audioreach_module *module, bool enable) { return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable); } static int audioreach_gapless_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY, EARLY_EOS_DELAY_MS); } static int audioreach_set_module_config(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { int size = le32_to_cpu(module->data->size); void *p; @@ -828,8 +832,8 @@ static int audioreach_set_module_config(struct q6apm_graph *graph, } static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { struct apm_module_param_data *param_data; struct param_id_mfc_media_format *media_format; @@ -863,7 +867,8 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, } static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, - void *p, struct audioreach_module_config *mcfg) + void *p, + const struct audioreach_module_config *mcfg) { struct payload_media_fmt_aac_t *aac_cfg; struct payload_media_fmt_pcm *mp3_cfg; @@ -944,7 +949,8 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, return 0; } -int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg) +int audioreach_compr_set_param(struct q6apm_graph *graph, + const struct audioreach_module_config *mcfg) { struct media_format *header; int rc; @@ -969,8 +975,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu EXPORT_SYMBOL_GPL(audioreach_compr_set_param); static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { struct apm_module_frame_size_factor_cfg *fs_cfg; struct apm_module_param_data *param_data; @@ -1037,7 +1043,7 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, } static int audioreach_logging_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module) + const struct audioreach_module *module) { struct apm_module_param_data *param_data; struct data_logging_config *cfg; @@ -1066,8 +1072,8 @@ static int audioreach_logging_set_media_format(struct q6apm_graph *graph, } static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *mcfg) + const struct audioreach_module *module, + const struct audioreach_module_config *mcfg) { struct payload_pcm_output_format_cfg *media_cfg; uint32_t num_channels = mcfg->num_channels; @@ -1113,8 +1119,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, } static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *mcfg) + const struct audioreach_module *module, + const struct audioreach_module_config *mcfg) { uint32_t num_channels = mcfg->num_channels; struct apm_module_param_data *param_data; @@ -1170,7 +1176,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, return audioreach_graph_send_cmd_sync(graph, pkt, 0); } -int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol) +int audioreach_gain_set_vol_ctrl(struct q6apm *apm, + const struct audioreach_module *module, int vol) { struct param_id_vol_ctrl_master_gain *cfg; struct apm_module_param_data *param_data; @@ -1195,7 +1202,8 @@ int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *mo } EXPORT_SYMBOL_GPL(audioreach_gain_set_vol_ctrl); -static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_module *module) +static int audioreach_gain_set(struct q6apm_graph *graph, + const struct audioreach_module *module) { struct apm_module_param_data *param_data; struct apm_gain_module_cfg *cfg; @@ -1218,7 +1226,7 @@ static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_modu } static int audioreach_speaker_protection(struct q6apm_graph *graph, - struct audioreach_module *module, + const struct audioreach_module *module, uint32_t operation_mode) { return audioreach_send_u32_param(graph, module, PARAM_ID_SP_OP_MODE, @@ -1226,8 +1234,8 @@ static int audioreach_speaker_protection(struct q6apm_graph *graph, } static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *mcfg) + const struct audioreach_module *module, + const struct audioreach_module_config *mcfg) { u32 num_channels = mcfg->num_channels; struct apm_module_sp_vi_op_mode_cfg *op_cfg; @@ -1304,8 +1312,9 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, return rc; } -int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, - struct audioreach_module_config *cfg) +int audioreach_set_media_format(struct q6apm_graph *graph, + const struct audioreach_module *module, + const struct audioreach_module_config *cfg) { int rc; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 03cfd32f1d0c..89f172aab8c0 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -831,8 +831,8 @@ void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token, void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, uint32_t src_port, uint32_t dest_port); -void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info - *info); +void *audioreach_alloc_graph_pkt(struct q6apm *apm, + const struct audioreach_graph_info *info); /* Topology specific */ int audioreach_tplg_init(struct snd_soc_component *component); @@ -848,13 +848,15 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt, uint32_t rsp_opcode); int audioreach_set_media_format(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg); + const struct audioreach_module *module, + const struct audioreach_module_config *cfg); int audioreach_shared_memory_send_eos(struct q6apm_graph *graph); int audioreach_gain_set_vol_ctrl(struct q6apm *apm, - struct audioreach_module *module, int vol); -int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module, + const struct audioreach_module *module, int vol); +int audioreach_send_u32_param(struct q6apm_graph *graph, + const struct audioreach_module *module, uint32_t param_id, uint32_t param_val); -int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg); +int audioreach_compr_set_param(struct q6apm_graph *graph, + const struct audioreach_module_config *mcfg); #endif /* __AUDIOREACH_H__ */ From 9e692bb5412a7b0e6534ba2d7158a57ed4b00658 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 19 Dec 2025 16:08:08 -0700 Subject: [PATCH 103/341] ASoC: rt1320: Change return type of rt1320_t0_load() to void Clang warns (or errors with CONFIG_WERROR=y / W=e): sound/soc/codecs/rt1320-sdw.c:1387:6: error: variable 'ret' is used uninitialized whenever 'if' condition is true [-Werror,-Wsometimes-uninitialized] 1387 | if (!fw_ready) { | ^~~~~~~~~ sound/soc/codecs/rt1320-sdw.c:1421:9: note: uninitialized use occurs here 1421 | return ret; | ^~~ sound/soc/codecs/rt1320-sdw.c:1387:2: note: remove the 'if' if its condition is always false 1387 | if (!fw_ready) { | ^~~~~~~~~~~~~~~~ 1388 | dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1389 | goto _exit_; | ~~~~~~~~~~~~ 1390 | } | ~ sound/soc/codecs/rt1320-sdw.c:1366:9: note: initialize the variable 'ret' to silence this warning 1366 | int ret; | ^ | = 0 The return value of rt1320_t0_load() is never actually used, so it can just be eliminated altogether by returning void, clearing up the warning. Fixes: da1682d5e8b5 ("ASoC: rt1320: support calibration and temperature/r0 loading") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512191711.wY6XU796-lkp@intel.com/ Signed-off-by: Nathan Chancellor Acked-by: Shuming Fan Link: https://patch.msgid.link/20251219-rt1320-sdw-avoid-uninit-ret-v1-1-faa3e250ebc4@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index e31d0dcb5b75..00eb79204835 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1359,13 +1359,12 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) } } -static int rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) +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; int l_t0_data[38], r_t0_data[38]; unsigned int fw_status_addr; - int ret; switch (rt1320->dev_id) { case RT1320_DEV_ID: @@ -1376,7 +1375,7 @@ static int rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, uns break; default: dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return -EINVAL; + return; } regmap_write(rt1320->regmap, @@ -1401,8 +1400,7 @@ static int rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, uns rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data)); rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data)); - ret = rt1320_check_fw_ready(rt1320); - if (ret < 0) + if (rt1320_check_fw_ready(rt1320) < 0) dev_err(dev, "%s: Failed to set FW param 3,4!\n", __func__); rt1320->temp_l_calib = l_t0; @@ -1419,8 +1417,6 @@ _exit_: 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); - - return ret; } static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) From c6bca73d699cfe00d3419566fdb2a45e112f44b0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 20 Dec 2025 11:45:40 +0300 Subject: [PATCH 104/341] ASoC: rt1320: Fix retry checking in rt1320_rae_load() This loop iterates 200 times and then gives up. The problem is that currently the loop exits with "retry" set to -1 on the failure path but the check for failure expects it to be 0. Change from a post-op to a pre-op so that it exits with "retry" set to 0. Fixes: 22937af75abb ("ASoC: rt1320: support RAE parameters loading") Signed-off-by: Dan Carpenter Acked-by: Shuming Fan Link: https://patch.msgid.link/aUZiNJ7pzuahXFYE@stanley.mountain Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 00eb79204835..e1c4a1adacff 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1478,7 +1478,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) /* RAE stop & CRC disable */ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); - while (retry--) { + while (--retry) { regmap_read(rt1320->regmap, 0xe83f, &value); if (value & 0x40) break; From 8db50f0fa43efe8799fd40b872dcdd39a90d7549 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 22 Dec 2025 18:13:29 +0800 Subject: [PATCH 105/341] ASoC: rt1320: fix the warning the string may be truncated 1488 | "realtek/rt1320/rt1320_%s_%s_%s.dat", vendor, product, sku); | ^~ ~~~~~~ sound/soc/codecs/rt1320-sdw.c:1487:17: note: 'snprintf' output between 29 and 410 bytes into a destination of size 128 1487 | snprintf(filename, sizeof(filename), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1488 | "realtek/rt1320/rt1320_%s_%s_%s.dat", vendor, product, sku); | Fixes: da1682d5e8b5 ("ASoC: rt1320: support calibration and temperature/r0 loading") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512191521.RK0edKdX-lkp@intel.com/ Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20251222101329.558973-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index e1c4a1adacff..2c621d94fcf5 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1433,7 +1433,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) const char *dmi_vendor, *dmi_product, *dmi_sku; char vendor[128], product[128], sku[128]; char *ptr_vendor, *ptr_product, *ptr_sku; - char rae_filename[128]; + char rae_filename[512]; char tag[5]; int ret = 0; int retry = 200; @@ -1600,7 +1600,7 @@ struct rt1320_dspfwheader { const char *dmi_vendor, *dmi_product, *dmi_sku; char vendor[128], product[128], sku[128]; char *ptr_vendor, *ptr_product, *ptr_sku; - char filename[128]; + char filename[512]; switch (rt1320->dev_id) { case RT1320_DEV_ID: From 5de5db35350d9c4def1de2ae273e224a4eee5ed1 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Mon, 22 Dec 2025 17:20:06 +0800 Subject: [PATCH 106/341] ALSA: hda/realtek - Enable Mute LED for Lenovo platform Enable SPK Mute Led and Mic Mute Led for Lenovo platform. Signed-off-by: Kailang Yang Link: https://patch.msgid.link/8a99edffee044e13b6e348d1b69c2b57@realtek.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 85e7b5d4c496..00ffea62bc78 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1616,6 +1616,20 @@ static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, } } +static void alc233_fixup_lenovo_coef_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mic_led_coef.idx = 0x10; + spec->mic_led_coef.mask = 1 << 13; + spec->mic_led_coef.on = 0; + spec->mic_led_coef.off = 1 << 13; + snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); + } +} + static void alc285_fixup_hp_mute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -1906,6 +1920,39 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, } } +/* GPIO2 = mic mute hotkey + * GPIO3 = mic mute LED + */ +static void alc233_fixup_lenovo_gpio2_mic_hotkey(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + alc233_fixup_lenovo_coef_micmute_led(codec, fix, action); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + alc_update_coef_idx(codec, 0x10, 1<<2, 1<<2); + if (alc_register_micmute_input_device(codec) != 0) + return; + + spec->gpio_mask |= 0x04; + spec->gpio_dir |= 0x0; + snd_hda_codec_write_cache(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); + snd_hda_jack_detect_enable_callback(codec, codec->core.afg, + gpio2_mic_hotkey_event); + return; + } + + if (!spec->kb_dev) + return; + + switch (action) { + case HDA_FIXUP_ACT_FREE: + input_unregister_device(spec->kb_dev); + spec->kb_dev = NULL; + } +} + /* Line2 = mic mute hotkey * GPIO2 = mic mute LED */ @@ -3791,6 +3838,7 @@ enum { ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED, ALC288_FIXUP_SURFACE_SWAP_DACS, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO, + ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6256,6 +6304,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc288_fixup_surface_swap_dacs, }, + [ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_gpio2_mic_hotkey, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7152,7 +7204,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x3341, "Lenovo ThinkCentre M90 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x3342, "Lenovo ThinkCentre M90 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x3343, "Lenovo ThinkCentre M70 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x3344, "Lenovo ThinkCentre M70 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x334f, "Lenovo ThinkCentre M90a Gen5", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), From e38bdd30708b8bad577b07de5fdb2baa1cae8527 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:11:46 +0100 Subject: [PATCH 107/341] ALSA: hda/cs35l41: Clean up runtime PM with guard() Replace the manual pm_runtime_get_sync() and pm_runtime_put_autosuspend() calls with the new guard(pm_runtime_active_auto) for code simplification. Along with this change, the former scoped_guard(mutex) can be set back to the plain guard(mutex), and the indent level is taken back, too. Merely code cleanups, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216141154.172218-2-tiwai@suse.de --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 23 +++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 21e00055c0c4..b64890006bb7 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1255,19 +1255,16 @@ static void cs35l41_fw_load_work(struct work_struct *work) { struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work); - pm_runtime_get_sync(cs35l41->dev); + guard(pm_runtime_active_auto)(cs35l41->dev); - scoped_guard(mutex, &cs35l41->fw_mutex) { - /* Recheck if playback is ongoing, mutex will block playback during firmware loading */ - if (cs35l41->playback_started) - dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n"); - else - cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load); + guard(mutex)(&cs35l41->fw_mutex); + /* Recheck if playback is ongoing, mutex will block playback during firmware loading */ + if (cs35l41->playback_started) + dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n"); + else + cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load); - cs35l41->fw_request_ongoing = false; - } - - pm_runtime_put_autosuspend(cs35l41->dev); + cs35l41->fw_request_ongoing = false; } static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol, @@ -1455,7 +1452,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas if (comp->dev) return -EBUSY; - pm_runtime_get_sync(dev); + guard(pm_runtime_active_auto)(dev); mutex_lock(&cs35l41->fw_mutex); @@ -1499,8 +1496,6 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas dev_warn(dev, "Unable to create device link\n"); unlock_system_sleep(sleep_flags); - pm_runtime_put_autosuspend(dev); - dev_info(cs35l41->dev, "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, From 0ff22680e51f4c636f9fa98a1357f21310cb2a9a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:11:47 +0100 Subject: [PATCH 108/341] ALSA: hda/cs35l56: Clean up with PM_RUNTIME_ACQUIRE*() macros Use PM_RUNTIME_ACQUIRE*() macros for replacing the manual pm_runtime_resume_and_get() and pm_runtime_put_*() calls. Merely code cleanups and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216141154.172218-3-tiwai@suse.de --- sound/hda/codecs/side-codecs/cs35l56_hda.c | 36 +++++++++------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index f7ba92e11957..cfc8de2ae499 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -588,7 +588,8 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) cs35l56->base.fw_patched = false; - ret = pm_runtime_resume_and_get(cs35l56->base.dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56->base.dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret < 0) { dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret); return; @@ -601,7 +602,7 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) */ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver); if (ret) - goto err_pm_put; + return; if (firmware_missing) preloaded_fw_ver = 0; @@ -690,8 +691,6 @@ err: err_fw_release: cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename); -err_pm_put: - pm_runtime_put(cs35l56->base.dev); } static void cs35l56_hda_dsp_work(struct work_struct *work) @@ -708,14 +707,12 @@ static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file, struct cs35l56_base *cs35l56_base = file->private_data; ssize_t ret; - ret = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; - ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos); - pm_runtime_autosuspend(cs35l56_base->dev); - - return ret; + return cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos); } static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file, @@ -725,14 +722,12 @@ static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file, struct cs35l56_base *cs35l56_base = file->private_data; ssize_t ret; - ret = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; - ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos); - pm_runtime_autosuspend(cs35l56_base->dev); - - return ret; + return cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos); } static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file, @@ -742,14 +737,12 @@ static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file, struct cs35l56_base *cs35l56_base = file->private_data; ssize_t ret; - ret = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; - ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos); - pm_runtime_autosuspend(cs35l56_base->dev); - - return ret; + return cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos); } static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file, @@ -767,7 +760,8 @@ static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file, if (ret < 0) return ret; - ret = pm_runtime_resume_and_get(cs35l56_base->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; @@ -777,8 +771,6 @@ static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file, else count = -EIO; - pm_runtime_autosuspend(cs35l56_base->dev); - return count; } From 4a91da4afc7db944d17234e4ecc164df8252b23b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:11:48 +0100 Subject: [PATCH 109/341] ALSA: hda/tas2781: Clean up runtime PM with guard() Use guard(pm_runtime_active_auto) for replacing the manual calls of pm_runtime_get_sync() and pm_runtime_put_autosuspend(). Along with this, we can use guard() for the tas_priv->codec_lock mutex in tasdev_fw_ready(), too (that aligns both i2c and spi codes). Merely code cleanups and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216141154.172218-4-tiwai@suse.de --- sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 10 +++------- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 7 ++----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index c8619995b1d7..c74f83d4ec7f 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -500,8 +500,8 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) struct hda_codec *codec = tas_priv->codec; int ret; - pm_runtime_get_sync(tas_priv->dev); - mutex_lock(&tas_priv->codec_lock); + guard(pm_runtime_active_auto)(tas_priv->dev); + guard(mutex)(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); if (ret) @@ -537,9 +537,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) } out: - mutex_unlock(&tas_hda->priv->codec_lock); release_firmware(fmw); - pm_runtime_put_autosuspend(tas_hda->dev); } static int tas2781_hda_bind(struct device *dev, struct device *master, @@ -571,7 +569,7 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, break; } - pm_runtime_get_sync(dev); + guard(pm_runtime_active_auto)(dev); comp->dev = dev; @@ -581,8 +579,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_put_autosuspend(dev); - 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 b9a55672bf15..f8412c5df919 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -636,7 +636,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) struct hda_codec *codec = tas_priv->codec; int ret, val; - pm_runtime_get_sync(tas_priv->dev); + guard(pm_runtime_active_auto)(tas_priv->dev); guard(mutex)(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); @@ -699,7 +699,6 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tas2781_save_calibration(tas_hda); out: release_firmware(fmw); - pm_runtime_put_autosuspend(tas_hda->priv->dev); } static int tas2781_hda_bind(struct device *dev, struct device *master, @@ -720,7 +719,7 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, codec = parent->codec; - pm_runtime_get_sync(dev); + guard(pm_runtime_active_auto)(dev); comp->dev = dev; @@ -731,8 +730,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_put_autosuspend(dev); - return ret; } From be9dd97060e704e702d508f4a295d06e1197ae08 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:11:49 +0100 Subject: [PATCH 110/341] ALSA: hda/tegra: Clean up runtime PM with guard() Use guard(pm_runtime_active) for replacing the manual calls of pm_runtime_get_sync() and pm_runtime_put(). Merely code cleanups and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216141154.172218-5-tiwai@suse.de --- sound/hda/controllers/tegra.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c index 6ab338f37db5..31c14c4bbe68 100644 --- a/sound/hda/controllers/tegra.c +++ b/sound/hda/controllers/tegra.c @@ -592,30 +592,26 @@ static void hda_tegra_probe_work(struct work_struct *work) struct platform_device *pdev = to_platform_device(hda->dev); int err; - pm_runtime_get_sync(hda->dev); + guard(pm_runtime_active)(hda->dev); err = hda_tegra_first_init(chip, pdev); if (err < 0) - goto out_free; + return; /* create codec instances */ err = azx_probe_codecs(chip, 8); if (err < 0) - goto out_free; + return; err = azx_codec_configure(chip); if (err < 0) - goto out_free; + return; err = snd_card_register(chip->card); if (err < 0) - goto out_free; + return; chip->running = 1; snd_hda_set_power_save(&chip->bus, power_save * 1000); - - out_free: - pm_runtime_put(hda->dev); - return; /* no error return from async probe */ } static void hda_tegra_remove(struct platform_device *pdev) From f8c537ff0492eb27c160592702a96ea7cb19b493 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Dec 2025 15:11:50 +0100 Subject: [PATCH 111/341] ALSA: x86: Clean up locks and runtime PM with guard() and co Use PM_RUNTIME_ACQUIRE_*() and guard() for replacing the manual calls of runtime PM and mutex lock in had_audio_wq(). Merely code cleanups and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20251216141154.172218-6-tiwai@suse.de --- sound/x86/intel_hdmi_audio.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 8e2a007311d3..f5807fc73c54 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1517,13 +1517,12 @@ static void had_audio_wq(struct work_struct *work) container_of(work, struct snd_intelhad, hdmi_audio_wq); struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data; struct intel_hdmi_lpe_audio_port_pdata *ppdata = &pdata->port[ctx->port]; - int ret; - ret = pm_runtime_resume_and_get(ctx->dev); - if (ret < 0) + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(ctx->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return; - mutex_lock(&ctx->mutex); + guard(mutex)(&ctx->mutex); if (ppdata->pipe < 0) { dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG : port = %d\n", __func__, ctx->port); @@ -1564,9 +1563,6 @@ static void had_audio_wq(struct work_struct *work) /* Restart the stream if necessary */ had_process_mode_change(ctx); } - - mutex_unlock(&ctx->mutex); - pm_runtime_put_autosuspend(ctx->dev); } /* From f92d27a6ee158c43e276712af23c79997780471a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 21:35:33 +0100 Subject: [PATCH 112/341] ASoC: rockchip: Discard pm_runtime_put() return value It is better to check directly whether or not CONFIG_PM has been enabled instead of relying on an error value returned by pm_runtime_put() in that case because pm_runtime_put() may also return an error value in other cases, like after writing "on" to the devices' runtime PM "control" attribute in sysfs for one example. This will facilitate a planned change of the pm_runtime_put() return type to void in the future. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5160923.0VBMTVartN@rafael.j.wysocki Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index ebdf0056065b..1bf614dbdf4d 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1487,8 +1487,9 @@ static int rockchip_sai_probe(struct platform_device *pdev) return 0; err_runtime_suspend: - /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */ - if (pm_runtime_put(&pdev->dev)) + if (IS_ENABLED(CONFIG_PM)) + pm_runtime_put(&pdev->dev); + else rockchip_sai_runtime_suspend(&pdev->dev); return ret; From 45e9066f3a487e9e26b842644364d045af054775 Mon Sep 17 00:00:00 2001 From: Brahmajit Das Date: Mon, 22 Dec 2025 00:25:31 +0530 Subject: [PATCH 113/341] ASoC: Intel: avs: replace strcmp with sysfs_streq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allmodconfig failes to build with GCC 16 with the following build error sound/soc/intel/avs/path.c:137:38: error: ‘strcmp’ reading 1 or more bytes from a region of size 0 [-Werror=stringop-overread] 137 | return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ‘avs_condpaths_walk’: events 1-3 137 | return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name); | ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (3) warning happens here | (1) when the condition is evaluated to true ...... 155 | if (id->id != path->template->owner->id || | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | (2) when the condition is evaluated to false 156 | strcmp(id->tplg_name, path->template->owner->owner->name)) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from sound/soc/intel/avs/path.h:14, from sound/soc/intel/avs/path.c:15: sound/soc/intel/avs/topology.h: In function ‘avs_condpaths_walk’: sound/soc/intel/avs/topology.h:152:13: note: at offset 4 into source object ‘id’ of size 4 152 | u32 id; | ^~ Using the sysfs_streq as an alternative to strcmp helps getting around this build failure. Please also refer https://docs.kernel.org/core-api/kernel-api.html#c.__sysfs_match_string Signed-off-by: Brahmajit Das Link: https://patch.msgid.link/20251221185531.6453-1-listout@listout.xyz Signed-off-by: Mark Brown --- sound/soc/intel/avs/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index c8b586aced20..899906e0bcb4 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -134,7 +134,7 @@ static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev, static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id, struct avs_tplg_path_template_id *id2) { - return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name); + return id->id == id2->id && !sysfs_streq(id->tplg_name, id2->tplg_name); } static struct avs_path *avs_condpath_find_match(struct avs_dev *adev, From 836ecc740ca829040d86a5371f1fcb276110df84 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Dec 2025 22:52:53 +0100 Subject: [PATCH 114/341] ASoC: rt1320: fix 32-bit link failure A plain 64-bit division causes a link failure in some configurations: ERROR: modpost: "__aeabi_uldivmod" [sound/soc/codecs/snd-soc-rt1320-sdw.ko] undefined! Since this divides by a constant, using the div_u64() macro ends up turning this into an efficient multiply/shift operation where possible. In rt1320_calc_r0(), the open-coded shift seems a litle simpler. Fixes: da1682d5e8b5 ("ASoC: rt1320: support calibration and temperature/r0 loading") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20251223215259.677762-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 2c621d94fcf5..0e97a00e49d6 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1067,8 +1067,8 @@ static int rt1320_invrs_load(struct rt1320_sdw_priv *rt1320) r_rsratio = rt1320_rsgain_to_rsratio(rt1320, r_rsgain); dev_dbg(dev, "%s, LR rsratio=%lld, %lld\n", __func__, l_rsratio, r_rsratio); - l_invrs = (l_rsratio * factor) / 1000000000U; - r_invrs = (r_rsratio * factor) / 1000000000U; + l_invrs = div_u64(l_rsratio * factor, 1000000000U); + r_invrs = div_u64(r_rsratio * factor, 1000000000U); rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); @@ -1089,15 +1089,15 @@ static int rt1320_invrs_load(struct rt1320_sdw_priv *rt1320) static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) { struct device *dev = &rt1320->sdw_slave->dev; - unsigned long long l_calir0, r_calir0; - const unsigned int factor = (1 << 27); + unsigned long long l_calir0, r_calir0, l_calir0_lo, r_calir0_lo; - l_calir0 = (rt1320->r0_l_reg * 1000) / factor; - r_calir0 = (rt1320->r0_r_reg * 1000) / factor; + l_calir0 = rt1320->r0_l_reg >> 27; + r_calir0 = rt1320->r0_r_reg >> 27; + l_calir0_lo = (rt1320->r0_l_reg & ((1ull << 27) - 1) * 1000) >> 27; + r_calir0_lo = (rt1320->r0_r_reg & ((1ull << 27) - 1) * 1000) >> 27; dev_dbg(dev, "%s, l_calir0=%lld.%03lld ohm, r_calir0=%lld.%03lld ohm\n", __func__, - l_calir0 / 1000, l_calir0 % 1000, - r_calir0 / 1000, r_calir0 % 1000); + l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); } static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) From 284853affe73fe1ca9786bd52b934eb9d420a942 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Dec 2025 22:53:19 +0100 Subject: [PATCH 115/341] ASoC: rt1320: fix size_t format string Printing a size_t portably requires the use of %z instead of %l: sound/soc/codecs/rt1320-sdw.c:1494:30: error: format '%lx' expects argument of type 'long unsigned int', but argument 5 has type 'size_t' {aka 'unsigned int'} [-Werror=format=] 1494 | dev_dbg(dev, "%s, rae_fw size=0x%lx\n", __func__, rae_fw->size); | ^~~~~~~~~~~~~~~~~~~~~~~~~ Fixes: 22937af75abb ("ASoC: rt1320: support RAE parameters loading") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20251223215322.694265-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 0e97a00e49d6..84e9e8ab9798 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1489,7 +1489,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) return -ETIMEDOUT; } - dev_dbg(dev, "%s, rae_fw size=0x%lx\n", __func__, rae_fw->size); + dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); regcache_cache_bypass(rt1320->regmap, true); for (fw_offset = 0; fw_offset < rae_fw->size;) { From fc22dfb13618ce1ae5730840607733a50569a15e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Dec 2025 00:05:51 +0000 Subject: [PATCH 116/341] ASoC: alc5623: tidyup clock inversion in alc5623_set_dai_fmt() It supports inverted bitclock (= _NB_, _IB_), but has no control for the frame polarity (= _NF, _IF). Let's tidyup it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0mrtogw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/alc5623.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index a9946dcdc9f6..ec229b315f9f 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -678,14 +678,9 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; - case SND_SOC_DAIFMT_IB_IF: - iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; - break; case SND_SOC_DAIFMT_IB_NF: iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; break; - case SND_SOC_DAIFMT_NB_IF: - break; default: return -EINVAL; } From 25abdc151a448a17d500ea9468ce32582c479faa Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 26 Dec 2025 14:42:55 +0800 Subject: [PATCH 117/341] ASoC: rt1320: fix the remainder calculation of r0 value This patch fixes the remainder calculation of r0 value. Fixes: 836ecc740ca8 ("ASoC: rt1320: fix 32-bit link failure") Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20251226064255.993735-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 84e9e8ab9798..6e3920d1d308 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1093,8 +1093,8 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) l_calir0 = rt1320->r0_l_reg >> 27; r_calir0 = rt1320->r0_r_reg >> 27; - l_calir0_lo = (rt1320->r0_l_reg & ((1ull << 27) - 1) * 1000) >> 27; - r_calir0_lo = (rt1320->r0_r_reg & ((1ull << 27) - 1) * 1000) >> 27; + l_calir0_lo = ((rt1320->r0_l_reg & ((1ull << 27) - 1)) * 1000) >> 27; + r_calir0_lo = ((rt1320->r0_r_reg & ((1ull << 27) - 1)) * 1000) >> 27; dev_dbg(dev, "%s, l_calir0=%lld.%03lld ohm, r_calir0=%lld.%03lld ohm\n", __func__, l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); From 537cd8fd3c58525ab407a3e80e170bc86a3e96ca Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Dec 2025 14:22:35 +0100 Subject: [PATCH 118/341] ALSA: mixart: adjust field name reference buf_period_pos seems to not have existed in the git history, but the buf_period_frag field is used with the buf_periods field in the function snd_mixart_stream_pointer in mixart.c, so it seems that buf_periods is what was intended. Signed-off-by: Julia Lawall Link: https://patch.msgid.link/20251230132235.90687-1-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index cbed6d9a9f2e..3fcc5ca757c1 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -125,7 +125,7 @@ struct mixart_stream { u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */ u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */ - u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */ + u32 buf_period_frag; /* defines with buf_periods the exact position in the buffer (< runtime->period_size) */ int channels; }; From 60dd3ace961ec037ecb4c80c3a22627715d9ac71 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Dec 2025 15:01:03 +0100 Subject: [PATCH 119/341] ALSA: ice1724: adjust function name reference The code just below the comment is_pro_rate_locked(), so it seems like that was what was intended for the comment. Signed-off-by: Julia Lawall Link: https://patch.msgid.link/20251230140103.93134-1-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1724.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index e2dbbbfbca9f..65bf48647d08 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -973,7 +973,7 @@ static int set_rate_constraints(struct snd_ice1712 *ice, ice->hw_rates); } -/* if the card has the internal rate locked (is_pro_locked), limit runtime +/* if the card has the internal rate locked (is_pro_rate_locked), limit runtime hw rates to the current internal rate only. */ static void constrain_rate_if_locked(struct snd_pcm_substream *substream) From 94968fc3009d4bd7e7e1300abd7037f3dde585ed Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 31 Dec 2025 17:11:16 +0100 Subject: [PATCH 120/341] ALSA: echoaudio: adjust function name The function restore_dsp_rettings sets a lot of things, so change the name to the more natural restore_dsp_settings. This name was indeed already used in a comment above the code in sound/pci/echoaudio/echoaudio.c. Signed-off-by: Julia Lawall Link: https://patch.msgid.link/20251231161116.141071-1-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 2 +- sound/pci/echoaudio/echoaudio_dsp.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index f2c8602a1ad7..8b7b6838106f 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2159,7 +2159,7 @@ static int snd_echo_resume(struct device *dev) */ pipe_alloc_mask = chip->pipe_alloc_mask; chip->pipe_alloc_mask = 0; - err = restore_dsp_rettings(chip); + err = restore_dsp_settings(chip); chip->pipe_alloc_mask = pipe_alloc_mask; if (err < 0) { kfree(commpage_bak); diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 2a40091d472c..6e872103305b 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -32,7 +32,7 @@ #error PAGE_SIZE is < 4k #endif -static int restore_dsp_rettings(struct echoaudio *chip); +static int restore_dsp_settings(struct echoaudio *chip); /* Some vector commands involve the DSP reading or writing data to and from the @@ -666,7 +666,7 @@ static void get_audio_meters(struct echoaudio *chip, long *meters) -static int restore_dsp_rettings(struct echoaudio *chip) +static int restore_dsp_settings(struct echoaudio *chip) { int i, o, err; @@ -1014,7 +1014,7 @@ static int init_line_levels(struct echoaudio *chip) chip->input_clock = ECHO_CLOCK_INTERNAL; chip->output_clock = ECHO_CLOCK_WORD; chip->sample_rate = 44100; - return restore_dsp_rettings(chip); + return restore_dsp_settings(chip); } From bc0305cb294c693b2762cf863324defb9e5175e5 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:04 +0000 Subject: [PATCH 121/341] firmware: cs_dsp: Handle long-offset data blocks Handle a new type of data block that has a 32-bit offset. These are identical to the normal blocks except that the offset is now in the 32-bit field that was previously 'sr'. A new file version of 3 indicates that it is mandatory to process the long-offset blocks, so that older code without that support will reject the file. The original 'sr' field was never used by the driver so it has been renamed offset32. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 19 +++++++++++++++---- include/linux/firmware/cirrus/wmfw.h | 7 ++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index d35d0f5ccaf7..aa6e740f9cd7 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -2138,7 +2138,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware const struct cs_dsp_region *mem; struct cs_dsp_alg_region *alg_region; const char *region_name; - int ret, pos, blocks, type, offset, reg, version; + int ret, pos, blocks, type, version; + unsigned int offset, reg; u8 *buf = NULL; size_t buf_len = 0; size_t region_len; @@ -2163,6 +2164,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware switch (be32_to_cpu(hdr->rev) & 0xff) { case 1: case 2: + case 3: break; default: cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", @@ -2171,7 +2173,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware goto out_fw; } - cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file, + cs_dsp_info(dsp, "%s (v%d): v%d.%d.%d\n", file, + be32_to_cpu(hdr->rev) & 0xff, (le32_to_cpu(hdr->ver) >> 16) & 0xff, (le32_to_cpu(hdr->ver) >> 8) & 0xff, le32_to_cpu(hdr->ver) & 0xff); @@ -2202,8 +2205,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware (le32_to_cpu(blk->ver) >> 16) & 0xff, (le32_to_cpu(blk->ver) >> 8) & 0xff, le32_to_cpu(blk->ver) & 0xff); - cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", - file, blocks, le32_to_cpu(blk->len), offset, type); + cs_dsp_dbg(dsp, "%s.%d: %d bytes off:%#x off32:%#x in %#x\n", + file, blocks, le32_to_cpu(blk->len), offset, + le32_to_cpu(blk->offset32), type); reg = 0; region_name = "Unknown"; @@ -2236,6 +2240,13 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware } break; + case WMFW_ADSP2_XM_LONG: + case WMFW_ADSP2_YM_LONG: + case WMFW_HALO_XM_PACKED_LONG: + case WMFW_HALO_YM_PACKED_LONG: + offset = le32_to_cpu(blk->offset32); + type &= 0xff; /* strip extended block type flags */ + fallthrough; case WMFW_ADSP1_DM: case WMFW_ADSP1_ZM: case WMFW_ADSP2_XM: diff --git a/include/linux/firmware/cirrus/wmfw.h b/include/linux/firmware/cirrus/wmfw.h index 74e5a4f6c13a..eae24dde9e41 100644 --- a/include/linux/firmware/cirrus/wmfw.h +++ b/include/linux/firmware/cirrus/wmfw.h @@ -172,7 +172,7 @@ struct wmfw_coeff_item { __le16 type; __le32 id; __le32 ver; - __le32 sr; + __le32 offset32; __le32 len; u8 data[]; } __packed; @@ -200,4 +200,9 @@ struct wmfw_coeff_item { #define WMFW_HALO_XM_PACKED 0x11 #define WMFW_HALO_YM_PACKED 0x12 +#define WMFW_ADSP2_XM_LONG 0xf405 +#define WMFW_ADSP2_YM_LONG 0xf406 +#define WMFW_HALO_XM_PACKED_LONG 0xf411 +#define WMFW_HALO_YM_PACKED_LONG 0xf412 + #endif From afcbb0460e0a9f0e971512c66619fc649bdb86a6 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:05 +0000 Subject: [PATCH 122/341] firmware: cs_dsp: test_bin: Run test cases with v3 file format The new v3 file format has all the same functionality as the earlier formats, so run all the existing test cases with a file type of 3. This is only done for Halo Core because v3 files are not used with the older ADSP cores. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_test_bin.c | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c index 163b7faecff4..4532a7e9833a 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c @@ -2149,7 +2149,8 @@ static void bin_patch_name_and_info(struct kunit *test) KUNIT_EXPECT_EQ(test, reg_val, payload_data); } -static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp) +static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp, + int wmdr_ver) { struct cs_dsp_test *priv; struct cs_dsp_mock_xm_header *xm_hdr; @@ -2197,7 +2198,7 @@ static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp) KUNIT_ASSERT_EQ(test, ret, 0); priv->local->bin_builder = - cs_dsp_mock_bin_init(priv, 1, + cs_dsp_mock_bin_init(priv, wmdr_ver, cs_dsp_mock_xm_header_get_fw_version(xm_hdr)); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->local->bin_builder); @@ -2227,7 +2228,7 @@ static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp) return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp); } -static int cs_dsp_bin_test_halo_init(struct kunit *test) +static int cs_dsp_bin_test_halo_init_common(struct kunit *test, int wmdr_ver) { struct cs_dsp *dsp; @@ -2243,7 +2244,17 @@ static int cs_dsp_bin_test_halo_init(struct kunit *test) dsp->base = cs_dsp_mock_halo_core_base; dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base; - return cs_dsp_bin_test_common_init(test, dsp); + return cs_dsp_bin_test_common_init(test, dsp, wmdr_ver); +} + +static int cs_dsp_bin_test_halo_init(struct kunit *test) +{ + return cs_dsp_bin_test_halo_init_common(test, 1); +} + +static int cs_dsp_bin_test_halo_wmdr3_init(struct kunit *test) +{ + return cs_dsp_bin_test_halo_init_common(test, 3); } static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test) @@ -2262,7 +2273,7 @@ static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test) dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes); dsp->base = cs_dsp_mock_adsp2_32bit_sysbase; - return cs_dsp_bin_test_common_init(test, dsp); + return cs_dsp_bin_test_common_init(test, dsp, 1); } static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test) @@ -2281,7 +2292,7 @@ static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test) dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes); dsp->base = cs_dsp_mock_adsp2_16bit_sysbase; - return cs_dsp_bin_test_common_init(test, dsp); + return cs_dsp_bin_test_common_init(test, dsp, 1); } /* Parameterize on choice of XM or YM with a range of word offsets */ @@ -2539,6 +2550,12 @@ static struct kunit_suite cs_dsp_bin_test_halo = { .test_cases = cs_dsp_bin_test_cases_halo, }; +static struct kunit_suite cs_dsp_bin_test_halo_wmdr3 = { + .name = "cs_dsp_bin_halo_wmdr_v3", + .init = cs_dsp_bin_test_halo_wmdr3_init, + .test_cases = cs_dsp_bin_test_cases_halo, +}; + static struct kunit_suite cs_dsp_bin_test_adsp2_32bit = { .name = "cs_dsp_bin_adsp2_32bit", .init = cs_dsp_bin_test_adsp2_32bit_init, @@ -2552,5 +2569,6 @@ static struct kunit_suite cs_dsp_bin_test_adsp2_16bit = { }; kunit_test_suites(&cs_dsp_bin_test_halo, + &cs_dsp_bin_test_halo_wmdr3, &cs_dsp_bin_test_adsp2_32bit, &cs_dsp_bin_test_adsp2_16bit); From a01816edf11fc9e6c8216bc03870311ce2add5a2 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:06 +0000 Subject: [PATCH 123/341] firmware: cs_dsp: test_bin: Make patch function a test parameter Make the call to cs_dsp_mock_bin_add_patch() a function pointer in the test case parameters, instead of calling it directly. This is to allow for future parameterization by which function to call to add a patch. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_test_bin.c | 708 +++++++++--------- 1 file changed, 357 insertions(+), 351 deletions(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c index 4532a7e9833a..7f53fa9964c4 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c @@ -69,6 +69,10 @@ struct bin_test_param { int mem_type; unsigned int offset_words; int alg_idx; + void (*add_patch)(struct cs_dsp_mock_bin_builder *builder, + unsigned int alg_id, unsigned int alg_ver, + int mem_region, unsigned int reg_addr_offset, + const void *payload_data, size_t payload_len_bytes); }; static const struct cs_dsp_mock_alg_def bin_test_mock_algs[] = { @@ -128,12 +132,12 @@ static void bin_patch_one_word(struct kunit *test) bin_test_mock_algs[param->alg_idx].id, param->mem_type); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - param->offset_words * reg_inc_per_word, - &payload_data, sizeof(payload_data)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + param->offset_words * reg_inc_per_word, + &payload_data, sizeof(payload_data)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -177,12 +181,12 @@ static void bin_patch_one_multiword(struct kunit *test) bin_test_mock_algs[param->alg_idx].id, param->mem_type); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - param->offset_words * reg_inc_per_word, - payload_data, sizeof(payload_data)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + param->offset_words * reg_inc_per_word, + payload_data, sizeof(payload_data)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -228,12 +232,12 @@ static void bin_patch_multi_oneword(struct kunit *test) /* Add one payload per word */ for (i = 0; i < ARRAY_SIZE(payload_data); ++i) { - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (param->offset_words + i) * reg_inc_per_word, - &payload_data[i], sizeof(payload_data[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (param->offset_words + i) * reg_inc_per_word, + &payload_data[i], sizeof(payload_data[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -285,13 +289,13 @@ static void bin_patch_multi_oneword_unordered(struct kunit *test) /* Add one payload per word */ for (i = 0; i < ARRAY_SIZE(word_order); ++i) { - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (param->offset_words + word_order[i]) * - reg_inc_per_word, - &payload_data[word_order[i]], sizeof(payload_data[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (param->offset_words + word_order[i]) * + reg_inc_per_word, + &payload_data[word_order[i]], sizeof(payload_data[0])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -346,12 +350,12 @@ static void bin_patch_multi_oneword_sparse_unordered(struct kunit *test) /* Add one payload per word */ for (i = 0; i < ARRAY_SIZE(word_offsets); ++i) { - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - word_offsets[i] * reg_inc_per_word, - &payload_data[i], sizeof(payload_data[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + word_offsets[i] * reg_inc_per_word, + &payload_data[i], sizeof(payload_data[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -413,27 +417,27 @@ static void bin_patch_one_word_multiple_mems(struct kunit *test) } /* Add words to XM, YM and ZM */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - WMFW_ADSP2_XM, - param->offset_words * reg_inc_per_word, - &payload_data[0], sizeof(payload_data[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + WMFW_ADSP2_XM, + param->offset_words * reg_inc_per_word, + &payload_data[0], sizeof(payload_data[0])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - WMFW_ADSP2_YM, - param->offset_words * reg_inc_per_word, - &payload_data[1], sizeof(payload_data[1])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + WMFW_ADSP2_YM, + param->offset_words * reg_inc_per_word, + &payload_data[1], sizeof(payload_data[1])); if (cs_dsp_mock_has_zm(priv)) { - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - WMFW_ADSP2_ZM, - param->offset_words * reg_inc_per_word, - &payload_data[2], sizeof(payload_data[2])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + WMFW_ADSP2_ZM, + param->offset_words * reg_inc_per_word, + &payload_data[2], sizeof(payload_data[2])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -502,12 +506,12 @@ static void bin_patch_one_word_multiple_algs(struct kunit *test) for (i = 0; i < ARRAY_SIZE(bin_test_mock_algs); ++i) { reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[i].id, - bin_test_mock_algs[i].ver, - param->mem_type, - param->offset_words * reg_inc_per_word, - &payload_data[i], sizeof(payload_data[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[i].id, + bin_test_mock_algs[i].ver, + param->mem_type, + param->offset_words * reg_inc_per_word, + &payload_data[i], sizeof(payload_data[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -565,12 +569,12 @@ static void bin_patch_one_word_multiple_algs_unordered(struct kunit *test) alg_idx = alg_order[i]; reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[alg_idx].id, - bin_test_mock_algs[alg_idx].ver, - param->mem_type, - param->offset_words * reg_inc_per_word, - &payload_data[i], sizeof(payload_data[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[alg_idx].id, + bin_test_mock_algs[alg_idx].ver, + param->mem_type, + param->offset_words * reg_inc_per_word, + &payload_data[i], sizeof(payload_data[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -628,12 +632,12 @@ static void bin_patch_1_packed(struct kunit *test) patch_pos_words = round_up(alg_base_words + param->offset_words, 4); patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -688,20 +692,20 @@ static void bin_patch_1_packed_1_single_trailing(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); /* Patch packed block */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); /* ... and the unpacked word following that */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 4) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 4) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -768,27 +772,27 @@ static void bin_patch_1_packed_2_single_trailing(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); /* Patch packed block */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); /* ... and the unpacked words following that */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 4) - alg_base_words) * 4, - &unpacked_payloads[0], sizeof(unpacked_payloads[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 4) - alg_base_words) * 4, + &unpacked_payloads[0], sizeof(unpacked_payloads[0])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 5) - alg_base_words) * 4, - &unpacked_payloads[1], sizeof(unpacked_payloads[1])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 5) - alg_base_words) * 4, + &unpacked_payloads[1], sizeof(unpacked_payloads[1])); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -857,34 +861,34 @@ static void bin_patch_1_packed_3_single_trailing(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); /* Patch packed block */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); /* ... and the unpacked words following that */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 4) - alg_base_words) * 4, - &unpacked_payloads[0], sizeof(unpacked_payloads[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 4) - alg_base_words) * 4, + &unpacked_payloads[0], sizeof(unpacked_payloads[0])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 5) - alg_base_words) * 4, - &unpacked_payloads[1], sizeof(unpacked_payloads[1])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 5) - alg_base_words) * 4, + &unpacked_payloads[1], sizeof(unpacked_payloads[1])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 6) - alg_base_words) * 4, - &unpacked_payloads[2], sizeof(unpacked_payloads[2])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 6) - alg_base_words) * 4, + &unpacked_payloads[2], sizeof(unpacked_payloads[2])); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -953,20 +957,20 @@ static void bin_patch_1_packed_2_trailing(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); /* Patch packed block */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); /* ... and the unpacked words following that */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 4) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 4) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1035,20 +1039,20 @@ static void bin_patch_1_packed_3_trailing(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); /* Patch packed block */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); /* ... and the unpacked words following that */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((patch_pos_words + 4) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((patch_pos_words + 4) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1117,20 +1121,20 @@ static void bin_patch_1_single_leading_1_packed(struct kunit *test) packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4; /* Patch the leading unpacked word */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 1) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 1) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); /* ... then the packed block */ patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1196,26 +1200,26 @@ static void bin_patch_2_single_leading_1_packed(struct kunit *test) packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4; /* Patch the leading unpacked words */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 2) - alg_base_words) * 4, - &unpacked_payload[0], sizeof(unpacked_payload[0])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 1) - alg_base_words) * 4, - &unpacked_payload[1], sizeof(unpacked_payload[1])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 2) - alg_base_words) * 4, + &unpacked_payload[0], sizeof(unpacked_payload[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 1) - alg_base_words) * 4, + &unpacked_payload[1], sizeof(unpacked_payload[1])); /* ... then the packed block */ patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1283,20 +1287,20 @@ static void bin_patch_2_leading_1_packed(struct kunit *test) packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4; /* Patch the leading unpacked words */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 2) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 2) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); /* ... then the packed block */ patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1364,32 +1368,32 @@ static void bin_patch_3_single_leading_1_packed(struct kunit *test) packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4; /* Patch the leading unpacked words */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 3) - alg_base_words) * 4, - &unpacked_payload[0], sizeof(unpacked_payload[0])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 2) - alg_base_words) * 4, - &unpacked_payload[1], sizeof(unpacked_payload[1])); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 1) - alg_base_words) * 4, - &unpacked_payload[2], sizeof(unpacked_payload[2])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 3) - alg_base_words) * 4, + &unpacked_payload[0], sizeof(unpacked_payload[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 2) - alg_base_words) * 4, + &unpacked_payload[1], sizeof(unpacked_payload[1])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 1) - alg_base_words) * 4, + &unpacked_payload[2], sizeof(unpacked_payload[2])); /* ... then the packed block */ patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1457,20 +1461,20 @@ static void bin_patch_3_leading_1_packed(struct kunit *test) packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4; /* Patch the leading unpacked words */ - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - unpacked_mem_type, - ((packed_patch_pos_words - 3) - alg_base_words) * 4, - unpacked_payload, sizeof(unpacked_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + unpacked_mem_type, + ((packed_patch_pos_words - 3) - alg_base_words) * 4, + unpacked_payload, sizeof(unpacked_payload)); /* ... then the packed block */ patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - &packed_payload, sizeof(packed_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + &packed_payload, sizeof(packed_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1537,12 +1541,12 @@ static void bin_patch_multi_onepacked(struct kunit *test) for (i = 0; i < ARRAY_SIZE(packed_payloads); ++i) { patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words + (i * 4)); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - payload_offset, - &packed_payloads[i], sizeof(packed_payloads[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + payload_offset, + &packed_payloads[i], sizeof(packed_payloads[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -1602,13 +1606,13 @@ static void bin_patch_multi_onepacked_unordered(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words + (payload_order[i] * 4)); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - payload_offset, - &packed_payloads[payload_order[i]], - sizeof(packed_payloads[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + payload_offset, + &packed_payloads[payload_order[i]], + sizeof(packed_payloads[0])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -1665,13 +1669,13 @@ static void bin_patch_multi_onepacked_sparse_unordered(struct kunit *test) patch_pos_words = round_up(alg_base_words + word_offsets[i], 4); patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - param->mem_type, - payload_offset, - &packed_payloads[i], - sizeof(packed_payloads[0])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + param->mem_type, + payload_offset, + &packed_payloads[i], + sizeof(packed_payloads[0])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -1737,21 +1741,21 @@ static void bin_patch_1_packed_multiple_mems(struct kunit *test) /* Add XM and YM patches */ alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_xm_base_words); patch_pos_in_packed_regs = _num_words_to_num_packed_regs(xm_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - WMFW_HALO_XM_PACKED, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - packed_xm_payload, sizeof(packed_xm_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + WMFW_HALO_XM_PACKED, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + packed_xm_payload, sizeof(packed_xm_payload)); alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_ym_base_words); patch_pos_in_packed_regs = _num_words_to_num_packed_regs(ym_patch_pos_words); - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[param->alg_idx].id, - bin_test_mock_algs[param->alg_idx].ver, - WMFW_HALO_YM_PACKED, - (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, - packed_ym_payload, sizeof(packed_ym_payload)); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[param->alg_idx].id, + bin_test_mock_algs[param->alg_idx].ver, + WMFW_HALO_YM_PACKED, + (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4, + packed_ym_payload, sizeof(packed_ym_payload)); fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); KUNIT_ASSERT_EQ(test, @@ -1821,12 +1825,12 @@ static void bin_patch_1_packed_multiple_algs(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[i].id, - bin_test_mock_algs[i].ver, - param->mem_type, - payload_offset, - packed_payload[i], sizeof(packed_payload[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[i].id, + bin_test_mock_algs[i].ver, + param->mem_type, + payload_offset, + packed_payload[i], sizeof(packed_payload[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -1907,12 +1911,12 @@ static void bin_patch_1_packed_multiple_algs_unordered(struct kunit *test) patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[alg_idx].id, - bin_test_mock_algs[alg_idx].ver, - param->mem_type, - payload_offset, - packed_payload[i], sizeof(packed_payload[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[alg_idx].id, + bin_test_mock_algs[alg_idx].ver, + param->mem_type, + payload_offset, + packed_payload[i], sizeof(packed_payload[i])); } fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder); @@ -2004,22 +2008,22 @@ static void bin_patch_mixed_packed_unpacked_random(struct kunit *test) alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_base_words); patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words); payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[0].id, - bin_test_mock_algs[0].ver, - param->mem_type, - payload_offset, - payload->packed[i], - sizeof(payload->packed[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[0].id, + bin_test_mock_algs[0].ver, + param->mem_type, + payload_offset, + payload->packed[i], + sizeof(payload->packed[i])); } else { payload_offset = offset_words[i] * 4; - cs_dsp_mock_bin_add_patch(priv->local->bin_builder, - bin_test_mock_algs[0].id, - bin_test_mock_algs[0].ver, - unpacked_mem_type, - payload_offset, - &payload->unpacked[i], - sizeof(payload->unpacked[i])); + param->add_patch(priv->local->bin_builder, + bin_test_mock_algs[0].id, + bin_test_mock_algs[0].ver, + unpacked_mem_type, + payload_offset, + &payload->unpacked[i], + sizeof(payload->unpacked[i])); } } @@ -2295,53 +2299,55 @@ static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test) return cs_dsp_bin_test_common_init(test, dsp, 1); } +#define WMDR_PATCH_SHORT .add_patch = cs_dsp_mock_bin_add_patch + /* Parameterize on choice of XM or YM with a range of word offsets */ static const struct bin_test_param x_or_y_and_offset_param_cases[] = { - { .mem_type = WMFW_ADSP2_XM, .offset_words = 0 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 1 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 2 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 3 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 4 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 23 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 22 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 21 }, - { .mem_type = WMFW_ADSP2_XM, .offset_words = 20 }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_SHORT }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 0 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 1 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 2 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 3 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 4 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 23 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 22 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 21 }, - { .mem_type = WMFW_ADSP2_YM, .offset_words = 20 }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_SHORT }, }; /* Parameterize on ZM with a range of word offsets */ static const struct bin_test_param z_and_offset_param_cases[] = { - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21 }, - { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20 }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20, WMDR_PATCH_SHORT }, }; /* Parameterize on choice of packed XM or YM with a range of word offsets */ static const struct bin_test_param packed_x_or_y_and_offset_param_cases[] = { - { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 }, - { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4 }, - { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8 }, - { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12 }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT }, - { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 }, - { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4 }, - { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8 }, - { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12 }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT }, }; static void x_or_y_or_z_and_offset_param_desc(const struct bin_test_param *param, @@ -2366,8 +2372,8 @@ KUNIT_ARRAY_PARAM(packed_x_or_y_and_offset, /* Parameterize on choice of packed XM or YM */ static const struct bin_test_param packed_x_or_y_param_cases[] = { - { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 }, - { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, }; static void x_or_y_or_z_param_desc(const struct bin_test_param *param, @@ -2379,15 +2385,15 @@ static void x_or_y_or_z_param_desc(const struct bin_test_param *param, KUNIT_ARRAY_PARAM(packed_x_or_y, packed_x_or_y_param_cases, x_or_y_or_z_param_desc); static const struct bin_test_param offset_param_cases[] = { - { .offset_words = 0 }, - { .offset_words = 1 }, - { .offset_words = 2 }, - { .offset_words = 3 }, - { .offset_words = 4 }, - { .offset_words = 23 }, - { .offset_words = 22 }, - { .offset_words = 21 }, - { .offset_words = 20 }, + { .offset_words = 0, WMDR_PATCH_SHORT }, + { .offset_words = 1, WMDR_PATCH_SHORT }, + { .offset_words = 2, WMDR_PATCH_SHORT }, + { .offset_words = 3, WMDR_PATCH_SHORT }, + { .offset_words = 4, WMDR_PATCH_SHORT }, + { .offset_words = 23, WMDR_PATCH_SHORT }, + { .offset_words = 22, WMDR_PATCH_SHORT }, + { .offset_words = 21, WMDR_PATCH_SHORT }, + { .offset_words = 20, WMDR_PATCH_SHORT }, }; static void offset_param_desc(const struct bin_test_param *param, char *desc) @@ -2398,10 +2404,10 @@ static void offset_param_desc(const struct bin_test_param *param, char *desc) KUNIT_ARRAY_PARAM(offset, offset_param_cases, offset_param_desc); static const struct bin_test_param alg_param_cases[] = { - { .alg_idx = 0 }, - { .alg_idx = 1 }, - { .alg_idx = 2 }, - { .alg_idx = 3 }, + { .alg_idx = 0, WMDR_PATCH_SHORT }, + { .alg_idx = 1, WMDR_PATCH_SHORT }, + { .alg_idx = 2, WMDR_PATCH_SHORT }, + { .alg_idx = 3, WMDR_PATCH_SHORT }, }; static void alg_param_desc(const struct bin_test_param *param, char *desc) @@ -2415,15 +2421,15 @@ static void alg_param_desc(const struct bin_test_param *param, char *desc) KUNIT_ARRAY_PARAM(alg, alg_param_cases, alg_param_desc); static const struct bin_test_param x_or_y_and_alg_param_cases[] = { - { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0 }, - { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1 }, - { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2 }, - { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3 }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_SHORT }, - { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0 }, - { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1 }, - { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2 }, - { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3 }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_SHORT }, }; static void x_or_y_or_z_and_alg_param_desc(const struct bin_test_param *param, char *desc) @@ -2438,24 +2444,24 @@ static void x_or_y_or_z_and_alg_param_desc(const struct bin_test_param *param, c KUNIT_ARRAY_PARAM(x_or_y_and_alg, x_or_y_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc); static const struct bin_test_param z_and_alg_param_cases[] = { - { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0 }, - { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1 }, - { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2 }, - { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3 }, + { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3, WMDR_PATCH_SHORT }, }; KUNIT_ARRAY_PARAM(z_and_alg, z_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc); static const struct bin_test_param packed_x_or_y_and_alg_param_cases[] = { - { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0 }, - { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1 }, - { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2 }, - { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3 }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT }, - { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0 }, - { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1 }, - { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2 }, - { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3 }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT }, }; KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg, packed_x_or_y_and_alg_param_cases, From 9e6f4c5b2d3af58390cf554ada9591935c5ac774 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:07 +0000 Subject: [PATCH 124/341] firmware: cs_dsp: mock_bin: Pass offset32 to cs_dsp_mock_bin_add_raw_block() Add an argument to cs_dsp_mock_bin_add_raw_block() to pass a 32-bit offset, and change the type of the existing offset argument to u16. The cs_dsp_test_bin_error.c test uses cs_dsp_mock_bin_add_raw_block() so it needs corresponding updates to pass 0 as the 32-bit offset. Version 1 and 2 of the bin file format had a 16-bit offset on blocks and the sample rate field of the blocks was not used. Version 3 adds new block types that change the old sample rate field to be a 32-bit offset with the old offset currently unused. cs_dsp_mock_bin_add_raw_block() doesn't attempt to do any magic - its purpose is to create a raw block exactly as specified by the calling test code. So the test case can pass a value for both offset fields. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/test/cs_dsp_mock_bin.c | 10 ++++++---- .../firmware/cirrus/test/cs_dsp_test_bin_error.c | 14 +++++++------- include/linux/firmware/cirrus/cs_dsp_test_utils.h | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c index 3f8777ee4dc0..bc6b8651259c 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c @@ -56,13 +56,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS") * @alg_id: Algorithm ID. * @alg_ver: Algorithm version. * @type: Type of the block. - * @offset: Offset. + * @offset: 16-bit offset. + * @offset32: 32-bit offset (sample rate on V1 and V2 file formats). * @payload_data: Pointer to buffer containing the payload data. * @payload_len_bytes: Length of payload data in bytes. */ void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder, unsigned int alg_id, unsigned int alg_ver, - int type, unsigned int offset, + int type, u16 offset, u32 offset32, const void *payload_data, size_t payload_len_bytes) { struct wmfw_coeff_item *item; @@ -75,6 +76,7 @@ void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder, item = builder->write_p; item->offset = cpu_to_le16(offset); + item->offset32 = cpu_to_le32(offset32); item->type = cpu_to_le16(type); item->id = cpu_to_le32(alg_id); item->ver = cpu_to_le32(alg_ver << 8); @@ -104,7 +106,7 @@ static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *bui info = tmp; } - cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len); + cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, 0, info, info_len); kunit_kfree(builder->test_priv->test, tmp); } @@ -156,7 +158,7 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder, KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0); cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver, - mem_region, reg_addr_offset, + mem_region, (u16)reg_addr_offset, 0, payload_data, payload_len_bytes); } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS"); diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c index a7ec956d2724..fe0112dc3077 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c @@ -66,24 +66,24 @@ static void bin_load_with_unknown_blocks(struct kunit *test) cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - 0xf5, 0, + 0xf5, 0, 0, random_data, sizeof(random_data)); cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - 0xf500, 0, + 0xf500, 0, 0, random_data, sizeof(random_data)); cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - 0xc300, 0, + 0xc300, 0, 0, random_data, sizeof(random_data)); /* Add a single payload to be written to DSP memory */ cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - WMFW_ADSP2_YM, 0, + WMFW_ADSP2_YM, 0, 0, payload_data, payload_size_bytes); bin = cs_dsp_mock_bin_get_firmware(local->bin_builder); @@ -277,7 +277,7 @@ static void bin_too_short_for_block_header(struct kunit *test) cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - param->block_type, 0, + param->block_type, 0, 0, NULL, 0); bin = cs_dsp_mock_bin_get_firmware(local->bin_builder); @@ -309,7 +309,7 @@ static void bin_too_short_for_block_payload(struct kunit *test) cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - param->block_type, 0, + param->block_type, 0, 0, payload, sizeof(payload)); bin = cs_dsp_mock_bin_get_firmware(local->bin_builder); @@ -341,7 +341,7 @@ static void bin_block_payload_len_garbage(struct kunit *test) cs_dsp_mock_bin_add_raw_block(local->bin_builder, cs_dsp_bin_err_test_mock_algs[0].id, cs_dsp_bin_err_test_mock_algs[0].ver, - param->block_type, 0, + param->block_type, 0, 0, &payload, sizeof(payload)); bin = cs_dsp_mock_bin_get_firmware(local->bin_builder); diff --git a/include/linux/firmware/cirrus/cs_dsp_test_utils.h b/include/linux/firmware/cirrus/cs_dsp_test_utils.h index 1f97764fdfd7..877fa4a496dd 100644 --- a/include/linux/firmware/cirrus/cs_dsp_test_utils.h +++ b/include/linux/firmware/cirrus/cs_dsp_test_utils.h @@ -126,7 +126,7 @@ struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv, unsigned int fw_version); void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder, unsigned int alg_id, unsigned int alg_ver, - int type, unsigned int offset, + int type, u16 offset, u32 offset32, const void *payload_data, size_t payload_len_bytes); void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder, const char *info); From 880f1eb5b95ccf250f567927462a7d3fa8f2a727 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:08 +0000 Subject: [PATCH 125/341] firmware: cs_dsp: mock_bin: Add function to create long-offset patches Add cs_dsp_mock_bin_add_patch_off32(). This is the same as cs_dsp_mock_bin_add_patch() except that it puts the offset in the new 32-bit offset field and modifies the block type to indicate that it uses the long offset. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-6-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_mock_bin.c | 28 +++++++++++++++++++ .../linux/firmware/cirrus/cs_dsp_test_utils.h | 4 +++ 2 files changed, 32 insertions(+) diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c index bc6b8651259c..635e917e0516 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c @@ -163,6 +163,34 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder, } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS"); +/** + * cs_dsp_mock_bin_add_patch_off32() - Add a patch data block with 32-bit offset. + * + * @builder: Pointer to struct cs_dsp_mock_bin_builder. + * @alg_id: Algorithm ID for the patch. + * @alg_ver: Algorithm version for the patch. + * @mem_region: Memory region for the patch. + * @reg_addr_offset: Offset to start of data in register addresses. + * @payload_data: Pointer to buffer containing the payload data. + * @payload_len_bytes: Length of payload data in bytes. + */ +void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder, + unsigned int alg_id, unsigned int alg_ver, + int mem_region, unsigned int reg_addr_offset, + const void *payload_data, size_t payload_len_bytes) +{ + /* Payload length must be a multiple of 4 */ + KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0); + + /* Mark the block as using the 32-bit offset */ + mem_region |= 0xf400; + + cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver, + mem_region, 0, reg_addr_offset, + payload_data, payload_len_bytes); +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch_off32, "FW_CS_DSP_KUNIT_TEST_UTILS"); + /** * cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder. * diff --git a/include/linux/firmware/cirrus/cs_dsp_test_utils.h b/include/linux/firmware/cirrus/cs_dsp_test_utils.h index 877fa4a496dd..51e99f47e90e 100644 --- a/include/linux/firmware/cirrus/cs_dsp_test_utils.h +++ b/include/linux/firmware/cirrus/cs_dsp_test_utils.h @@ -136,6 +136,10 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder, unsigned int alg_id, unsigned int alg_ver, int mem_region, unsigned int reg_addr_offset, const void *payload_data, size_t payload_len_bytes); +void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder, + unsigned int alg_id, unsigned int alg_ver, + int mem_region, unsigned int reg_addr_offset, + const void *payload_data, size_t payload_len_bytes); struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder); struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv, From 6e60c6aa1e4b929f6fbb04dc9fa786aeaedf3693 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:09 +0000 Subject: [PATCH 126/341] firmware: cs_dsp: test: Increase size of XM and YM on Halo Core Increase the size of the XM and YM regions in the mock Halo Core register map for testing patch blocks with 32-bit offsets. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-7-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_mock_mem_maps.c | 8 ++++---- .../firmware/cirrus/test/cs_dsp_mock_regmap.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c index 95946fac5563..7fb404425fd6 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c @@ -23,10 +23,10 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS" /* List of sizes in bytes, for each entry above */ const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = { 0x5000, /* PM_PACKED */ - 0x6000, /* XM_PACKED */ - 0x47F4, /* YM_PACKED */ - 0x8000, /* XM_UNPACKED_24 */ - 0x5FF8, /* YM_UNPACKED_24 */ + 0x8fff4, /* XM_PACKED */ + 0x8fff4, /* YM_PACKED */ + 0xbfff8, /* XM_UNPACKED_24 */ + 0xbfff8, /* YM_UNPACKED_24 */ 0 /* terminator */ }; diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c index fb8e4a5d189a..5167305521cd 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c @@ -157,22 +157,22 @@ static const struct reg_default halo_register_defaults[] = { }; static const struct regmap_range halo_readable_registers[] = { - regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */ + regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */ regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */ regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */ - regmap_reg_range(0x2800000, 0x2807fff), /* XM */ + regmap_reg_range(0x2800000, 0x28bfff4), /* XM */ regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */ - regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */ - regmap_reg_range(0x3400000, 0x3405ff7), /* YM */ + regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */ + regmap_reg_range(0x3400000, 0x34bfff4), /* YM */ regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */ }; static const struct regmap_range halo_writeable_registers[] = { - regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */ - regmap_reg_range(0x2800000, 0x2807fff), /* XM */ + regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */ + regmap_reg_range(0x2800000, 0x28bfff4), /* XM */ regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */ - regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */ - regmap_reg_range(0x3400000, 0x3405ff7), /* YM */ + regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */ + regmap_reg_range(0x3400000, 0x34bfff4), /* YM */ regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */ }; From 7fecf0bf1202599920d929704a3787291fc068db Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:10 +0000 Subject: [PATCH 127/341] firmware: cs_dsp: test_bin: Run test cases on long-offset blocks Run the patch block test cases using the new long-offset block type. This adds a new set of parameterization that uses cs_dsp_mock_bin_add_patch_off32() to create the patch blocks in the test bin file. The test cases for Halo Core with V3 file format include another run of the tests with the new parameterization, so that the tests are run on the standard blocks and the long-offset blocks. This commit does not add any new cases for offsets > 0xffff. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-8-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_test_bin.c | 229 +++++++++++++++++- 1 file changed, 220 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c index 7f53fa9964c4..67af7da4f8c1 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c @@ -2300,6 +2300,7 @@ static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test) } #define WMDR_PATCH_SHORT .add_patch = cs_dsp_mock_bin_add_patch +#define WMDR_PATCH_LONG .add_patch = cs_dsp_mock_bin_add_patch_off32 /* Parameterize on choice of XM or YM with a range of word offsets */ static const struct bin_test_param x_or_y_and_offset_param_cases[] = { @@ -2324,6 +2325,28 @@ static const struct bin_test_param x_or_y_and_offset_param_cases[] = { { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = { + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_LONG }, + + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_LONG }, +}; + /* Parameterize on ZM with a range of word offsets */ static const struct bin_test_param z_and_offset_param_cases[] = { { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0, WMDR_PATCH_SHORT }, @@ -2350,18 +2373,35 @@ static const struct bin_test_param packed_x_or_y_and_offset_param_cases[] = { { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param packed_x_or_y_and_long_offset_param_cases[] = { + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_LONG }, + + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_LONG }, +}; + static void x_or_y_or_z_and_offset_param_desc(const struct bin_test_param *param, char *desc) { - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u", + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u %s", cs_dsp_mem_region_name(param->mem_type), - param->offset_words); + param->offset_words, + (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : ""); } KUNIT_ARRAY_PARAM(x_or_y_and_offset, x_or_y_and_offset_param_cases, x_or_y_or_z_and_offset_param_desc); +KUNIT_ARRAY_PARAM(x_or_y_and_long_offset, + x_or_y_and_long_offset_param_cases, + x_or_y_or_z_and_offset_param_desc); + KUNIT_ARRAY_PARAM(z_and_offset, z_and_offset_param_cases, x_or_y_or_z_and_offset_param_desc); @@ -2370,19 +2410,31 @@ KUNIT_ARRAY_PARAM(packed_x_or_y_and_offset, packed_x_or_y_and_offset_param_cases, x_or_y_or_z_and_offset_param_desc); +KUNIT_ARRAY_PARAM(packed_x_or_y_and_long_offset, + packed_x_or_y_and_long_offset_param_cases, + x_or_y_or_z_and_offset_param_desc); + /* Parameterize on choice of packed XM or YM */ static const struct bin_test_param packed_x_or_y_param_cases[] = { { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param packed_x_or_y_long_param_cases[] = { + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, +}; + static void x_or_y_or_z_param_desc(const struct bin_test_param *param, char *desc) { - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", cs_dsp_mem_region_name(param->mem_type)); + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %s", + cs_dsp_mem_region_name(param->mem_type), + (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : ""); } KUNIT_ARRAY_PARAM(packed_x_or_y, packed_x_or_y_param_cases, x_or_y_or_z_param_desc); +KUNIT_ARRAY_PARAM(packed_x_or_y_long, packed_x_or_y_long_param_cases, x_or_y_or_z_param_desc); static const struct bin_test_param offset_param_cases[] = { { .offset_words = 0, WMDR_PATCH_SHORT }, @@ -2396,12 +2448,27 @@ static const struct bin_test_param offset_param_cases[] = { { .offset_words = 20, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param long_offset_param_cases[] = { + { .offset_words = 0, WMDR_PATCH_LONG }, + { .offset_words = 1, WMDR_PATCH_LONG }, + { .offset_words = 2, WMDR_PATCH_LONG }, + { .offset_words = 3, WMDR_PATCH_LONG }, + { .offset_words = 4, WMDR_PATCH_LONG }, + { .offset_words = 23, WMDR_PATCH_LONG }, + { .offset_words = 22, WMDR_PATCH_LONG }, + { .offset_words = 21, WMDR_PATCH_LONG }, + { .offset_words = 20, WMDR_PATCH_LONG }, +}; + static void offset_param_desc(const struct bin_test_param *param, char *desc) { - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u", param->offset_words); + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u %s", + param->offset_words, + (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : ""); } KUNIT_ARRAY_PARAM(offset, offset_param_cases, offset_param_desc); +KUNIT_ARRAY_PARAM(long_offset, long_offset_param_cases, offset_param_desc); static const struct bin_test_param alg_param_cases[] = { { .alg_idx = 0, WMDR_PATCH_SHORT }, @@ -2410,15 +2477,24 @@ static const struct bin_test_param alg_param_cases[] = { { .alg_idx = 3, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param alg_long_param_cases[] = { + { .alg_idx = 0, WMDR_PATCH_LONG }, + { .alg_idx = 1, WMDR_PATCH_LONG }, + { .alg_idx = 2, WMDR_PATCH_LONG }, + { .alg_idx = 3, WMDR_PATCH_LONG }, +}; + static void alg_param_desc(const struct bin_test_param *param, char *desc) { WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs)); - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x)", - param->alg_idx, bin_test_mock_algs[param->alg_idx].id); + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x) %s", + param->alg_idx, bin_test_mock_algs[param->alg_idx].id, + (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : ""); } KUNIT_ARRAY_PARAM(alg, alg_param_cases, alg_param_desc); +KUNIT_ARRAY_PARAM(alg_long, alg_long_param_cases, alg_param_desc); static const struct bin_test_param x_or_y_and_alg_param_cases[] = { { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_SHORT }, @@ -2432,16 +2508,31 @@ static const struct bin_test_param x_or_y_and_alg_param_cases[] = { { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param x_or_y_and_alg_long_param_cases[] = { + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_LONG }, + + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_LONG }, +}; + static void x_or_y_or_z_and_alg_param_desc(const struct bin_test_param *param, char *desc) { WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs)); - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x)", + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x) %s", cs_dsp_mem_region_name(param->mem_type), - param->alg_idx, bin_test_mock_algs[param->alg_idx].id); + param->alg_idx, bin_test_mock_algs[param->alg_idx].id, + (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : ""); } KUNIT_ARRAY_PARAM(x_or_y_and_alg, x_or_y_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc); +KUNIT_ARRAY_PARAM(x_or_y_and_alg_long, x_or_y_and_alg_long_param_cases, + x_or_y_or_z_and_alg_param_desc); static const struct bin_test_param z_and_alg_param_cases[] = { { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0, WMDR_PATCH_SHORT }, @@ -2464,9 +2555,24 @@ static const struct bin_test_param packed_x_or_y_and_alg_param_cases[] = { { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT }, }; +static const struct bin_test_param packed_x_or_y_and_alg_long_param_cases[] = { + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG }, + + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG }, +}; + KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg, packed_x_or_y_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc); +KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg_long, packed_x_or_y_and_alg_long_param_cases, + x_or_y_or_z_and_alg_param_desc); + static struct kunit_case cs_dsp_bin_test_cases_halo[] = { /* Unpacked memory */ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params), @@ -2522,6 +2628,111 @@ static struct kunit_case cs_dsp_bin_test_cases_halo[] = { { } /* terminator */ }; +static struct kunit_case cs_dsp_bin_test_cases_halo_wmdr3[] = { + /* Unpacked memory */ + KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered, x_or_y_and_offset_gen_params), + + /* Packed memory tests */ + KUNIT_CASE_PARAM(bin_patch_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered, + packed_x_or_y_and_alg_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered, + packed_x_or_y_and_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random, + packed_x_or_y_gen_params), + + /* Unpacked memory with long-offset blocks */ + KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_long_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_long_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered, + x_or_y_and_long_offset_gen_params), + + /* Packed memory tests with long offset blocks */ + KUNIT_CASE_PARAM(bin_patch_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_long_gen_params), + KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered, + packed_x_or_y_and_alg_long_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered, + packed_x_or_y_and_long_offset_gen_params), + KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random, + packed_x_or_y_long_gen_params), + + KUNIT_CASE(bin_patch_name_and_info), + + { } /* terminator */ +}; + static struct kunit_case cs_dsp_bin_test_cases_adsp2[] = { /* XM and YM */ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params), @@ -2559,7 +2770,7 @@ static struct kunit_suite cs_dsp_bin_test_halo = { static struct kunit_suite cs_dsp_bin_test_halo_wmdr3 = { .name = "cs_dsp_bin_halo_wmdr_v3", .init = cs_dsp_bin_test_halo_wmdr3_init, - .test_cases = cs_dsp_bin_test_cases_halo, + .test_cases = cs_dsp_bin_test_cases_halo_wmdr3, }; static struct kunit_suite cs_dsp_bin_test_adsp2_32bit = { From 211243b69533e968cc6f0259fb80ffee02fbe0ca Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 31 Dec 2025 17:27:11 +0000 Subject: [PATCH 128/341] firmware: cs_dsp: test_bin: Add tests for offsets > 0xffff Add test cases for using the new long-offset block types to patch memory that is >0xffff from the algorithm base. This is just adding entries to the parameter data that have larger offset values. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20251231172711.450024-9-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../firmware/cirrus/test/cs_dsp_test_bin.c | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c index 67af7da4f8c1..99148ea22df3 100644 --- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c +++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c @@ -2326,6 +2326,7 @@ static const struct bin_test_param x_or_y_and_offset_param_cases[] = { }; static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = { + /* Offset < 0xffff in long-offset block type */ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_LONG }, @@ -2336,6 +2337,7 @@ static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = { { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_LONG }, + /* Offset < 0xffff in long-offset block type */ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_LONG }, @@ -2345,6 +2347,28 @@ static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = { { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_LONG }, { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_LONG }, + + /* Offset > 0xffff in long-offset block type */ + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10001, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10002, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10003, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10004, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f003, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f002, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f001, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f000, WMDR_PATCH_LONG }, + + /* Offset > 0xffff in long-offset block type */ + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10001, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10002, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10003, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10004, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f003, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f002, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f001, WMDR_PATCH_LONG }, + { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f000, WMDR_PATCH_LONG }, }; /* Parameterize on ZM with a range of word offsets */ @@ -2374,15 +2398,31 @@ static const struct bin_test_param packed_x_or_y_and_offset_param_cases[] = { }; static const struct bin_test_param packed_x_or_y_and_long_offset_param_cases[] = { + /* Offset < 0xffff in long-offset block type */ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_LONG }, + /* Offset < 0xffff in long-offset block type */ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_LONG }, { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_LONG }, + + /* Offset > 0xffff in long-offset block type */ + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG }, + + /* Offset > 0xffff in long-offset block type */ + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG }, + { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG }, }; static void x_or_y_or_z_and_offset_param_desc(const struct bin_test_param *param, @@ -2449,6 +2489,7 @@ static const struct bin_test_param offset_param_cases[] = { }; static const struct bin_test_param long_offset_param_cases[] = { + /* Offset < 0xffff in long-offset block type */ { .offset_words = 0, WMDR_PATCH_LONG }, { .offset_words = 1, WMDR_PATCH_LONG }, { .offset_words = 2, WMDR_PATCH_LONG }, @@ -2458,6 +2499,17 @@ static const struct bin_test_param long_offset_param_cases[] = { { .offset_words = 22, WMDR_PATCH_LONG }, { .offset_words = 21, WMDR_PATCH_LONG }, { .offset_words = 20, WMDR_PATCH_LONG }, + + /* Offset > 0xffff in long-offset block type */ + { .offset_words = 0x10000, WMDR_PATCH_LONG }, + { .offset_words = 0x10001, WMDR_PATCH_LONG }, + { .offset_words = 0x10002, WMDR_PATCH_LONG }, + { .offset_words = 0x10003, WMDR_PATCH_LONG }, + { .offset_words = 0x10004, WMDR_PATCH_LONG }, + { .offset_words = 0x2f000, WMDR_PATCH_LONG }, + { .offset_words = 0x2f001, WMDR_PATCH_LONG }, + { .offset_words = 0x2f002, WMDR_PATCH_LONG }, + { .offset_words = 0x2f003, WMDR_PATCH_LONG }, }; static void offset_param_desc(const struct bin_test_param *param, char *desc) From 163eb876a275e6139fcc81122c5d34fa521db25a Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 5 Jan 2026 17:15:44 +0800 Subject: [PATCH 129/341] ASoC: dt-bindings: ES8389: Add property about power supply Add VDDA supply and VDDD supply Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260105091548.4196-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/everest,es8389.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/everest,es8389.yaml b/Documentation/devicetree/bindings/sound/everest,es8389.yaml index a673df485ab3..75ce0bc48904 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8389.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8389.yaml @@ -30,10 +30,20 @@ properties: "#sound-dai-cells": const: 0 + vdda-supply: + description: + Analogue power supply. + + vddd-supply: + description: + Interface power supply. + required: - compatible - reg - "#sound-dai-cells" + - vddd-supply + - vdda-supply additionalProperties: false @@ -46,5 +56,7 @@ examples: compatible = "everest,es8389"; reg = <0x10>; #sound-dai-cells = <0>; + vddd-supply = <&vdd3v3>; + vdda-supply = <&vdd3v3>; }; }; From 59e447ca608bf869fed23a9bba8fcb513852f02b Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 5 Jan 2026 17:15:45 +0800 Subject: [PATCH 130/341] ASoC: codecs: ES8389: Add members related to power supply Added the members `avdd-supply` and `dvdd-supply` to enable the driver to get the power supply status. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260105091548.4196-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 27 ++++++++++++++++++++++++++- sound/soc/codecs/es8389.h | 8 ++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index a84d79f9d3d1..9400c5ca4e3a 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -31,13 +32,20 @@ struct es8389_private { struct regmap *regmap; struct clk *mclk; + struct regulator_bulk_data core_supply[2]; unsigned int sysclk; int mastermode; u8 mclk_src; + u8 vddd; enum snd_soc_bias_level bias_level; }; +static const char * const es8389_core_supplies[] = { + "vddd", + "vdda", +}; + static bool es8389_volatile_register(struct device *dev, unsigned int reg) { @@ -818,7 +826,7 @@ static int es8389_resume(struct snd_soc_component *component) static int es8389_probe(struct snd_soc_component *component) { - int ret; + int ret, i; struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src); @@ -827,6 +835,14 @@ static int es8389_probe(struct snd_soc_component *component) es8389->mclk_src = ES8389_MCLK_SOURCE; } + for (i = 0; i < ARRAY_SIZE(es8389_core_supplies); i++) + es8389->core_supply[i].supply = es8389_core_supplies[i]; + ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(es8389_core_supplies), es8389->core_supply); + if (ret) { + dev_err(component->dev, "Failed to request core supplies %d\n", ret); + return ret; + } + es8389->mclk = devm_clk_get(component->dev, "mclk"); if (IS_ERR(es8389->mclk)) return dev_err_probe(component->dev, PTR_ERR(es8389->mclk), @@ -841,6 +857,13 @@ static int es8389_probe(struct snd_soc_component *component) return ret; } + ret = regulator_bulk_enable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply); + if (ret) { + dev_err(component->dev, "Failed to enable core supplies: %d\n", ret); + clk_disable_unprepare(es8389->mclk); + return ret; + } + es8389_init(component); es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); @@ -907,6 +930,8 @@ static void es8389_i2c_shutdown(struct i2c_client *i2c) regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08); regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1); regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00); + + regulator_bulk_disable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply); } static int es8389_i2c_probe(struct i2c_client *i2c_client) diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index 123d1e4b2d53..d21e72f876a6 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -137,4 +137,12 @@ #define ES8389_STATE_ON (13 << 0) #define ES8389_STATE_STANDBY (7 << 0) +enum ES8389_supplies { + ES8389_SUPPLY_VD = 0, + ES8389_SUPPLY_VA, +}; + +#define ES8389_3V3 1 +#define ES8389_1V8 0 + #endif From b35340e997e0809e045692949a88adf56b0d1ea0 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 5 Jan 2026 17:15:46 +0800 Subject: [PATCH 131/341] ASoC: codecs: ES8389: Adjust wakeup configuration Update wake-up configuration to ensure the codec works properly. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260105091548.4196-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 9400c5ca4e3a..fb650ab2dd17 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -629,10 +629,6 @@ static int es8389_set_bias_level(struct snd_soc_component *component, regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4); regmap_write(es8389->regmap, ES8389_RESET, 0x01); regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); - usleep_range(70000, 72000); - regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00); break; case SND_SOC_BIAS_PREPARE: break; @@ -663,6 +659,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) { struct snd_soc_component *component = dai->component; struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + unsigned int regv; if (mute) { if (direction == SNDRV_PCM_STREAM_PLAYBACK) { @@ -673,10 +670,22 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) 0x03, 0x03); } } else { + regmap_read(es8389->regmap, ES8389_CSM_STATE1, ®v); + if (regv != ES8389_STATE_ON) { + regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9); + regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4); + regmap_write(es8389->regmap, ES8389_RESET, 0x01); + regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3); + } + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 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_FORMAT_MUTE, 0x03, 0x00); } From e5077facc770a9348d653dd9f1dbafb747a87e38 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 5 Jan 2026 17:15:47 +0800 Subject: [PATCH 132/341] ASoC: codecs: ES8389: Add members about the version Execute different configurations based on version number in order to support different versions of es8389. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260105091548.4196-5-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index fb650ab2dd17..e16e68785088 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -38,6 +38,7 @@ struct es8389_private { u8 mclk_src; u8 vddd; + int version; enum snd_soc_bias_level bias_level; }; @@ -681,6 +682,10 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) } if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (!es8389->version) { + regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00); + usleep_range(70000, 72000); + } regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x00); } else { @@ -730,7 +735,10 @@ static struct snd_soc_dai_driver es8389_dai = { static void es8389_init(struct snd_soc_component *component) { struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + unsigned int reg; + regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); + es8389->version = reg; regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); regmap_write(es8389->regmap, ES8389_RESET, 0x7E); regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); From 4c5e6d5b31bc623d89185d551681ab91cfd037c9 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 5 Jan 2026 17:15:48 +0800 Subject: [PATCH 133/341] ASoC: codecs: ES8389: Update clock configuration To ensure better performance of the codec, different configurations will be employed based on power supply conditions. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260105091548.4196-6-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 194 ++++++++++++++++++++++---------------- 1 file changed, 113 insertions(+), 81 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index e16e68785088..8d418cae371a 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -376,95 +376,110 @@ struct _coeff_div { u8 Reg0x16; u8 Reg0x18; u8 Reg0x19; + u8 dvdd_vol; + u8 dmic_sel; }; /* codec hifi mclk clock divider coefficients */ static const struct _coeff_div coeff_div[] = { - {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, - {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, - {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14}, - {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B}, - {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, - {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25}, - {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, - {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, - {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, - {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F}, - - {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, - {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, - {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, + {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0}, + {50, 400000, 8000, 0x00, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1}, + {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0}, + {600, 4800000, 8000, 0x02, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1}, + {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {2400, 19200000, 8000, 0x05, 0x14, 0x01, 0xC9, 0x00, 0xD2, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1}, + {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0}, + {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2}, + {3250, 26000000, 8000, 0x40, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1}, + {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0}, + {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, + {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}, }; -static inline int get_coeff(int mclk, int rate) +static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) { int i; + u8 dmic_det, vddd_det; for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { - if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) - return i; + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) { + vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01; + dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01; + vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01; + dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01; + + if (vddd_det && dmic_det) + return i; + } } + return -EINVAL; } @@ -546,8 +561,9 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); - int coeff; - u8 state = 0; + int coeff, ret; + u8 dmic_enable, state = 0; + unsigned int regv; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -578,7 +594,23 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); } - coeff = get_coeff(es8389->sysclk, params_rate(params)); + regmap_read(es8389->regmap, ES8389_DMIC_EN, ®v); + dmic_enable = regv >> 7 & 0x01; + + ret = regulator_get_voltage(es8389->core_supply[ES8389_SUPPLY_VD].consumer); + switch (ret) { + case 1800000 ... 2000000: + es8389->vddd = ES8389_1V8; + break; + case 2500000 ... 3300000: + es8389->vddd = ES8389_3V3; + break; + default: + es8389->vddd = ES8389_3V3; + break; + } + + coeff = get_coeff(es8389->vddd, dmic_enable, es8389->sysclk, params_rate(params)); if (coeff >= 0) { regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04); regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05); From ee69f55eb183efb43da14cdad72910b1b1cc2932 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 31 Dec 2025 10:35:44 +0800 Subject: [PATCH 134/341] spi: export of_find_spi_controller_by_node() Some devices are primarily described on another bus (e.g. I2C) but also have an additional SPI connection that serves as a transport for firmware loading. Export of_find_spi_controller_by_node() so drivers can obtain the SPI controller referenced by a DT phandle. Signed-off-by: Oder Chiou Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/0e572a00aa305e588357162d400ba9472ce56dd3.1767148150.git.oder_chiou@realtek.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 ++- include/linux/spi/spi.h | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index e25df9990f82..ecb5281b04a2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -4771,7 +4771,7 @@ static struct spi_device *of_find_spi_device_by_node(struct device_node *node) } /* The spi controllers are not using spi_bus, so we find it with another way */ -static struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) +struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) { struct device *dev; @@ -4784,6 +4784,7 @@ static struct spi_controller *of_find_spi_controller_by_node(struct device_node /* Reference got in class_find_device */ return container_of(dev, struct spi_controller, dev); } +EXPORT_SYMBOL_GPL(of_find_spi_controller_by_node); static int of_spi_notify(struct notifier_block *nb, unsigned long action, void *arg) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cb2c2df31089..e6fdaf02386c 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -882,6 +882,15 @@ extern int devm_spi_register_controller(struct device *dev, struct spi_controller *ctlr); extern void spi_unregister_controller(struct spi_controller *ctlr); +#if IS_ENABLED(CONFIG_OF_DYNAMIC) +extern struct spi_controller *of_find_spi_controller_by_node(struct device_node *node); +#else +static inline struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) +{ + return NULL; +} +#endif + #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SPI_MASTER) extern struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev); extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, From 037f8d896688bf3384eb6bf34e24e8fbc9f6e02d Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 31 Dec 2025 10:36:49 +0800 Subject: [PATCH 135/341] spi: change of_find_spi_controller_by_node() gating to CONFIG_OF Currently, the helper of_find_spi_controller_by_node() is gated under CONFIG_OF_DYNAMIC. This prevents drivers from using it in all CONFIG_OF configurations. This patch moves the gating to CONFIG_OF, keeping the inline fallback returning NULL when Device Tree support is disabled. Signed-off-by: Oder Chiou Link: https://patch.msgid.link/6d8ae977d9f4726ea23ad5382638750593f9a2e4.1767148150.git.oder_chiou@realtek.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 20 +++++++++++--------- include/linux/spi/spi.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ecb5281b04a2..2badacc7a91c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -4761,15 +4761,7 @@ EXPORT_SYMBOL_GPL(spi_write_then_read); /*-------------------------------------------------------------------------*/ -#if IS_ENABLED(CONFIG_OF_DYNAMIC) -/* Must call put_device() when done with returned spi_device device */ -static struct spi_device *of_find_spi_device_by_node(struct device_node *node) -{ - struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node); - - return dev ? to_spi_device(dev) : NULL; -} - +#if IS_ENABLED(CONFIG_OF) /* The spi controllers are not using spi_bus, so we find it with another way */ struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) { @@ -4785,6 +4777,16 @@ struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) return container_of(dev, struct spi_controller, dev); } EXPORT_SYMBOL_GPL(of_find_spi_controller_by_node); +#endif + +#if IS_ENABLED(CONFIG_OF_DYNAMIC) +/* Must call put_device() when done with returned spi_device device */ +static struct spi_device *of_find_spi_device_by_node(struct device_node *node) +{ + struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node); + + return dev ? to_spi_device(dev) : NULL; +} static int of_spi_notify(struct notifier_block *nb, unsigned long action, void *arg) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e6fdaf02386c..8bc616b00343 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -882,7 +882,7 @@ extern int devm_spi_register_controller(struct device *dev, struct spi_controller *ctlr); extern void spi_unregister_controller(struct spi_controller *ctlr); -#if IS_ENABLED(CONFIG_OF_DYNAMIC) +#if IS_ENABLED(CONFIG_OF) extern struct spi_controller *of_find_spi_controller_by_node(struct device_node *node); #else static inline struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) From af4c0b951b18a8af73fa8541fabf1bf2484bba9b Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 31 Dec 2025 10:37:01 +0800 Subject: [PATCH 136/341] ASoC: dt-bindings: realtek,rt5575: add support for ALC5575 Audio codec with I2S, I2C and SPI. Signed-off-by: Oder Chiou Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/cad38383a8f4c7235158779c270fee7f61bf6cfe.1767148150.git.oder_chiou@realtek.com Signed-off-by: Mark Brown --- .../bindings/sound/realtek,rt5575.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5575.yaml diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml new file mode 100644 index 000000000000..981ebc39b195 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5575.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/realtek,rt5575.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ALC5575 audio CODEC + +maintainers: + - Oder Chiou + +description: + The device supports both I2C and SPI. I2C is mandatory, while SPI is + optional depending on the hardware configuration. SPI is used for + firmware loading if present. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: realtek,rt5575 + + reg: + maxItems: 1 + + spi-parent: + description: + Optional phandle reference to the SPI controller used for firmware + loading. The argument specifies the chip select. + $ref: /schemas/types.yaml#/definitions/phandle-array + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + # I2C-only node + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@57 { + compatible = "realtek,rt5575"; + reg = <0x57>; + }; + }; + + # I2C + optional SPI node + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@57 { + compatible = "realtek,rt5575"; + reg = <0x57>; + spi-parent = <&spi0 0>; /* chip-select 0 */ + }; + }; From 420739112e95c9bb286b4e87875706925970abd3 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 31 Dec 2025 10:37:11 +0800 Subject: [PATCH 137/341] ASoC: rt5575: Add the codec driver for the ALC5575 The ALC5575 integrates an audio DSP that typically loads its firmware from an external flash via its own SPI host interface. In certain hardware configurations, the firmware can alternatively be loaded through the SPI client interface. The driver provides basic mute and volume control functions. When the SPI client interface is enabled, firmware loading is handled by the SPI driver. Signed-off-by: Oder Chiou Link: https://patch.msgid.link/17c36d07af44ffb1d600977955da95852f8d60f3.1767148150.git.oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 3 + sound/soc/codecs/rt5575-spi.c | 118 ++++++++++++ sound/soc/codecs/rt5575-spi.h | 27 +++ sound/soc/codecs/rt5575.c | 352 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5575.h | 58 ++++++ 6 files changed, 568 insertions(+) create mode 100644 sound/soc/codecs/rt5575-spi.c create mode 100644 sound/soc/codecs/rt5575-spi.h create mode 100644 sound/soc/codecs/rt5575.c create mode 100644 sound/soc/codecs/rt5575.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061791e61907..14f3d09e7117 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -212,6 +212,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1305 imply SND_SOC_RT1308 imply SND_SOC_RT5514 + imply SND_SOC_RT5575 imply SND_SOC_RT5616 imply SND_SOC_RT5631 imply SND_SOC_RT5640 @@ -1783,6 +1784,15 @@ config SND_SOC_RT5514_SPI_BUILTIN bool # force RT5514_SPI to be built-in to avoid link errors default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m +config SND_SOC_RT5575 + tristate "Realtek ALC5575 Codec - I2C" + depends on I2C + +config SND_SOC_RT5575_SPI + tristate "Realtek ALC5575 Codec - SPI" + depends on SPI_MASTER && I2C + depends on SND_SOC_RT5575 + config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d687d4f74363..a6406bc907a9 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -253,6 +253,8 @@ snd-soc-rt286-y := rt286.o snd-soc-rt298-y := rt298.o snd-soc-rt5514-y := rt5514.o snd-soc-rt5514-spi-y := rt5514-spi.o +snd-soc-rt5575-y := rt5575.o +snd-soc-rt5575-$(CONFIG_SND_SOC_RT5575_SPI) += rt5575-spi.o snd-soc-rt5616-y := rt5616.o snd-soc-rt5631-y := rt5631.o snd-soc-rt5640-y := rt5640.o @@ -686,6 +688,7 @@ obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o +obj-$(CONFIG_SND_SOC_RT5575) += snd-soc-rt5575.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c new file mode 100644 index 000000000000..9a349965435b --- /dev/null +++ b/sound/soc/codecs/rt5575-spi.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5575-spi.c -- ALC5575 SPI driver + * + * Copyright(c) 2025 Realtek Semiconductor Corp. + * + */ + +#include +#include +#include + +#include "rt5575-spi.h" + +#define RT5575_SPI_CMD_BURST_WRITE 5 +#define RT5575_SPI_BUF_LEN 240 + +struct rt5575_spi_burst_write { + u8 cmd; + u32 addr; + u8 data[RT5575_SPI_BUF_LEN]; + u8 dummy; +} __packed; + +struct spi_device *rt5575_spi_get_device(struct device *dev) +{ + struct spi_device *spi; + struct spi_controller *ctlr; + struct device_node *spi_np; + u32 cs; + + spi_np = of_parse_phandle(dev->of_node, "spi-parent", 0); + if (!spi_np) { + dev_err(dev, "Failed to get spi-parent phandle\n"); + return NULL; + } + + if (of_property_read_u32_index(dev->of_node, "spi-parent", 1, &cs)) + cs = 0; + + ctlr = of_find_spi_controller_by_node(spi_np); + of_node_put(spi_np); + if (!ctlr) { + dev_err(dev, "Failed to get spi_controller\n"); + return NULL; + } + + if (cs >= ctlr->num_chipselect) { + dev_err(dev, "Chip select has wrong number %d\n", cs); + spi_controller_put(ctlr); + return NULL; + } + + spi = spi_new_device(ctlr, &(struct spi_board_info){ + .modalias = "rt5575", + .chip_select = cs, + .max_speed_hz = 10000000, + }); + + spi_controller_put(ctlr); + return spi; +} + +/** + * rt5575_spi_burst_write - Write data to SPI by rt5575 address. + * @spi: SPI device. + * @addr: Start address. + * @txbuf: Data buffer for writing. + * @len: Data length. + * + */ +static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *txbuf, size_t len) +{ + struct rt5575_spi_burst_write buf = { + .cmd = RT5575_SPI_CMD_BURST_WRITE, + }; + unsigned int end, offset = 0; + + while (offset < len) { + if (offset + RT5575_SPI_BUF_LEN <= len) + end = RT5575_SPI_BUF_LEN; + else + end = len % RT5575_SPI_BUF_LEN; + + buf.addr = cpu_to_le32(addr + offset); + memcpy(&buf.data, &txbuf[offset], end); + spi_write(spi, &buf, sizeof(buf)); + + offset += RT5575_SPI_BUF_LEN; + } +} + +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", + "realtek/rt5575/rt5575_fw2.bin", + "realtek/rt5575/rt5575_fw3.bin", + "realtek/rt5575/rt5575_fw4.bin", + }; + static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; + + for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { + ret = request_firmware(&firmware, fw_path[i], dev); + if (ret) { + dev_err(dev, "Request firmware failure: %d\n", ret); + return ret; + } + + rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); + release_firmware(firmware); + } + + return 0; +} diff --git a/sound/soc/codecs/rt5575-spi.h b/sound/soc/codecs/rt5575-spi.h new file mode 100644 index 000000000000..084638820b6f --- /dev/null +++ b/sound/soc/codecs/rt5575-spi.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5575-spi.h -- ALC5575 SPI driver + * + * Copyright(c) 2025 Realtek Semiconductor Corp. + * + */ + +#ifndef __RT5575_SPI_H__ +#define __RT5575_SPI_H__ + +#if IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI) +struct spi_device *rt5575_spi_get_device(struct device *dev); +int rt5575_spi_fw_load(struct spi_device *spi); +#else +static inline struct spi_device *rt5575_spi_get_device(struct device *dev) +{ + return NULL; +} + +static inline int rt5575_spi_fw_load(struct spi_device *spi) +{ + return -EINVAL; +} +#endif + +#endif /* __RT5575_SPI_H__ */ diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c new file mode 100644 index 000000000000..c5525ad195ee --- /dev/null +++ b/sound/soc/codecs/rt5575.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5575.c -- ALC5575 ALSA SoC audio component driver + * + * Copyright(c) 2025 Realtek Semiconductor Corp. + * + */ + +#include +#include +#include + +#include "rt5575.h" +#include "rt5575-spi.h" + +static bool rt5575_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5575_BOOT: + case RT5575_ID: + case RT5575_ID_1: + case RT5575_MIXL_VOL: + case RT5575_MIXR_VOL: + case RT5575_PROMPT_VOL: + case RT5575_SPK01_VOL: + case RT5575_SPK23_VOL: + case RT5575_MIC1_VOL: + case RT5575_MIC2_VOL: + case RT5575_WNC_CTRL: + case RT5575_MODE_CTRL: + case RT5575_I2S_RATE_CTRL: + case RT5575_SLEEP_CTRL: + case RT5575_ALG_BYPASS_CTRL: + case RT5575_PINMUX_CTRL_2: + case RT5575_GPIO_CTRL_1: + case RT5575_DSP_BUS_CTRL: + case RT5575_SW_INT: + case RT5575_DSP_BOOT_ERR: + case RT5575_DSP_READY: + case RT5575_DSP_CMD_ADDR: + case RT5575_EFUSE_DATA_2: + case RT5575_EFUSE_DATA_3: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(ob_tlv, -9525, 75, 0); + +static const struct snd_kcontrol_new rt5575_snd_controls[] = { + SOC_DOUBLE("Speaker CH-01 Playback Switch", RT5575_SPK01_VOL, 31, 15, 1, 1), + SOC_DOUBLE_TLV("Speaker CH-01 Playback Volume", RT5575_SPK01_VOL, 17, 1, 167, 0, ob_tlv), + SOC_DOUBLE("Speaker CH-23 Playback Switch", RT5575_SPK23_VOL, 31, 15, 1, 1), + SOC_DOUBLE_TLV("Speaker CH-23 Playback Volume", RT5575_SPK23_VOL, 17, 1, 167, 0, ob_tlv), + SOC_DOUBLE("Mic1 Capture Switch", RT5575_MIC1_VOL, 31, 15, 1, 1), + SOC_DOUBLE_TLV("Mic1 Capture Volume", RT5575_MIC1_VOL, 17, 1, 167, 0, ob_tlv), + SOC_DOUBLE("Mic2 Capture Switch", RT5575_MIC2_VOL, 31, 15, 1, 1), + SOC_DOUBLE_TLV("Mic2 Capture Volume", RT5575_MIC2_VOL, 17, 1, 167, 0, ob_tlv), + SOC_DOUBLE_R("Mix Playback Switch", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 31, 1, 1), + SOC_DOUBLE_R_TLV("Mix Playback Volume", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 1, 127, 0, + ob_tlv), + SOC_DOUBLE("Prompt Playback Switch", RT5575_PROMPT_VOL, 31, 15, 1, 1), + SOC_DOUBLE_TLV("Prompt Playback Volume", RT5575_PROMPT_VOL, 17, 1, 167, 0, ob_tlv), +}; + +static const struct snd_soc_dapm_widget rt5575_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_INPUT("INPUT"), + SND_SOC_DAPM_OUTPUT("OUTPUT"), +}; + +static const struct snd_soc_dapm_route rt5575_dapm_routes[] = { + { "AIF1TX", NULL, "INPUT" }, + { "AIF2TX", NULL, "INPUT" }, + { "AIF3TX", NULL, "INPUT" }, + { "AIF4TX", NULL, "INPUT" }, + { "OUTPUT", NULL, "AIF1RX" }, + { "OUTPUT", NULL, "AIF2RX" }, + { "OUTPUT", NULL, "AIF3RX" }, + { "OUTPUT", NULL, "AIF4RX" }, +}; + +static long long rt5575_get_priv_id(struct rt5575_priv *rt5575) +{ + int priv_id_low, priv_id_high; + + regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0xa0000000); + regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_2, &priv_id_low); + regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_3, &priv_id_high); + regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0); + + return ((long long)priv_id_high << 32) | (long long)priv_id_low; +} + +static int rt5575_probe(struct snd_soc_component *component) +{ + struct rt5575_priv *rt5575 = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + + rt5575->component = component; + + dev_info(dev, "Private ID: %llx\n", rt5575_get_priv_id(rt5575)); + + return 0; +} + +#define RT5575_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5575_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver rt5575_dai[] = { + { + .name = "rt5575-aif1", + .id = RT5575_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + }, + { + .name = "rt5575-aif2", + .id = RT5575_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + }, + { + .name = "rt5575-aif3", + .id = RT5575_AIF3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + }, + { + .name = "rt5575-aif4", + .id = RT5575_AIF4, + .playback = { + .stream_name = "AIF4 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + .capture = { + .stream_name = "AIF4 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5575_STEREO_RATES, + .formats = RT5575_FORMATS, + }, + }, +}; + +static const struct snd_soc_component_driver rt5575_soc_component_dev = { + .probe = rt5575_probe, + .controls = rt5575_snd_controls, + .num_controls = ARRAY_SIZE(rt5575_snd_controls), + .dapm_widgets = rt5575_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5575_dapm_widgets), + .dapm_routes = rt5575_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5575_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5575_dsp_regmap = { + .name = "dsp", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 2, +}; + +static int rt5575_i2c_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5575_priv *rt5575 = i2c_get_clientdata(client); + + return regmap_read(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val); +} + +static int rt5575_i2c_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5575_priv *rt5575 = i2c_get_clientdata(client); + + return regmap_write(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val); +} + +static const struct regmap_config rt5575_regmap = { + .reg_bits = 16, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xfffc, + .readable_reg = rt5575_readable_register, + .reg_read = rt5575_i2c_read, + .reg_write = rt5575_i2c_write, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt5575_fw_load_by_spi(struct rt5575_priv *rt5575) +{ + struct i2c_client *i2c = rt5575->i2c; + struct spi_device *spi; + struct device *dev = &i2c->dev; + int ret; + + spi = rt5575_spi_get_device(dev); + if (!spi) { + dev_err(dev, "Failed to get spi_device\n"); + return -ENODEV; + } + + regmap_write(rt5575->dsp_regmap, 0xfafafafa, 0x00000004); + regmap_write(rt5575->dsp_regmap, 0x18008064, 0x00000000); + regmap_write(rt5575->dsp_regmap, 0x18008068, 0x0002ffff); + + ret = rt5575_spi_fw_load(spi); + if (ret) { + dev_err(dev, "Load firmware failure: %d\n", ret); + return -ENODEV; + } + + regmap_write(rt5575->dsp_regmap, 0x18000000, 0x00000000); + regmap_update_bits(rt5575->regmap, RT5575_SW_INT, 1, 1); + + regmap_read_poll_timeout(rt5575->regmap, RT5575_SW_INT, ret, !ret, 100000, 10000000); + if (ret) { + dev_err(dev, "Run firmware failure: %d\n", ret); + return -ENODEV; + } + + return 0; +} + +static int rt5575_i2c_probe(struct i2c_client *i2c) +{ + struct rt5575_priv *rt5575; + int ret, val, boot; + struct device *dev = &i2c->dev; + + rt5575 = devm_kzalloc(dev, sizeof(struct rt5575_priv), GFP_KERNEL); + if (!rt5575) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5575); + + rt5575->i2c = i2c; + + rt5575->dsp_regmap = devm_regmap_init_i2c(i2c, &rt5575_dsp_regmap); + if (IS_ERR(rt5575->dsp_regmap)) { + ret = PTR_ERR(rt5575->dsp_regmap); + dev_err(dev, "Failed to allocate DSP register map: %d\n", ret); + return ret; + } + + rt5575->regmap = devm_regmap_init(dev, NULL, i2c, &rt5575_regmap); + if (IS_ERR(rt5575->regmap)) { + ret = PTR_ERR(rt5575->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + regmap_read(rt5575->regmap, RT5575_ID, &val); + if (val != RT5575_DEVICE_ID) { + dev_err(dev, "Device with ID register %08x is not rt5575\n", val); + return -ENODEV; + } + + regmap_read(rt5575->regmap, RT5575_BOOT, &boot); + if ((boot & RT5575_BOOT_MASK) == RT5575_BOOT_SPI) { + if (!IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI)) { + dev_err(dev, "Please enable CONFIG_SND_SOC_RT5575_SPI\n"); + return -ENODEV; + } + + if (rt5575_fw_load_by_spi(rt5575)) + return -ENODEV; + } + + return devm_snd_soc_register_component(dev, &rt5575_soc_component_dev, rt5575_dai, + ARRAY_SIZE(rt5575_dai)); +} + +static const struct i2c_device_id rt5575_i2c_id[] = { + { "rt5575" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5575_i2c_id); + +static const struct of_device_id rt5575_of_match[] = { + { .compatible = "realtek,rt5575" }, + { } +}; +MODULE_DEVICE_TABLE(of, rt5575_of_match); + +static struct i2c_driver rt5575_i2c_driver = { + .driver = { + .name = "rt5575", + .owner = THIS_MODULE, + .of_match_table = rt5575_of_match, + }, + .probe = rt5575_i2c_probe, + .id_table = rt5575_i2c_id, +}; +module_i2c_driver(rt5575_i2c_driver); + +MODULE_DESCRIPTION("ASoC ALC5575 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt5575.h b/sound/soc/codecs/rt5575.h new file mode 100644 index 000000000000..752a3c8f5aa9 --- /dev/null +++ b/sound/soc/codecs/rt5575.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5575.h -- ALC5575 ALSA SoC audio driver + * + * Copyright(c) 2025 Realtek Semiconductor Corp. + * + */ + +#ifndef __RT5575_H__ +#define __RT5575_H__ + +#define RT5575_DEVICE_ID 0x10ec5575 +#define RT5575_DSP_MAPPING 0x18000000 + +#define RT5575_BOOT 0x8004 +#define RT5575_ID 0x8008 +#define RT5575_ID_1 0x800c +#define RT5575_MIXL_VOL 0x8a14 +#define RT5575_MIXR_VOL 0x8a18 +#define RT5575_PROMPT_VOL 0x8a84 +#define RT5575_SPK01_VOL 0x8a88 +#define RT5575_SPK23_VOL 0x8a8c +#define RT5575_MIC1_VOL 0x8a98 +#define RT5575_MIC2_VOL 0x8a9c +#define RT5575_WNC_CTRL 0x80ec +#define RT5575_MODE_CTRL 0x80f0 +#define RT5575_I2S_RATE_CTRL 0x80f4 +#define RT5575_SLEEP_CTRL 0x80f8 +#define RT5575_ALG_BYPASS_CTRL 0x80fc +#define RT5575_PINMUX_CTRL_2 0x81a4 +#define RT5575_GPIO_CTRL_1 0x8208 +#define RT5575_DSP_BUS_CTRL 0x880c +#define RT5575_SW_INT 0x0018 +#define RT5575_DSP_BOOT_ERR 0x8e14 +#define RT5575_DSP_READY 0x8e24 +#define RT5575_DSP_CMD_ADDR 0x8e28 +#define RT5575_EFUSE_DATA_2 0xc638 +#define RT5575_EFUSE_DATA_3 0xc63c +#define RT5575_EFUSE_PID 0xc660 + +#define RT5575_BOOT_MASK 0x3 +#define RT5575_BOOT_SPI 0x0 + +enum { + RT5575_AIF1, + RT5575_AIF2, + RT5575_AIF3, + RT5575_AIF4, + RT5575_AIFS, +}; + +struct rt5575_priv { + struct i2c_client *i2c; + struct snd_soc_component *component; + struct regmap *dsp_regmap, *regmap; +}; + +#endif /* __RT5575_H__ */ From 52ddc0106c77ff0eacf07b309833ae6e6a4e8587 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Wed, 31 Dec 2025 16:45:54 +0800 Subject: [PATCH 138/341] ASoC: es8328: Remove duplicate DAPM routes The DAPM routes for "Left Line Mux" and "Right Line Mux" are defined twice in es8328_dapm_routes[]. The redundant entries appear after the "Mic Bias" route and duplicate the definitions found earlier in the array. Remove the duplicate entries to clean up the code. Tested on Rockchip RK3588 with ES8328 codec. Verified that removing the duplicate routes does not alter the DAPM graph or the mixer controls. Both 'tinymix' output and DAPM widget lists remain identical to the baseline. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20251231084554.265916-1-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 1e11175cfbbb..bd74e896d602 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -406,16 +406,6 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { { "Mic Bias", NULL, "Mic Bias Gen" }, - { "Left Line Mux", "Line 1", "LINPUT1" }, - { "Left Line Mux", "Line 2", "LINPUT2" }, - { "Left Line Mux", "PGA", "Left PGA Mux" }, - { "Left Line Mux", "Differential", "Differential Mux" }, - - { "Right Line Mux", "Line 1", "RINPUT1" }, - { "Right Line Mux", "Line 2", "RINPUT2" }, - { "Right Line Mux", "PGA", "Right PGA Mux" }, - { "Right Line Mux", "Differential", "Differential Mux" }, - { "Left Mixer", NULL, "Left DAC" }, { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, { "Left Mixer", "Right Playback Switch", "Right DAC" }, From 70237853edf0a69773a7370eb74ea2a44dfe3050 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:15 +0200 Subject: [PATCH 139/341] ASoC: nau8821: Fixup nau8821_enable_jack_detect() The nau8821_enable_jack_detect() function was supposed to allow enabling or disabling jack events reporting. However, once enabled, any subsequent invocation would fail and the following splat is shown: [ 3136.996771] Hardware name: Valve Jupiter/Jupiter, BIOS F7A0131 01/30/2024 [ 3136.996773] Workqueue: events_unbound deferred_probe_work_func [ 3136.996780] Call Trace: [ 3136.996782] [ 3136.996787] dump_stack_lvl+0x6e/0xa0 [ 3136.996796] __setup_irq.cold+0x9c/0xce [ 3136.996803] ? __pfx_irq_default_primary_handler+0x10/0x10 [ 3136.996812] ? __pfx_nau8821_interrupt+0x10/0x10 [snd_soc_nau8821] [ 3136.996825] request_threaded_irq+0xd9/0x160 [ 3136.996853] devm_request_threaded_irq+0x71/0xd0 [ 3136.996859] ? __pfx_nau8821_interrupt+0x10/0x10 [snd_soc_nau8821] [ 3136.996882] nau8821_enable_jack_detect+0xa5/0xc0 [snd_soc_nau8821] [ 3136.996901] acp5x_8821_init+0x8d/0xa0 [snd_soc_acp5x_mach] [ 3136.996917] snd_soc_link_init+0x25/0x50 [snd_soc_core] [ 3136.996958] snd_soc_bind_card+0x615/0xd00 [snd_soc_core] [ 3136.997026] snd_soc_register_card+0x1b2/0x1c0 [snd_soc_core] [ 3136.997064] devm_snd_soc_register_card+0x47/0x90 [snd_soc_core] [ 3136.997108] acp5x_probe+0x72/0xb0 [snd_soc_acp5x_mach] [...] [ 3136.997508] nau8821 i2c-NVTN2020:00: Cannot request irq 58 (-16) Introduce jdet_active flag to driver data structure and use it to provide one-time initialization of the jack detection work queue and related interrupt line. Note this is also a prerequisite for additional fixes around module unloading and suspend handling. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-1-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 5 +++++ sound/soc/codecs/nau8821.h | 1 + 2 files changed, 6 insertions(+) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 3beb3c44dc2c..2d25a182f4ab 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1655,8 +1655,13 @@ int nau8821_enable_jack_detect(struct snd_soc_component *component, int ret; nau8821->jack = jack; + + if (nau8821->jdet_active) + return 0; + /* Initiate jack detection work queue */ INIT_DELAYED_WORK(&nau8821->jdet_work, nau8821_jdet_work); + nau8821->jdet_active = true; ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL, nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h index 88602923780d..f9d7cd8cbd21 100644 --- a/sound/soc/codecs/nau8821.h +++ b/sound/soc/codecs/nau8821.h @@ -562,6 +562,7 @@ struct nau8821 { struct snd_soc_dapm_context *dapm; struct snd_soc_jack *jack; struct delayed_work jdet_work; + bool jdet_active; int irq; int clk_id; int micbias_voltage; From dbd3fd05cddfdeec1e49b0a66269881c09eebd17 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:16 +0200 Subject: [PATCH 140/341] ASoC: nau8821: Cancel delayed work on component remove Attempting to unload the driver while a jack detection work is pending would likely crash the kernel when it is eventually scheduled for execution: [ 1984.896308] BUG: unable to handle page fault for address: ffffffffc10c2a20 [...] [ 1984.896388] Hardware name: Valve Jupiter/Jupiter, BIOS F7A0131 01/30/2024 [ 1984.896396] Workqueue: events nau8821_jdet_work [snd_soc_nau8821] [ 1984.896414] RIP: 0010:__mutex_lock+0x9f/0x11d0 [...] [ 1984.896504] Call Trace: [ 1984.896511] [ 1984.896524] ? snd_soc_dapm_disable_pin+0x26/0x60 [snd_soc_core] [ 1984.896572] ? snd_soc_dapm_disable_pin+0x26/0x60 [snd_soc_core] [ 1984.896596] snd_soc_dapm_disable_pin+0x26/0x60 [snd_soc_core] [ 1984.896622] nau8821_jdet_work+0xeb/0x1e0 [snd_soc_nau8821] [ 1984.896636] process_one_work+0x211/0x590 [ 1984.896649] ? srso_return_thunk+0x5/0x5f [ 1984.896670] worker_thread+0x1cd/0x3a0 Cancel unscheduled jdet_work or wait for its execution to finish before the component driver gets removed. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Fixes: ee70bacef1c6 ("ASoC: nau8821: Avoid unnecessary blocking in IRQ handler") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-2-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 2d25a182f4ab..2e2714b47501 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1264,6 +1264,14 @@ static int nau8821_component_probe(struct snd_soc_component *component) return 0; } +static void nau8821_component_remove(struct snd_soc_component *component) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (nau8821->jdet_active) + cancel_delayed_work_sync(&nau8821->jdet_work); +}; + /** * nau8821_calc_fll_param - Calculate FLL parameters. * @fll_in: external clock provided to codec. @@ -1621,6 +1629,7 @@ static int __maybe_unused nau8821_resume(struct snd_soc_component *component) static const struct snd_soc_component_driver nau8821_component_driver = { .probe = nau8821_component_probe, + .remove = nau8821_component_remove, .set_sysclk = nau8821_set_sysclk, .set_pll = nau8821_set_fll, .set_bias_level = nau8821_set_bias_level, From 7786b10688ac0ebeaff655923cbb2c7d34a98995 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:17 +0200 Subject: [PATCH 141/341] ASoC: nau8821: Cancel pending work before suspend A jack detection work that is unscheduled or in progress while executing the suspend handler could trigger a race condition. Ensure state consistency by cancelling any pending work or wait for its execution to complete before processing the suspend. Since driver (re)enables both insert and eject interrupts on resume, there is no risk to miss the related jack events. Therefore, flush_delayed_work() is not required here. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Fixes: ee70bacef1c6 ("ASoC: nau8821: Avoid unnecessary blocking in IRQ handler") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-3-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 2e2714b47501..58d2d5e77c8f 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1605,6 +1605,10 @@ static int __maybe_unused nau8821_suspend(struct snd_soc_component *component) if (nau8821->irq) disable_irq(nau8821->irq); + + if (nau8821->jdet_active) + cancel_delayed_work_sync(&nau8821->jdet_work); + snd_soc_dapm_force_bias_level(nau8821->dapm, SND_SOC_BIAS_OFF); /* Power down codec power; don't support button wakeup */ snd_soc_dapm_disable_pin(nau8821->dapm, "MICBIAS"); From 2a3dc1bcc75e357e4e68c92ff9b9e7f6e775c3e7 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:18 +0200 Subject: [PATCH 142/341] ASoC: nau8821: Drop superfluous return statement Simplify error handling in nau8821_enable_jack_detect() by removing the unnecessary return after logging the request irq message. Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-4-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 58d2d5e77c8f..6dffda69f1ff 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1679,11 +1679,9 @@ int nau8821_enable_jack_detect(struct snd_soc_component *component, ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL, nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "nau8821", nau8821); - if (ret) { + if (ret) dev_err(nau8821->dev, "Cannot request irq %d (%d)\n", nau8821->irq, ret); - return ret; - } return ret; } From fd843051b8cad777f69cd09c797b4a2d81d48f4c Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:19 +0200 Subject: [PATCH 143/341] ASoC: nau8821: Simplify conditional in nau8821_get_osr() Get rid of the unnecessary branch to address checkpatch complaint: WARNING: else is not generally useful after a break or return Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-5-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 6dffda69f1ff..10e8c220c0e0 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -806,16 +806,20 @@ nau8821_get_osr(struct nau8821 *nau8821, int stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr); osr &= NAU8821_DAC_OVERSAMPLE_MASK; + if (osr >= ARRAY_SIZE(osr_dac_sel)) return NULL; + return &osr_dac_sel[osr]; - } else { - regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr); - osr &= NAU8821_ADC_SYNC_DOWN_MASK; - if (osr >= ARRAY_SIZE(osr_adc_sel)) - return NULL; - return &osr_adc_sel[osr]; } + + regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr); + osr &= NAU8821_ADC_SYNC_DOWN_MASK; + + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return NULL; + + return &osr_adc_sel[osr]; } static int nau8821_dai_startup(struct snd_pcm_substream *substream, From 42ca16e3bace1e76803876358ea908ce9d87e0d4 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:20 +0200 Subject: [PATCH 144/341] ASoC: nau8821: Drop unneeded braces in nau8821_hw_params() Get rid of the superfluous braces and silent checkpatch complaint: WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-6-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 10e8c220c0e0..181214fa792d 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -872,15 +872,16 @@ static int nau8821_hw_params(struct snd_pcm_substream *substream, if (ctrl_val & NAU8821_I2S_MS_MASTER) { /* get the bclk and fs ratio */ bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs; + if (bclk_fs <= 32) clk_div = 3; else if (bclk_fs <= 64) clk_div = 2; else if (bclk_fs <= 128) clk_div = 1; - else { + else return -EINVAL; - } + regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK, (clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div); From 9bf0bd7bdea6c402007ffb784dd0c0f704aa2310 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 31 Dec 2025 22:04:21 +0200 Subject: [PATCH 145/341] ASoC: nau8821: Sort #include directives Make sure #include directives are ordered alphabetically. Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251231-nau8821-cleanup-v1-7-6b0b76cbbb64@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 181214fa792d..ffb526de0021 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -11,10 +11,10 @@ #include #include #include -#include #include -#include +#include #include +#include #include #include #include @@ -24,6 +24,7 @@ #include #include #include + #include "nau8821.h" #define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0) From 04b61513dfe40f80f0dcc795003637b510522b3c Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Tue, 30 Dec 2025 15:34:29 +0100 Subject: [PATCH 146/341] ASoC: SDCA: Replace use of system_wq with system_dfl_wq This patch continues the effort to refactor workqueue APIs, which has begun with the changes introducing new workqueues and a new alloc_workqueue flag: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") The point of the refactoring is to eventually alter the default behavior of workqueues to become unbound by default so that their workload placement is optimized by the scheduler. Before that to happen after a careful review and conversion of each individual case, workqueue users must be converted to the better named new workqueues with no intended behaviour changes: system_wq -> system_percpu_wq system_unbound_wq -> system_dfl_wq This specific workload has no benefits being per-cpu, so system_wq has been replaced with system_dfl_wq (the unbound workqueue). This way the old obsolete workqueues (system_wq, system_unbound_wq) can be removed in the future. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251230143429.179643-1-marco.crivellari@suse.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_ump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c index 8aba3ff16872..a86bb28c6d0a 100644 --- a/sound/soc/sdca/sdca_ump.c +++ b/sound/soc/sdca/sdca_ump.c @@ -257,6 +257,6 @@ void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_u if (!timeout_us) return; - queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us)); + queue_delayed_work(system_dfl_wq, work, usecs_to_jiffies(timeout_us)); } EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA"); From 0585c53b21541cd6b17ad5ab41b371a0d52e358c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 6 Jan 2026 16:08:18 -0700 Subject: [PATCH 147/341] ALSA: pcm: Revert bufs move in snd_pcm_xfern_frames_ioctl() When building with clang older than 17 targeting architectures that use asm goto for their get_user() and put_user(), such as arm64, after commit f3d233daf011 ("ALSA: pcm: Relax __free() variable declarations"), there are bogus errors around skipping over a variable declared with the cleanup attribute: sound/core/pcm_native.c:3308:6: error: cannot jump from this asm goto statement to one of its possible targets if (put_user(result, &_xfern->result)) ^ ... arch/arm64/include/asm/uaccess.h:298:2: note: expanded from macro '__put_mem_asm' asm goto( ^ sound/core/pcm_native.c:3295:6: note: possible target of asm goto statement if (put_user(0, &_xfern->result)) ^ ... sound/core/pcm_native.c:3300:8: note: jump exits scope of variable with __attribute__((cleanup)) void *bufs __free(kfree) = ^ clang-17 fixed a bug in clang's jump scope checker [1] where all labels in a function were checked as valid targets for all asm goto instances in a function, regardless of whether they were actual targets in a paricular asm goto's provided list of labels. To workaround this, revert the change done to snd_pcm_xfern_frames_ioctl() by commit f3d233daf011 ("ALSA: pcm: Relax __free() variable declarations") to avoid a variable declared with cleanup from existing between multiple uses of asm goto. There are no other uses of cleanup in this function so there should be low risk from moving this variable back to the top of the function. Link: https://github.com/ClangBuiltLinux/linux/issues/1886 [1] Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512190802.i4Jzbcsl-lkp@intel.com/ Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260106-pcm_native-revert-var-move-free-for-old-clang-v1-1-06a03693423d@kernel.org Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4352c0a40f8d..30ace7112c27 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3286,6 +3286,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, { struct snd_xfern xfern; struct snd_pcm_runtime *runtime = substream->runtime; + void *bufs __free(kfree) = NULL; snd_pcm_sframes_t result; if (runtime->state == SNDRV_PCM_STATE_OPEN) @@ -3297,8 +3298,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - void *bufs __free(kfree) = - memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); + bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); if (IS_ERR(bufs)) return PTR_ERR(bufs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) From b47ce586300b3c2b6650f4c7ac5c0f59c6bf6f4b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 6 Jan 2026 10:59:51 -0800 Subject: [PATCH 148/341] ALSA: hda - fix function names & missing function parameter Use the correct function names and add a function parameter description to avoid kernel-doc warnings: hda_jack.h:97: warning: Function parameter or struct member 'cb' not described in 'snd_hda_jack_detect_enable_callback' hda_jack.h:97: warning: expecting prototype for snd_hda_jack_detect_enable(). Prototype was for snd_hda_jack_detect_enable_callback() instead hda_local.h:441: warning: expecting prototype for _snd_hda_set_pin_ctl(). Prototype was for snd_hda_set_pin_ctl() instead Fixes: cdd03cedc5b5 ("ALSA: hda - Introduce snd_hda_set_pin_ctl*() helper functions") Fixes: 5204a05d70d9 ("ALSA: hda - Add DP-MST jack support") Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260106185951.2179242-1-rdunlap@infradead.org Signed-off-by: Takashi Iwai --- sound/hda/common/hda_jack.h | 4 ++-- sound/hda/common/hda_local.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h index ff7d289c034b..e9b9970c59ed 100644 --- a/sound/hda/common/hda_jack.h +++ b/sound/hda/common/hda_jack.h @@ -82,10 +82,10 @@ snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id, hda_jack_callback_fn func); /** - * snd_hda_jack_detect_enable - enable the jack-detection + * snd_hda_jack_detect_enable_callback - enable the jack-detection * @codec: the HDA codec * @nid: pin NID to enable - * @func: callback function to register + * @cb: callback function to register * * In the case of error, the return value will be a pointer embedded with * errno. Check and handle the return value appropriately with standard diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index a7e53277a0fe..ab423f1cef54 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -424,7 +424,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached); /** - * _snd_hda_set_pin_ctl - Set a pin-control value safely + * snd_hda_set_pin_ctl - Set a pin-control value safely * @codec: the codec instance * @pin: the pin NID to set the control * @val: the pin-control value (AC_PINCTL_* bits) From 22a4776a9ce50aa47f602d28f53ba9d613a38f49 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 7 Jan 2026 05:18:08 +0000 Subject: [PATCH 149/341] ASoC: codecs: es8375: remove unnecessary format check It already have default for error case (A), no need to have SND_SOC_DAIFMT_RIGHT_J for error (B). Remove it. static int es8375_set_dai_fmt(...) ... switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: (B) return -EINVAL; ... default: (A) return -EINVAL; } ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87eco20zn3.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8375.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index 36b0ebdce514..0b9406e93c0e 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -397,8 +397,6 @@ static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_I2S: codeciface &= 0xFC; break; - case SND_SOC_DAIFMT_RIGHT_J: - return -EINVAL; case SND_SOC_DAIFMT_LEFT_J: codeciface &= 0xFC; codeciface |= 0x01; From 0cd9bf6a6d9a1861087236cc5c275b3bea83cfdd Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 8 Jan 2026 17:44:19 +0200 Subject: [PATCH 150/341] ASoC: codecs: da7213: Move comma operator at the end of the line Move the comma operator to the end of the line to comply with the coding style. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260108154419.3580562-1-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 0a2b50cdea95..19f69a523f22 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -79,8 +79,8 @@ static const char * const da7213_audio_hpf_corner_txt[] = { }; static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner, - DA7213_DAC_FILTERS1 - , DA7213_AUDIO_HPF_CORNER_SHIFT, + DA7213_DAC_FILTERS1, + DA7213_AUDIO_HPF_CORNER_SHIFT, da7213_audio_hpf_corner_txt); static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner, From e76f8c269e3558d02e19515cd3cc470e42ddbd36 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 9 Jan 2026 16:02:05 +0100 Subject: [PATCH 151/341] ASoC: codecs: aw88261: Remove AW88261_I2C_NAME macro Replace the macro by its simple string value, to make the code less unnecessarily complicated. Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20260109-aw88261-dt-v1-1-45840c7632a3@fairphone.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 4 ++-- sound/soc/codecs/aw88261.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 8f37bfb974ae..ceea0c8c60b1 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1264,14 +1264,14 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88261_i2c_id[] = { - { AW88261_I2C_NAME }, + { "aw88261" }, { } }; MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); static struct i2c_driver aw88261_i2c_driver = { .driver = { - .name = AW88261_I2C_NAME, + .name = "aw88261", }, .probe = aw88261_i2c_probe, .id_table = aw88261_i2c_id, diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 734d0f93ced9..1fee589608d6 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -370,8 +370,6 @@ #define AW88261_START_RETRIES (5) #define AW88261_START_WORK_DELAY_MS (0) -#define AW88261_I2C_NAME "aw88261" - #define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000) #define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ From b9198ce5c6dfee19b9662dda95ba559af9cdf53f Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 9 Jan 2026 16:02:06 +0100 Subject: [PATCH 152/341] ASoC: codecs: aw88261: Add devicetree support Add the compatible "awinic,aw88261" so that module autoloading will work based on the compatible from devicetree. Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20260109-aw88261-dt-v1-2-45840c7632a3@fairphone.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index ceea0c8c60b1..810c90f5e783 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1269,9 +1269,16 @@ static const struct i2c_device_id aw88261_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); +static const struct of_device_id aw88261_of_table[] = { + { .compatible = "awinic,aw88261" }, + { } +}; +MODULE_DEVICE_TABLE(of, aw88261_of_table); + static struct i2c_driver aw88261_i2c_driver = { .driver = { .name = "aw88261", + .of_match_table = aw88261_of_table, }, .probe = aw88261_i2c_probe, .id_table = aw88261_i2c_id, From 35bffbe49dfd3395c65d76f14d773a1225f3489f Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Thu, 8 Jan 2026 15:44:40 -0600 Subject: [PATCH 153/341] ASoC: dt-bindings: Convert realtek,rt5651 to DT schema Convert the Realtek RT5661 codec binding to DT schema format. Add missing clocks/clock-names for MCLK which is in use already. Also add the standard "#sound-dai-cells" property. Signed-off-by: Rob Herring (Arm) Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260108214443.1127685-1-robh@kernel.org Signed-off-by: Mark Brown --- .../bindings/sound/realtek,rt5651.yaml | 100 ++++++++++++++++++ .../devicetree/bindings/sound/rt5651.txt | 63 ----------- 2 files changed, 100 insertions(+), 63 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5651.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5651.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml new file mode 100644 index 000000000000..dc4f2eef7cf9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5651.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek RT5651 audio CODEC + +maintainers: + - Bard Liao + +description: > + This device supports I2C only. + + Pins on the device (for linking into audio routes) for RT5651: + + * DMIC L1 + * DMIC R1 + * IN1P + * IN2P + * IN2N + * IN3P + * HPOL + * HPOR + * LOUTL + * LOUTR + * PDML + * PDMR + +allOf: + - $ref: /schemas/sound/dai-common.yaml# + +properties: + compatible: + const: realtek,rt5651 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: mclk + + '#sound-dai-cells': + const: 0 + + realtek,in2-differential: + type: boolean + description: Indicate MIC2 input are differential, rather than single-ended. + + realtek,dmic-en: + type: boolean + description: Indicates DMIC is used. + + realtek,jack-detect-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Select jack-detect input pin. + enum: [1, 2, 3] + + realtek,jack-detect-not-inverted: + type: boolean + description: + Normal jack-detect switches give an inverted (active-low) signal. Set this + bool in the rare case you've a jack-detect switch which is not inverted. + + realtek,over-current-threshold-microamp: + description: Micbias over-current detection threshold in µA. + enum: [600, 1500, 2000] + + realtek,over-current-scale-factor: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + Micbias over-current detection scale factor: + + 0: scale current by 0.5 + 1: scale current by 0.75 + 2: scale current by 1.0 + 3: scale current by 1.5 + enum: [0, 1, 2, 3] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@1a { + compatible = "realtek,rt5651"; + reg = <0x1a>; + realtek,dmic-en; + realtek,in2-differential; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5651.txt b/Documentation/devicetree/bindings/sound/rt5651.txt deleted file mode 100644 index 56e736a1cba9..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5651.txt +++ /dev/null @@ -1,63 +0,0 @@ -RT5651 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : "realtek,rt5651". - -- reg : The I2C address of the device. - -Optional properties: - -- realtek,in2-differential - Boolean. Indicate MIC2 input are differential, rather than single-ended. - -- realtek,dmic-en - Boolean. true if dmic is used. - -- realtek,jack-detect-source - u32. Valid values: - 1: Use JD1_1 pin for jack-detect - 2: Use JD1_2 pin for jack-detect - 3: Use JD2 pin for jack-detect - -- realtek,jack-detect-not-inverted - bool. Normal jack-detect switches give an inverted (active-low) signal, - set this bool in the rare case you've a jack-detect switch which is not - inverted. - -- realtek,over-current-threshold-microamp - u32, micbias over-current detection threshold in µA, valid values are - 600, 1500 and 2000µA. - -- realtek,over-current-scale-factor - u32, micbias over-current detection scale-factor, valid values are: - 0: Scale current by 0.5 - 1: Scale current by 0.75 - 2: Scale current by 1.0 - 3: Scale current by 1.5 - -Pins on the device (for linking into audio routes) for RT5651: - - * DMIC L1 - * DMIC R1 - * IN1P - * IN2P - * IN2N - * IN3P - * HPOL - * HPOR - * LOUTL - * LOUTR - * PDML - * PDMR - -Example: - -rt5651: codec@1a { - compatible = "realtek,rt5651"; - reg = <0x1a>; - realtek,dmic-en = "true"; - realtek,in2-diff = "false"; -}; From 77157cb45c66bd652a08a360693fcced558c5ef9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Jan 2026 23:54:15 +0000 Subject: [PATCH 154/341] ASoC: codecs: rt1320-sdw: convert to snd_soc_dapm_xxx() This patch converts below functions. snd_soc_component_get_bias_level() -> snd_soc_dapm_get_bias_level() snd_soc_component_get_dapm() -> snd_soc_component_to_dapm() Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87344f3bko.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 0d54ed754bd2..e37b4cb87fbe 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1246,7 +1246,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); int ret; if (!rt1320->hw_init) @@ -1258,7 +1258,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, rt1320->cali_done = false; snd_soc_dapm_mutex_lock(dapm); - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF && ucontrol->value.integer.value[0]) { rt1320_calibrate(rt1320); } @@ -1582,8 +1582,7 @@ struct rt1320_dspfwheader { short crc; }; - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(rt1320->component); + 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; @@ -2388,7 +2387,7 @@ static int rt1320_r0_load_mode_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); int ret; if (!rt1320->hw_init) @@ -2403,7 +2402,7 @@ static int rt1320_r0_load_mode_put(struct snd_kcontrol *kcontrol, return ret; snd_soc_dapm_mutex_lock(dapm); - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { rt1320->r0_l_reg = ucontrol->value.integer.value[0]; rt1320->r0_r_reg = ucontrol->value.integer.value[1]; rt1320_calc_r0(rt1320); @@ -2447,6 +2446,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); int ret; if (!rt1320->hw_init) @@ -2456,7 +2456,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (ret < 0 && ret != -EACCES) return ret; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF && ucontrol->value.integer.value[0]) rt1320_dspfw_load_code(rt1320); @@ -2481,6 +2481,7 @@ static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); int ret; if (!rt1320->hw_init) @@ -2490,7 +2491,7 @@ static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol, if (ret < 0 && ret != -EACCES) return ret; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF && ucontrol->value.integer.value[0] && rt1320->fw_load_done) rt1320_rae_load(rt1320); @@ -2516,7 +2517,7 @@ static int rt1320_r0_temperature_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(rt1320->component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); int ret; if (!rt1320->hw_init) @@ -2527,7 +2528,7 @@ static int rt1320_r0_temperature_put(struct snd_kcontrol *kcontrol, return ret; snd_soc_dapm_mutex_lock(dapm); - if ((snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) && + if ((snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) && ucontrol->value.integer.value[0] && ucontrol->value.integer.value[1]) rt1320_t0_load(rt1320, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]); snd_soc_dapm_mutex_unlock(dapm); From 5c19da34df029fdc29fec1bedf210af7d2c4fccf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:09:58 +0200 Subject: [PATCH 155/341] ASoC: SOF: Use guard()/scoped_guard() for mutex locks where it makes sense Replace the manual mutex lock/unlock pairs with guard()/scoped_guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 5 ++-- sound/soc/sof/ipc3-topology.c | 6 ++--- sound/soc/sof/ipc3.c | 8 ++---- sound/soc/sof/ipc4-mtrace.c | 24 +++++++----------- sound/soc/sof/ipc4-pcm.c | 3 +-- sound/soc/sof/ipc4-topology.c | 5 +--- sound/soc/sof/ipc4.c | 8 ++---- sound/soc/sof/ops.h | 10 +++----- sound/soc/sof/sof-audio.c | 46 +++++++++++++---------------------- sound/soc/sof/sof-client.c | 40 +++++++++--------------------- 10 files changed, 51 insertions(+), 104 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 3fb8d3e9dc6a..3fdeb07bafa3 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -225,9 +225,8 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev) return; /* disable sending of ipc's */ - mutex_lock(&ipc->tx_mutex); - ipc->disable_ipc_tx = true; - mutex_unlock(&ipc->tx_mutex); + scoped_guard(mutex, &ipc->tx_mutex) + ipc->disable_ipc_tx = true; if (ipc->ops->exit) ipc->ops->exit(sdev); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index f449362a2905..743f42fb26c0 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2427,9 +2427,9 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_ /* Do not free widgets for static pipelines with FW older than SOF2.2 */ if (!verify && !swidget->dynamic_pipeline_widget && SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { - mutex_lock(&swidget->setup_mutex); - swidget->use_count = 0; - mutex_unlock(&swidget->setup_mutex); + scoped_guard(mutex, &swidget->setup_mutex) + swidget->use_count = 0; + if (swidget->spipe) swidget->spipe->complete = 0; continue; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 4a194a705ace..85bb22bbe18d 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -378,7 +378,7 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ } /* Serialise IPC TX */ - mutex_lock(&ipc->tx_mutex); + guard(mutex)(&ipc->tx_mutex); ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); @@ -405,8 +405,6 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ } } - mutex_unlock(&ipc->tx_mutex); - return ret; } @@ -477,7 +475,7 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da memcpy(cdata_chunk, cdata, hdr_bytes); /* Serialise IPC TX */ - mutex_lock(&sdev->ipc->tx_mutex); + guard(mutex)(&ipc->tx_mutex); /* copy the payload data in a loop */ for (i = 0; i < num_msg; i++) { @@ -511,8 +509,6 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes); } - mutex_unlock(&sdev->ipc->tx_mutex); - kfree(cdata_chunk); return ret; diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c index aa5b78604db6..cfd999b42458 100644 --- a/sound/soc/sof/ipc4-mtrace.c +++ b/sound/soc/sof/ipc4-mtrace.c @@ -118,22 +118,19 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file) struct sof_mtrace_core_data *core_data = inode->i_private; int ret; - mutex_lock(&core_data->buffer_lock); + guard(mutex)(&core_data->buffer_lock); - if (core_data->log_buffer) { - ret = -EBUSY; - goto out; - } + if (core_data->log_buffer) + return -EBUSY; ret = debugfs_file_get(file->f_path.dentry); if (unlikely(ret)) - goto out; + return ret; core_data->log_buffer = kmalloc(SOF_IPC4_DEBUG_SLOT_SIZE, GFP_KERNEL); if (!core_data->log_buffer) { debugfs_file_put(file->f_path.dentry); - ret = -ENOMEM; - goto out; + return -ENOMEM; } ret = simple_open(inode, file); @@ -142,9 +139,6 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file) debugfs_file_put(file->f_path.dentry); } -out: - mutex_unlock(&core_data->buffer_lock); - return ret; } @@ -281,10 +275,10 @@ static int sof_ipc4_mtrace_dfs_release(struct inode *inode, struct file *file) debugfs_file_put(file->f_path.dentry); - mutex_lock(&core_data->buffer_lock); - kfree(core_data->log_buffer); - core_data->log_buffer = NULL; - mutex_unlock(&core_data->buffer_lock); + scoped_guard(mutex, &core_data->buffer_lock) { + kfree(core_data->log_buffer); + core_data->log_buffer = NULL; + } return 0; } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 6d81969e181c..c3337c3f08c1 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -487,7 +487,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, return -ENOMEM; } - mutex_lock(&ipc4_data->pipeline_state_mutex); + guard(mutex)(&ipc4_data->pipeline_state_mutex); /* * IPC4 requires pipelines to be triggered in order starting at the sink and @@ -580,7 +580,6 @@ skip_pause_transition: } free: - mutex_unlock(&ipc4_data->pipeline_state_mutex); kfree(trigger_list); kfree(pipe_priority); return ret; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 479772dc466a..a5a1ef0b96c4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3150,7 +3150,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_fw_data *ipc4_data = sdev->private; int ret = 0; - mutex_lock(&ipc4_data->pipeline_state_mutex); + guard(mutex)(&ipc4_data->pipeline_state_mutex); /* freeing a pipeline frees all the widgets associated with it */ if (swidget->id == snd_soc_dapm_scheduler) { @@ -3161,7 +3161,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget if (pipeline->use_chain_dma) { dev_warn(sdev->dev, "use_chain_dma set for scheduler %s", swidget->widget->name); - mutex_unlock(&ipc4_data->pipeline_state_mutex); return 0; } @@ -3189,8 +3188,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ida_free(&fw_module->m_ida, swidget->instance_id); } - mutex_unlock(&ipc4_data->pipeline_state_mutex); - return ret; } diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 73d9f2083326..c9c6c0c52c62 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -412,7 +412,7 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ } /* Serialise IPC TX */ - mutex_lock(&ipc->tx_mutex); + guard(mutex)(&ipc->tx_mutex); ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); @@ -429,8 +429,6 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); } - mutex_unlock(&ipc->tx_mutex); - return ret; } @@ -506,7 +504,7 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, } /* Serialise IPC TX */ - mutex_lock(&sdev->ipc->tx_mutex); + guard(mutex)(&sdev->ipc->tx_mutex); do { size_t tx_size, rx_size; @@ -590,8 +588,6 @@ out: if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); - mutex_unlock(&sdev->ipc->tx_mutex); - kfree(tx_payload_for_get); return ret; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index d73644e85b6e..72af1f4ff620 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -287,16 +287,12 @@ static inline int snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { - int ret = 0; - - mutex_lock(&sdev->power_state_access); + guard(mutex)(&sdev->power_state_access); if (sof_ops(sdev)->set_power_state) - ret = sof_ops(sdev)->set_power_state(sdev, target_state); + return sof_ops(sdev)->set_power_state(sdev, target_state); - mutex_unlock(&sdev->power_state_access); - - return ret; + return 0; } /* debug */ diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a9664b4cf43f..d55ee7343f8e 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -121,13 +121,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - int ret; - - mutex_lock(&swidget->setup_mutex); - ret = sof_widget_free_unlocked(sdev, swidget); - mutex_unlock(&swidget->setup_mutex); - - return ret; + guard(mutex)(&swidget->setup_mutex); + return sof_widget_free_unlocked(sdev, swidget); } EXPORT_SYMBOL(sof_widget_free); @@ -240,13 +235,8 @@ use_count_dec: int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - int ret; - - mutex_lock(&swidget->setup_mutex); - ret = sof_widget_setup_unlocked(sdev, swidget); - mutex_unlock(&swidget->setup_mutex); - - return ret; + guard(mutex)(&swidget->setup_mutex); + return sof_widget_setup_unlocked(sdev, swidget); } EXPORT_SYMBOL(sof_widget_setup); @@ -377,24 +367,22 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, else swidget = sroute->src_widget; - mutex_lock(&swidget->setup_mutex); - if (!swidget->use_count) { - mutex_unlock(&swidget->setup_mutex); - continue; - } + scoped_guard(mutex, &swidget->setup_mutex) { + if (!swidget->use_count) + continue; - if (tplg_ops && tplg_ops->route_setup) { - /* - * this route will get freed when either the source widget or the sink - * widget is freed during hw_free - */ - ret = tplg_ops->route_setup(sdev, sroute); - if (!ret) - sroute->setup = true; + if (tplg_ops && tplg_ops->route_setup) { + /* + * this route will get freed when either the + * source widget or the sink widget is freed + * during hw_free + */ + ret = tplg_ops->route_setup(sdev, sroute); + if (!ret) + sroute->setup = true; + } } - mutex_unlock(&swidget->setup_mutex); - if (ret < 0) return ret; } diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index b0802484a2d3..38f1d7cec470 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -265,9 +265,8 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, } /* add to list of SOF client devices */ - mutex_lock(&sdev->ipc_client_mutex); - list_add(¢ry->list, &sdev->ipc_client_list); - mutex_unlock(&sdev->ipc_client_mutex); + scoped_guard(mutex, &sdev->ipc_client_mutex) + list_add(¢ry->list, &sdev->ipc_client_list); return 0; @@ -285,7 +284,7 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i { struct sof_client_dev_entry *centry; - mutex_lock(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->ipc_client_mutex); /* * sof_client_auxdev_release() will be invoked to free up memory @@ -301,8 +300,6 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i break; } } - - mutex_unlock(&sdev->ipc_client_mutex); } EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT"); @@ -400,7 +397,7 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) const struct auxiliary_driver *adrv; struct sof_client_dev_entry *centry; - mutex_lock(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->ipc_client_mutex); list_for_each_entry(centry, &sdev->ipc_client_list, list) { struct sof_client_dev *cdev = ¢ry->client_dev; @@ -414,8 +411,6 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) adrv->suspend(&cdev->auxdev, state); } - mutex_unlock(&sdev->ipc_client_mutex); - return 0; } EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT"); @@ -425,7 +420,7 @@ int sof_resume_clients(struct snd_sof_dev *sdev) const struct auxiliary_driver *adrv; struct sof_client_dev_entry *centry; - mutex_lock(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->ipc_client_mutex); list_for_each_entry(centry, &sdev->ipc_client_list, list) { struct sof_client_dev *cdev = ¢ry->client_dev; @@ -439,8 +434,6 @@ int sof_resume_clients(struct snd_sof_dev *sdev) adrv->resume(&cdev->auxdev); } - mutex_unlock(&sdev->ipc_client_mutex); - return 0; } EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT"); @@ -532,14 +525,11 @@ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) return; } - mutex_lock(&sdev->client_event_handler_mutex); - + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { if (event->ipc_msg_type == msg_type) event->callback(event->cdev, msg_buf); } - - mutex_unlock(&sdev->client_event_handler_mutex); } int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, @@ -573,9 +563,8 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, event->callback = callback; /* add to list of SOF client devices */ - mutex_lock(&sdev->client_event_handler_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_add(&event->list, &sdev->ipc_rx_handler_list); - mutex_unlock(&sdev->client_event_handler_mutex); return 0; } @@ -587,7 +576,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_ipc_event_entry *event; - mutex_lock(&sdev->client_event_handler_mutex); + guard(mutex)(&sdev->ipc_client_mutex); list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { @@ -596,8 +585,6 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, break; } } - - mutex_unlock(&sdev->client_event_handler_mutex); } EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); @@ -606,12 +593,10 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { struct sof_state_event_entry *event; - mutex_lock(&sdev->client_event_handler_mutex); + guard(mutex)(&sdev->ipc_client_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) event->callback(event->cdev, sdev->fw_state); - - mutex_unlock(&sdev->client_event_handler_mutex); } int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, @@ -631,9 +616,8 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, event->callback = callback; /* add to list of SOF client devices */ - mutex_lock(&sdev->client_event_handler_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_add(&event->list, &sdev->fw_state_handler_list); - mutex_unlock(&sdev->client_event_handler_mutex); return 0; } @@ -644,7 +628,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_state_event_entry *event; - mutex_lock(&sdev->client_event_handler_mutex); + guard(mutex)(&sdev->ipc_client_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) { if (event->cdev == cdev) { @@ -653,8 +637,6 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) break; } } - - mutex_unlock(&sdev->client_event_handler_mutex); } EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT"); From 599a5b00a1bf2fdc5abb42985eb21c1ce3489416 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:09:59 +0200 Subject: [PATCH 156/341] ASoC: SOF: Intel: Use guard()/scoped_guard() for mutex locks where it makes sense Replace the manual mutex lock/unlock pairs with guard()/scoped_guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai-ops.c | 22 ++++++++++++---------- sound/soc/sof/intel/hda-mlink.c | 29 ++++++++--------------------- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 92681ca7f24d..cdfa3636f70c 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -311,7 +311,7 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp if (pipe_widget->instance_id < 0) return 0; - mutex_lock(&ipc4_data->pipeline_state_mutex); + guard(mutex)(&ipc4_data->pipeline_state_mutex); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -323,16 +323,16 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - goto out; + return ret; pipeline->state = SOF_IPC4_PIPE_PAUSED; + break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); ret = -EINVAL; } -out: - mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } @@ -388,7 +388,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c if (pipe_widget->instance_id < 0) return 0; - mutex_lock(&ipc4_data->pipeline_state_mutex); + guard(mutex)(&ipc4_data->pipeline_state_mutex); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -396,14 +396,16 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - goto out; + return ret; + pipeline->state = SOF_IPC4_PIPE_PAUSED; } ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) - goto out; + return ret; + pipeline->state = SOF_IPC4_PIPE_RUNNING; swidget->spipe->started_count++; break; @@ -411,7 +413,8 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) - goto out; + return ret; + pipeline->state = SOF_IPC4_PIPE_RUNNING; break; case SNDRV_PCM_TRIGGER_SUSPEND: @@ -429,8 +432,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c ret = -EINVAL; break; } -out: - mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index ce561fe52bd5..6f15213937a3 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -524,11 +524,8 @@ void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, boo hlink = &h2link->hext_link; - mutex_lock(&h2link->eml_lock); - - hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); - - mutex_unlock(&h2link->eml_lock); + scoped_guard(mutex, &h2link->eml_lock) + hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); } EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK"); @@ -837,11 +834,8 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) hlink = &h2link->hext_link; - mutex_lock(&h2link->eml_lock); - - hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); - - mutex_unlock(&h2link->eml_lock); + scoped_guard(mutex, &h2link->eml_lock) + hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK"); @@ -875,12 +869,8 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, lchan = 0; } - mutex_lock(&h2link->eml_lock); - - hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, - stream_id, dir); - - mutex_unlock(&h2link->eml_lock); + scoped_guard(mutex, &h2link->eml_lock) + hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir); val = readw(pcmsycm); @@ -1012,11 +1002,8 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e hlink = &h2link->hext_link; - mutex_lock(&h2link->eml_lock); - - hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); - - mutex_unlock(&h2link->eml_lock); + scoped_guard(mutex, &h2link->eml_lock) + hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); return 0; } From 58a581c38babe5ae2fa96b9a8387418f3275993e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:10:00 +0200 Subject: [PATCH 157/341] ASoC: SOF: amd: acp-ipc: Use guard() for spinlock_irq() Replace the manual spinlock_irq lock/unlock pairs with guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-ipc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 22d4b807e1bb..3cd4674dd800 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -190,15 +190,13 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write); if (dsp_ack) { if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { - spin_lock_irq(&sdev->ipc_lock); + guard(spinlock_irq)(&sdev->ipc_lock); /* handle immediate reply from DSP core */ acp_dsp_ipc_get_reply(sdev); snd_sof_ipc_reply(sdev, 0); /* set the done bit */ acp_dsp_ipc_dsp_done(sdev); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n", dsp_ack); From aa234886c7263e8c78031a20e33a9725acdbcf5d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:10:01 +0200 Subject: [PATCH 158/341] ASoC: SOF: imx: imx-common: Use guard() for spinlock_irqsafe() Replace the manual spinlock_irqsafe lock/unlock pairs with guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx-common.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index e787d3932fbb..7a03c8cc5dd4 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -81,14 +81,10 @@ EXPORT_SYMBOL(imx8_dump); static void imx_handle_reply(struct imx_dsp_ipc *ipc) { - struct snd_sof_dev *sdev; - unsigned long flags; + struct snd_sof_dev *sdev = imx_dsp_get_data(ipc); - sdev = imx_dsp_get_data(ipc); - - spin_lock_irqsave(&sdev->ipc_lock, flags); + guard(spinlock_irqsave)(&sdev->ipc_lock); snd_sof_ipc_process_reply(sdev, 0); - spin_unlock_irqrestore(&sdev->ipc_lock, flags); } static void imx_handle_request(struct imx_dsp_ipc *ipc) From 36fabc449a055547960712c164bfa3fe77cf0a88 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:10:02 +0200 Subject: [PATCH 159/341] ASoC: SOF: mediatek: mtk-adsp-common: Use guard() for spinlock_irqsave Replace the manual spinlock_irqsave lock/unlock pairs with guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mtk-adsp-common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 01bbadb160ff..75b4af4b5111 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -107,11 +107,9 @@ EXPORT_SYMBOL(mtk_adsp_send_msg); void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc) { struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); - unsigned long flags; - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); + guard(spinlock_irqsave)(&priv->sdev->ipc_lock); snd_sof_ipc_process_reply(priv->sdev, 0); - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } EXPORT_SYMBOL(mtk_adsp_handle_reply); From 294b9e7e8ecafd4dd4b1cc13d7585082451be0e7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:10:03 +0200 Subject: [PATCH 160/341] ASoC: SOF: Intel: Use guard() for spinlocks where it makes sense Replace the manual spinlock lock/unlock pairs with guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/atom.c | 7 +------ sound/soc/sof/intel/bdw.c | 7 +------ sound/soc/sof/intel/cnl.c | 11 ++--------- sound/soc/sof/intel/hda-dai-ops.c | 3 +-- sound/soc/sof/intel/hda-ipc.c | 11 ++--------- sound/soc/sof/intel/hda-stream.c | 11 ++++------- sound/soc/sof/intel/mtl.c | 5 +---- 7 files changed, 12 insertions(+), 43 deletions(-) diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 0d364bcdcfa9..32bf5e5e5978 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -143,9 +143,6 @@ irqreturn_t atom_irq_thread(int irq, void *context) /* reply message from DSP */ if (ipcx & SHIM_BYT_IPCX_DONE) { - - spin_lock_irq(&sdev->ipc_lock); - /* * handle immediate reply from DSP core. If the msg is * found, set done bit in cmd_done which is called at the @@ -153,11 +150,9 @@ irqreturn_t atom_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ + guard(spinlock_irq)(&sdev->ipc_lock); snd_sof_ipc_process_reply(sdev, ipcx); - atom_dsp_done(sdev); - - spin_unlock_irq(&sdev->ipc_lock); } /* new message from DSP */ diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index f1287d509835..9534d18be97d 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -315,9 +315,6 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, SHIM_IMRX_DONE, SHIM_IMRX_DONE); - - spin_lock_irq(&sdev->ipc_lock); - /* * handle immediate reply from DSP core. If the msg is * found, set done bit in cmd_done which is called at the @@ -325,11 +322,9 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ + guard(spinlock_irq)(&sdev->ipc_lock); snd_sof_ipc_process_reply(sdev, ipcx); - bdw_dsp_done(sdev); - - spin_unlock_irq(&sdev->ipc_lock); } ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 0cc5725515e7..69376fb5b20d 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -69,13 +69,10 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) data->primary = primary; data->extension = extension; - spin_lock_irq(&sdev->ipc_lock); - + guard(spinlock_irq)(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); cnl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x|%#x\n", @@ -141,15 +138,11 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context) CNL_DSP_REG_HIPCCTL_DONE, 0); if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { - spin_lock_irq(&sdev->ipc_lock); - /* handle immediate reply from DSP core */ + guard(spinlock_irq)(&sdev->ipc_lock); hda_dsp_ipc_get_reply(sdev); snd_sof_ipc_reply(sdev, msg); - cnl_ipc_dsp_done(sdev); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n", msg); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index cdfa3636f70c..b2c559559962 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -58,7 +58,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream return NULL; } - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); list_for_each_entry(hstream, &bus->stream_list, list) { struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -110,7 +110,6 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream res->link_locked = 1; res->link_substream = substream; } - spin_unlock_irq(&bus->reg_lock); return res; } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 94425c510861..2aef3954f4f7 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -204,13 +204,10 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) data->primary = primary; data->extension = extension; - spin_lock_irq(&sdev->ipc_lock); - + guard(spinlock_irq)(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); hda_dsp_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x|%#x\n", @@ -289,16 +286,12 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) * reply. */ if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { - spin_lock_irq(&sdev->ipc_lock); - /* handle immediate reply from DSP core */ + guard(spinlock_irq)(&sdev->ipc_lock); hda_dsp_ipc_get_reply(sdev); snd_sof_ipc_reply(sdev, msg); - /* set the done bit */ hda_dsp_ipc_dsp_done(sdev); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n", msg); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 9c3b3a9aaf83..8fdaf1fdc338 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -724,12 +724,12 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct hdac_bus *bus = sof_to_bus(sdev); u32 mask = BIT(hstream->index); - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); + /* couple host and link DMA if link DMA channel is idle */ if (!hext_stream->link_locked) snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0); - spin_unlock_irq(&bus->reg_lock); } hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); @@ -747,7 +747,7 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) u32 status; /* The function can be called at irq thread, so use spin_lock_irq */ - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); @@ -757,8 +757,6 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) if (status != 0xffffffff) ret = true; - spin_unlock_irq(&bus->reg_lock); - return ret; } EXPORT_SYMBOL_NS(hda_dsp_check_stream_irq, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -842,7 +840,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) * unsolicited responses from the codec */ for (i = 0, active = true; i < 10 && active; i++) { - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); @@ -853,7 +851,6 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) if (status & AZX_INT_CTRL_EN) { active |= hda_codec_check_rirb_status(sdev); } - spin_unlock_irq(&bus->reg_lock); } return IRQ_HANDLED; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 095dcf1a18e4..4ac81537ca05 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -596,13 +596,10 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) data->primary = primary; data->extension = extension; - spin_lock_irq(&sdev->ipc_lock); - + guard(spinlock_irq)(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); mtl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); - - spin_unlock_irq(&sdev->ipc_lock); } else { dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x|%#x\n", From 83aee46dc2142eed2dc40b5cef0e9e08e14cac42 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Jan 2026 12:10:04 +0200 Subject: [PATCH 161/341] ASoC: SOF: ipc/ops: Use guard() for spinlocks Replace the manual spinlock lock/unlock pairs with guard(). Only code refactoring, and no behavior change. Signed-off-by: Peter Ujfalusi Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260112101004.7648-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 4 +--- sound/soc/sof/ops.c | 34 +++++++--------------------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 3fdeb07bafa3..e6d8894b8ef6 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -47,7 +47,7 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, * The spin-lock is needed to protect message objects against other * atomic contexts. */ - spin_lock_irq(&sdev->ipc_lock); + guard(spinlock_irq)(&sdev->ipc_lock); /* initialise the message */ msg = &ipc->msg; @@ -66,8 +66,6 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, if (!ret) msg->ipc_complete = false; - spin_unlock_irq(&sdev->ipc_lock); - return ret; } diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index bd52e7ec6883..74c04dcf4167 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -38,13 +38,8 @@ bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, u32 mask, u32 value) { - unsigned long flags; - bool change; - - spin_lock_irqsave(&sdev->hw_lock, flags); - change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); - spin_unlock_irqrestore(&sdev->hw_lock, flags); - return change; + guard(spinlock_irqsave)(&sdev->hw_lock); + return snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); } EXPORT_SYMBOL(snd_sof_pci_update_bits); @@ -90,28 +85,16 @@ EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { - unsigned long flags; - bool change; - - spin_lock_irqsave(&sdev->hw_lock, flags); - change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, - value); - spin_unlock_irqrestore(&sdev->hw_lock, flags); - return change; + guard(spinlock_irqsave)(&sdev->hw_lock); + return snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, value); } EXPORT_SYMBOL(snd_sof_dsp_update_bits); bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, u64 mask, u64 value) { - unsigned long flags; - bool change; - - spin_lock_irqsave(&sdev->hw_lock, flags); - change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, - value); - spin_unlock_irqrestore(&sdev->hw_lock, flags); - return change; + guard(spinlock_irqsave)(&sdev->hw_lock); + return snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, value); } EXPORT_SYMBOL(snd_sof_dsp_update_bits64); @@ -134,11 +117,8 @@ void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { - unsigned long flags; - - spin_lock_irqsave(&sdev->hw_lock, flags); + guard(spinlock_irqsave)(&sdev->hw_lock); snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); - spin_unlock_irqrestore(&sdev->hw_lock, flags); } EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); From 0432fe32c129780f89fd5426059cb1ddd8e50858 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 12 Jan 2026 13:32:18 +0200 Subject: [PATCH 162/341] ASoC: sof: ipc4-topology: Add topology tokens domain_in stack & heap_bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add topology tokens for defining user-space domain_id, required stack and heap size byte for a component. The new topology tokens are SOF_TKN_COMP_DOMAIN_ID, SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT and SOF_TKN_COMP_STACK_BYTES_REQUIREMENT for defining required stack and heap size for a component. Signed-off-by: Jyri Sarha Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Péter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260112113221.4442-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 9ce72fbd6f11..5fa8ab5088e0 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -107,6 +107,9 @@ #define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME 417 #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 /* SSP */ #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 From 854d4389f20f07b646981ebb1ef44b7503658e9c Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 12 Jan 2026 13:32:19 +0200 Subject: [PATCH 163/341] ASoC: sof: Add domain_id, heap_bytes and stack_bytes to snd_sof_widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dp_domain_id, dp_heap_bytes and dp_stack_bytes to struct snd_sof_widget and fill the values from topology tuples with SOF_TKN_COMP_DOMAIN_ID, SOF_TKN_COMP_STACK_BYTES_REQUIREMENT and SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT tokens. Signed-off-by: Jyri Sarha Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Péter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260112113221.4442-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 6 ++++++ sound/soc/sof/sof-audio.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 479772dc466a..a14023c4c3e7 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -158,6 +158,12 @@ static const struct sof_topology_token comp_ext_tokens[] = { offsetof(struct snd_sof_widget, core)}, {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)}, + {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_widget, dp_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)}, }; static const struct sof_topology_token gain_tokens[] = { diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a8b93a2eec9c..03c8dd29e071 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -454,6 +454,11 @@ 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 */ + struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ struct snd_sof_pipeline *spipe; From 1cd8fbec6dfa9a9c25400b775fed887b59153afd Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 12 Jan 2026 13:32:20 +0200 Subject: [PATCH 164/341] ASoC: SOF: ipc4: sof_ipc4_module_init_ext_init structs and macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add structs and macros for struct sof_ipc4_module_init_ext_init, following struct sof_ipc4_module_init_ext_object array, and struct sof_ipc4_mod_init_ext_dp_memory_data as object payload. Signed-off-by: Jyri Sarha Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Péter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260112113221.4442-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 15fac532688e..4554e5e8cab5 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -352,6 +352,10 @@ struct sof_ipc4_base_module_cfg { #define SOF_IPC4_MOD_EXT_DOMAIN_MASK BIT(28) #define SOF_IPC4_MOD_EXT_DOMAIN(x) ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT) +#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_SHIFT 29 +#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK BIT(29) +#define SOF_IPC4_MOD_EXT_EXTENDED_INIT(x) ((x) << SOF_IPC4_MOD_EXT_EXTENDED_SHIFT) + /* bind/unbind module ipc msg */ #define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT 0 #define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK GENMASK(15, 0) @@ -586,6 +590,77 @@ struct sof_ipc4_notify_module_data { #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0) +/* + * Macros for creating struct sof_ipc4_module_init_ext_init payload + * with its associated data. ext_init payload should be the first + * piece of payload following SOF_IPC4_MOD_INIT_INSTANCE msg, and its + * existence is indicated with SOF_IPC4_MOD_EXT_EXTENDED-bit. + * + * The macros below apply to sof_ipc4_module_init_ext_init.word0 + */ +#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT 0 +#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_MASK BIT(0) +#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN(x) ((x) << SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT) + +#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT 1 +#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_MASK BIT(1) +#define SOF_IPC4_MOD_INIT_EXT_GNA_USED(x) ((x) << SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT) + +#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT 2 +#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK BIT(2) +#define SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT) + +struct sof_ipc4_module_init_ext_init { + u32 word0; + u32 rsvd1; + u32 rsvd2; +} __packed __aligned(4); + +/* + * SOF_IPC4_MOD_EXT_EXTENDED payload may be followed by arbitrary + * number of object array objects. SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY + * -bit indicates that an array object follows struct + * sof_ipc4_module_init_ext_init. + * + * The object header's SOF_IPC4_MOD_INIT_EXT_OBJ_LAST-bit in struct + * sof_ipc4_module_init_ext_object indicates if the array is continued + * with another object. The header has also fields to identify the + * object, SOF_IPC4_MOD_INIT_EXT_OBJ_ID, and to indicate the object's + * size in 32-bit words, SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS, not + * including the header itself. + * + * The macros below apply to sof_ipc4_module_init_ext_object.header + */ +#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT 0 +#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK BIT(0) +#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT) + +#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT 1 +#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_MASK GENMASK(15, 1) +#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT) + +#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT 16 +#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_MASK GENMASK(31, 16) +#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT) + +struct sof_ipc4_module_init_ext_object { + u32 header; + u32 data[]; +} __packed __aligned(4); + +enum sof_ipc4_mod_init_ext_obj_id { + SOF_IPC4_MOD_INIT_DATA_ID_INVALID = 0, + SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, + 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 */ +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); + /** @}*/ #endif From fc6ceb7e4ea746e663ff8c5593e67ad8ccbee34a Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 12 Jan 2026 13:32:21 +0200 Subject: [PATCH 165/341] ASoC: sof ipc4: Add sof_ipc4_widget_setup_msg_payload() and call it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add of_ipc4_widget_setup_msg_payload() for adding struct sof_ipc4_module_init_ext_init payload with associated objects. The function allocates memory for the additional payload, sets up the payload according to data collected from topology, and copies pre-encoded module specific payload after the ext_init payload. The function is called in sof_ipc4_widget_setup(). Signed-off-by: Jyri Sarha Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Péter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260112113221.4442-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index a14023c4c3e7..f997b95d9cec 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2974,6 +2974,77 @@ 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) +{ + 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; + 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. + */ + if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { + msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; + return 0; + } + + payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + /* 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 dp_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 | + 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->dp_domain_id; + dp_mem_data->stack_bytes = swidget->dp_stack_bytes; + dp_mem_data->heap_bytes = swidget->dp_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 */ + new_size = ext_pos * sizeof(u32) + ipc_size; + if (new_size > sdev->ipc->max_payload_size) { + dev_err(sdev->dev, "Max ipc payload size %zu exceeded: %u", + sdev->ipc->max_payload_size, new_size); + kfree(payload); + return -EINVAL; + } + *new_data = payload; + + /* Copy module specific ipc_payload to end */ + memcpy(&payload[ext_pos], ipc_data, ipc_size); + + /* Update msg extension bits according to the payload changes */ + msg->extension |= SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; + msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK; + msg->extension |= SOF_IPC4_MOD_EXT_PARAM_SIZE(DIV_ROUND_UP(new_size, sizeof(u32))); + + return new_size; +} + 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; @@ -2981,6 +3052,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_pipeline *pipeline; struct sof_ipc4_msg *msg; void *ipc_data = NULL; + void *ext_data = NULL; u32 ipc_size = 0; int ret; @@ -3125,6 +3197,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", 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); + if (ret < 0) + goto fail; + + if (ret > 0) { + ipc_size = ret; + ipc_data = ext_data; + } } else { dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, @@ -3135,6 +3217,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->data_ptr = ipc_data; ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size); + +fail: if (ret < 0) { dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name); @@ -3147,6 +3231,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget } } + kfree(ext_data); return ret; } From 66c26346ae30c883eef70acf9cf9054dfdb4fb2f Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 5 Jan 2026 04:02:08 +0100 Subject: [PATCH 166/341] ASoC: wm8962: Add WM8962_ADC_MONOMIX to "3D Coefficients" mask This bit is handled by a separate control. Signed-off-by: Sebastian Krzyszkowiak Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260105-wm8962-l5-fixes-v1-1-f4f4eeacf089@puri.sm Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e9e317ce6898..1040740fc80f 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1760,7 +1760,7 @@ SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18), SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0), -SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA), +SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA | WM8962_ADC_MONOMIX), SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0), SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA), From e590752119029d87ce46d725e11245a52d22e1fe Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 5 Jan 2026 04:02:10 +0100 Subject: [PATCH 167/341] ASoC: wm8962: Don't report a microphone if it's shorted to ground on plug This usually means that a TRS plug with no microphone pin has been plugged into a TRRS socket. Cases where a user is plugging in a microphone while pressing a button will be handled via incoming interrupt after the user releases the button, so the microphone will still be detected once it becomes usable. Signed-off-by: Sebastian Krzyszkowiak Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260105-wm8962-l5-fixes-v1-3-f4f4eeacf089@puri.sm Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 1040740fc80f..bff864467416 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -67,6 +67,8 @@ struct wm8962_priv { struct mutex dsp2_ena_lock; u16 dsp2_ena; + int mic_status; + struct delayed_work mic_work; struct snd_soc_jack *jack; @@ -3081,8 +3083,16 @@ static void wm8962_mic_work(struct work_struct *work) if (reg & WM8962_MICSHORT_STS) { status |= SND_JACK_BTN_0; irq_pol |= WM8962_MICSCD_IRQ_POL; + + /* Don't report a microphone if it's shorted right after + * plugging in, as this may be a TRS plug in a TRRS socket. + */ + if (!(wm8962->mic_status & WM8962_MICDET_STS)) + status = 0; } + wm8962->mic_status = status; + snd_soc_jack_report(wm8962->jack, status, SND_JACK_MICROPHONE | SND_JACK_BTN_0); From daf86dcdbb40c4a0e4b8e579c6eecf148560711f Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Mon, 12 Jan 2026 09:27:32 +0800 Subject: [PATCH 168/341] ASoC: codecs: rtq9128: Modify the chip initial setting Modify the chip initial setting to default enable DC load detection function. This function is the chip specific feature that can detect the output open/short. Signed-off-by: ChiYuan Huang Link: https://patch.msgid.link/340c88ae78edeb76cde812453c9a72d28b73e9f4.1768180827.git.cy_huang@richtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rtq9128.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c index 391cc03d687f..289bb3c04a90 100644 --- a/sound/soc/codecs/rtq9128.c +++ b/sound/soc/codecs/rtq9128.c @@ -352,7 +352,7 @@ static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = { static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = { { 0xA0, 0xEF }, { 0x0D, 0x00 }, - { 0x03, 0x05 }, + { 0x03, 0x45 }, { 0x05, 0x31 }, { 0x06, 0x23 }, { 0x70, 0x11 }, @@ -367,7 +367,7 @@ static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = { static const struct rtq9128_init_reg rtq9128_dh_tables[] = { { 0x0F, 0x00 }, - { 0x03, 0x0D }, + { 0x03, 0x4D }, { 0xB2, 0xFF }, { 0xB3, 0xFF }, { 0x30, 0x180 }, @@ -378,7 +378,7 @@ static const struct rtq9128_init_reg rtq9128_dh_tables[] = { static const struct rtq9128_init_reg rtq9128_dl_tables[] = { { 0x0F, 0x00 }, - { 0x03, 0x0D }, + { 0x03, 0x4D }, { 0x30, 0x180 }, { 0x8A, 0x55 }, { 0x72, 0x00 }, From b7d53fe53cb57db1ca5743d2ac2db28140c37647 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Mon, 12 Jan 2026 09:27:33 +0800 Subject: [PATCH 169/341] ASoC: dt-bindings: rtq9128: Add rtq9154 backward compatible Add rtq9154 backward compatible support. Signed-off-by: ChiYuan Huang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/ab31e3965e9cb50ecdc14d5ea90d70dc26d1d187.1768180827.git.cy_huang@richtek.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/richtek,rtq9128.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml index d54686a19ab7..a125663988a5 100644 --- a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml +++ b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml @@ -14,13 +14,21 @@ description: class-D audio power amplifier and delivering 4x75W into 4OHm at 10% THD+N from a 25V supply in automotive applications. + The RTQ9154 is the family series of RTQ9128. The major change is to modify + the package size. Beside this, whole functions are almost all the same. + allOf: - $ref: dai-common.yaml# properties: compatible: - enum: - - richtek,rtq9128 + oneOf: + - enum: + - richtek,rtq9128 + - items: + - enum: + - richtek,rtq9154 + - const: richtek,rtq9128 reg: maxItems: 1 From 6be9ea62afedef0f976eb3dba4c117be0c1d3809 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Mon, 12 Jan 2026 09:27:34 +0800 Subject: [PATCH 170/341] ASoC: codecs: rtq9128: Add compatible changes for rtq9154 Although rtq9154 only modify the outter package, some register settings related to the channel order definition are still different. Use the chip model ID code to seperate these changes. Signed-off-by: ChiYuan Huang Link: https://patch.msgid.link/ca3a07c8987a033c3d505f5d79956d0e935ea03f.1768180827.git.cy_huang@richtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rtq9128.c | 108 +++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c index 289bb3c04a90..14a2c0723d33 100644 --- a/sound/soc/codecs/rtq9128.c +++ b/sound/soc/codecs/rtq9128.c @@ -40,6 +40,12 @@ #define RTQ9128_REG_EFUSE_DATA 0xE0 #define RTQ9128_REG_VENDOR_ID 0xF9 +#define RTQ9154_REG_CH1_VOL 0x34 +#define RTQ9154_REG_CH2_VOL 0x33 +#define RTQ9154_REG_CH3_VOL 0x32 +#define RTQ9154_REG_CH4_VOL 0x31 +#define RTQ9154_REG_AUTOULQM 0xAD + #define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0) #define RTQ9128_DOLEN_MASK GENMASK(7, 6) #define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4) @@ -48,6 +54,7 @@ #define RTQ9128_MSMUTE_MASK BIT(0) #define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0) #define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8) +#define RTQ9128_MODEL_ID_MASK GENMASK(7, 4) #define RTQ9128_SOFT_RESET_VAL 0x80 #define RTQ9128_VENDOR_ID_VAL 0x470 @@ -56,6 +63,15 @@ #define RTQ9128_TKA470B_VAL 0 #define RTQ9128_RTQ9128DH_VAL 0x0F #define RTQ9128_RTQ9128DL_VAL 0x10 +#define RTQ9154_MODEL_ID 0x08 + +#define RTQ9154_AUTOULQM_VAL 0x82 + +enum rtq9128_chip_model { + CHIP_MODEL_RTQ9128 = 0, + CHIP_MODEL_RTQ9154, + CHIP_MODEL_MAX +}; struct rtq9128_data { struct gpio_desc *enable; @@ -63,6 +79,7 @@ struct rtq9128_data { int tdm_slots; int tdm_slot_width; bool tdm_input_data2_select; + enum rtq9128_chip_model chip_model; }; struct rtq9128_init_reg { @@ -251,6 +268,28 @@ static const struct soc_enum rtq9128_out4_phase_enum = SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text), phase_select_text); +static const struct soc_enum rtq9154_ch1_si_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text), + source_select_text); +static const struct soc_enum rtq9154_ch2_si_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text), + source_select_text); +static const struct soc_enum rtq9154_ch3_si_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text), + source_select_text); +static const struct soc_enum rtq9154_ch4_si_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text), + source_select_text); +static const struct soc_enum rtq9154_out1_phase_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text), + phase_select_text); +static const struct soc_enum rtq9154_out2_phase_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text), + phase_select_text); +static const struct soc_enum rtq9154_out3_phase_enum = + SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text), + phase_select_text); + /* * In general usage, DVDD could be 1P8V, 3P0V or 3P3V. * This DVDD undervoltage protection is to prevent from the abnormal power @@ -283,10 +322,33 @@ static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = { SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum), }; +static const struct snd_kcontrol_new rtq9154_snd_ctrls[] = { + SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("CH1 Volume", RTQ9154_REG_CH1_VOL, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("CH2 Volume", RTQ9154_REG_CH2_VOL, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("CH3 Volume", RTQ9154_REG_CH3_VOL, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("CH4 Volume", RTQ9154_REG_CH4_VOL, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv), + SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 4, 1, 0), + SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 5, 1, 0), + SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0), + SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0), + SOC_ENUM("CH1 SI Select", rtq9154_ch1_si_enum), + SOC_ENUM("CH2 SI Select", rtq9154_ch2_si_enum), + SOC_ENUM("CH3 SI Select", rtq9154_ch3_si_enum), + SOC_ENUM("CH4 SI Select", rtq9154_ch4_si_enum), + SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum), + SOC_ENUM("OUT1 Phase Select", rtq9154_out1_phase_enum), + SOC_ENUM("OUT2 Phase Select", rtq9154_out2_phase_enum), + SOC_ENUM("OUT3 Phase Select", rtq9154_out3_phase_enum), + SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum), +}; + static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct rtq9128_data *data = snd_soc_component_get_drvdata(comp); unsigned int shift, mask; int ret; @@ -301,6 +363,10 @@ static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kco else shift = 0; + /* Compared to RTQ9128, RTQ9154 use the reverse order for DACx bitfield location */ + if (data->chip_model == CHIP_MODEL_RTQ9154) + shift = 6 - shift; + mask = RTQ9128_CHSTAT_VAL_MASK << shift; /* Turn channel state to Normal or HiZ */ @@ -387,6 +453,7 @@ static const struct rtq9128_init_reg rtq9128_dl_tables[] = { static int rtq9128_component_probe(struct snd_soc_component *comp) { + struct rtq9128_data *data = snd_soc_component_get_drvdata(comp); const struct rtq9128_init_reg *table, *curr; size_t table_size; unsigned int val; @@ -421,6 +488,14 @@ static int rtq9128_component_probe(struct snd_soc_component *comp) return ret; } + + if (data->chip_model == CHIP_MODEL_RTQ9154) { + /* Enable RTQ9154 Specific AUTO ULQM feature */ + ret = snd_soc_component_write(comp, RTQ9154_REG_AUTOULQM, RTQ9154_AUTOULQM_VAL); + if (ret < 0) + return ret; + } + pm_runtime_mark_last_busy(comp->dev); pm_runtime_put(comp->dev); @@ -439,6 +514,18 @@ static const struct snd_soc_component_driver rtq9128_comp_driver = { .endianness = 1, }; +static const struct snd_soc_component_driver rtq9154_comp_driver = { + .probe = rtq9128_component_probe, + .controls = rtq9154_snd_ctrls, + .num_controls = ARRAY_SIZE(rtq9154_snd_ctrls), + .dapm_widgets = rtq9128_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets), + .dapm_routes = rtq9128_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai); @@ -679,7 +766,8 @@ static int rtq9128_probe(struct i2c_client *i2c) struct device *dev = &i2c->dev; struct rtq9128_data *data; struct regmap *regmap; - unsigned int venid; + unsigned int veninfo, venid, chip_model; + const struct snd_soc_component_driver *comp_drv; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -712,21 +800,33 @@ static int rtq9128_probe(struct i2c_client *i2c) if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); - ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid); + ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &veninfo); if (ret) return dev_err_probe(dev, ret, "Failed to get vendor id\n"); - venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid); + venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, veninfo); if (venid != RTQ9128_VENDOR_ID_VAL) return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid); + chip_model = FIELD_GET(RTQ9128_MODEL_ID_MASK, veninfo); + switch (chip_model) { + case RTQ9154_MODEL_ID: + data->chip_model = CHIP_MODEL_RTQ9154; + comp_drv = &rtq9154_comp_driver; + break; + default: + data->chip_model = CHIP_MODEL_RTQ9128; + comp_drv = &rtq9128_comp_driver; + break; + } + pm_runtime_set_active(dev); pm_runtime_mark_last_busy(dev); ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); - return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1); + return devm_snd_soc_register_component(dev, comp_drv, &rtq9128_dai, 1); } static int rtq9128_pm_runtime_suspend(struct device *dev) From 7af9e30a0c8bb05b7254fdca857c1a1126b652eb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 27 Dec 2025 23:10:41 -0800 Subject: [PATCH 171/341] ASoC: pxa: drop unused Kconfig symbol Drop a bogus dangling Kconfig symbol for select SND_SOC_AC97_BUS_NEW. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216748 Fixes: 1c8bc7b3de5e ("ASoC: pxa: switch to new ac97 bus support") Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251228071041.2246718-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e54abcd39f79..4bd14ae330d5 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -18,7 +18,6 @@ config SND_PXA2XX_SOC_AC97 select AC97_BUS_NEW select SND_PXA2XX_LIB select SND_PXA2XX_LIB_AC97 - select SND_SOC_AC97_BUS_NEW config SND_PXA2XX_SOC_I2S select SND_PXA2XX_LIB From 0bef51df7cf882e2b4ec0f7d52c311b09c850b9a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 28 Dec 2025 11:05:42 -0800 Subject: [PATCH 172/341] ASoC: amd: drop unused Kconfig symbols Remove the dangling Kconfig references to CLK_FIXED_FCH since they are not used anywhere else in the kernel source tree. Fixes: 281ddf62f551 ("ASoC: amd: Kconfig: Select fch clock support with machine driver") Fixes: d4c750f2c7d4 ("ASoC: amd: acp: Add generic machine driver support for ACP cards") Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251228190542.2482910-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 2 -- sound/soc/amd/acp/Kconfig | 1 - 2 files changed, 3 deletions(-) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index fd35a03aadcb..f7c3010624df 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,7 +8,6 @@ config SND_SOC_AMD_ACP config SND_SOC_AMD_CZ_DA7219MX98357_MACH tristate "AMD CZ support for DA7219, RT5682 and MAX9835" - select CLK_FIXED_FCH select SND_SOC_DA7219 select SND_SOC_RT5682_I2C select SND_SOC_MAX98357A @@ -45,7 +44,6 @@ config SND_SOC_AMD_ACP3x config SND_SOC_AMD_RV_RT5682_MACH tristate "AMD RV support for RT5682" - select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_MAX98357A select SND_SOC_CROS_EC_CODEC diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index c2a60bc80ee6..6d528bd34681 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -104,7 +104,6 @@ config SND_AMD_ASOC_ACP70 config SND_SOC_AMD_MACH_COMMON tristate depends on X86 && PCI && I2C - select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_DMIC select SND_SOC_RT1019 From e7c30ac379b429d439eb62ae1bb69720a6701e26 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 10 Jan 2026 12:14:14 +0530 Subject: [PATCH 173/341] ASoC: amd: acp: soc-acpi: add is_device_rt712_vb() helper Add a filter to skip the RT172 VB configuration if a SmartMic Function is not found in the SDCA descriptors. If the ACPI information is incorrect this can only be quirked further with DMI information. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20260110064505.1485927-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 6 +++ sound/soc/amd/acp/Makefile | 2 + sound/soc/amd/acp/amd-acp70-acpi-match.c | 50 ++++++++++++++++++++ sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c | 42 ++++++++++++++++ sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h | 14 ++++++ 5 files changed, 114 insertions(+) create mode 100644 sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c create mode 100644 sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 6d528bd34681..b17aaf2c6ccb 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -15,8 +15,14 @@ config SND_SOC_AMD_ACP_COMMON config SND_SOC_ACPI_AMD_MATCH tristate + select SND_SOC_ACPI_AMD_SDCA_QUIRKS select SND_SOC_ACPI if ACPI +config SND_SOC_ACPI_AMD_SDCA_QUIRKS + tristate + depends on ACPI + depends on SND_SOC_SDCA + if SND_SOC_AMD_ACP_COMMON config SND_SOC_AMD_ACP_PDM diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index 08220b9a3802..81d23aded348 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -27,6 +27,7 @@ snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o snd-acp-sdw-mach-y := acp-sdw-mach-common.o snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o +snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o @@ -40,6 +41,7 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o +obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 871b4f054a84..fa39f18578ca 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -7,6 +7,7 @@ */ #include +#include "soc-acpi-amd-sdca-quirks.h" #include "../mach-config.h" static const struct snd_soc_acpi_endpoint single_endpoint = { @@ -44,6 +45,39 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = { .group_id = 1 }; +static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* Amp Endpoint, work as spk_l_endpoint */ + { + .num = 1, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + /* DMIC Endpoint */ + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device rt712_vb_1_group1_adr[] = { + { + .adr = 0x000130025D071201ull, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints, + .name_prefix = "rt712" + } +}; + static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = { { .adr = 0x000030025D071101ull, @@ -254,6 +288,15 @@ static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1[] = { {} }; +static const struct snd_soc_acpi_link_adr acp70_alc712_vb_l1[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr), + .adr_d = rt712_vb_1_group1_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = { { .mask = BIT(0), @@ -308,6 +351,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_cs35l56x4_l1, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(1), + .links = acp70_alc712_vb_l1, + .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, + .drv_name = "amd_sdw", + }, {}, }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines); @@ -327,3 +376,4 @@ EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines); MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS"); diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c new file mode 100644 index 000000000000..63bf9e3c0ae1 --- /dev/null +++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * soc-acpi-amd-sdca-quirks.c - tables and support for SDCA quirks + * + * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include "soc-acpi-amd-sdca-quirks.h" + +/* + * Pretend machine quirk. The argument type is not the traditional + * 'struct snd_soc_acpi_mach' pointer but instead the sdw_amd_ctx + * which contains the peripheral information required for the + * SoundWire/SDCA filter on the SMART_MIC setup and interface + * revision. When the return value is false, the entry in the + * 'snd_soc_acpi_mach' table needs to be skipped. + */ +bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg) +{ + struct sdw_amd_ctx *ctx = arg; + int i; + + if (!ctx) + return false; + + for (i = 0; i < ctx->peripherals->num_peripherals; i++) { + if (sdca_device_quirk_match(ctx->peripherals->array[i], + SDCA_QUIRKS_RT712_VB)) + return true; + } + + return false; +} +EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS"); + +MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h new file mode 100644 index 000000000000..7e345a236da1 --- /dev/null +++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * soc-acpi-amd-sdca-quirks.h - tables and support for SDCA quirks + * + * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + */ + +#ifndef _SND_SOC_ACPI_AMD_SDCA_QUIRKS +#define _SND_SOC_ACPI_AMD_SDCA_QUIRKS + +bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg); + +#endif From 0924c6bb67b67384c53c63df4a3f4a86cd2c2624 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 12 Jan 2026 14:28:50 +0000 Subject: [PATCH 174/341] ALSA: hda/cs8409: Add quirk for CDB35L56-FOUR-HD Adds quirkiness for the Cirrus Logic CDB35L56-FOUR-HD board. The quirk must be forced by model name "CDB35L56-FOUR-HD" because there isn't a unique SSID that can be used. For example in /etc/modprobe.d: options snd-hda-intel model="CDB35L56-FOUR-HD" The CDB35L56-FOUR-HD is not a complete PC. It is an add-on audio board that requires a host system and replaces the normal HDA codec on the host. Because of this there isn't an SSID that uniquely identifies this configuration. Also, the usual host board is an Aaeon UpXtreme, which doesn't have a unique SSID. Because of this, the quirk must be forced by a module param. This is acceptable because it is a development board, not an end-user system, so there is no need for it to be detected automatically. Signed-off-by: Simon Trimmer Co-developed-by: Richard Fitzgerald Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260112142850.243054-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/cirrus/Kconfig | 1 + sound/hda/codecs/cirrus/cs8409-tables.c | 76 ++++++++++- sound/hda/codecs/cirrus/cs8409.c | 172 ++++++++++++++++++++++++ sound/hda/codecs/cirrus/cs8409.h | 13 ++ 4 files changed, 261 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig index ec6cbcaf64f0..d7a1b619d243 100644 --- a/sound/hda/codecs/cirrus/Kconfig +++ b/sound/hda/codecs/cirrus/Kconfig @@ -34,6 +34,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_CS8409 tristate "Build Cirrus Logic HDA bridge support" select SND_HDA_GENERIC + select SND_HDA_SCODEC_COMPONENT help Say Y or M here to include Cirrus Logic HDA bridge support such as CS8409. diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c index 8c703b714a71..b9ec8fb8eab7 100644 --- a/sound/hda/codecs/cirrus/cs8409-tables.c +++ b/sound/hda/codecs/cirrus/cs8409-tables.c @@ -468,6 +468,70 @@ struct sub_codec dolphin_cs42l42_1 = { .no_type_dect = 1, }; +/****************************************************************************** + * CDB35L56-FOUR-HD Specific Arrays + ******************************************************************************/ +const struct hda_verb cs8409_cdb35l56_four_init_verbs[] = { + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ + {} /* terminator */ +}; + +static const struct hda_pintbl cs8409_cdb35l56_four_pincfgs[] = { + /* 0xPPLLLLLLDDDDTTTTCCCCMMMMAAAASSSS + * P = PCON: AC_JACK_PORT_* + * L = LOC: AC_JACK_LOC_* + * D = DD: device type AC_JACK_* + * T = CTYP: AC_JACK_CONN_* + * C = COL: AC_JACK_COLOR_* + * M = MISC: ? + * A = DA: AC_DEFCFG_DEF_ASSOC + * S = SEQ: Sequence number in DA group + */ + { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ + /* "Mic" */ + { CS8409_PIN_ASP2_RECEIVER_A, 0x04a12050 }, /* ASP-2-RX */ + {} /* terminator */ +}; + +const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[] = { + /* +PLL1/2_EN, +I2C_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, + /* ASP1/2_EN=0, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, + /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, + /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 }, + /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 }, + /* ASP2.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL1, 0x0800 }, + /* ASP2.A: RX.RAP=1, RX.RSZ=24 bits, RX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL2, 0x2800 }, + /* ASP1: LCHI = 00h */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, + /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, + /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, + /* ASP2: LCHI=1Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f }, + /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f }, + /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c }, + /* ASP1/2_BEEP=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, + /* ASP1/2_EN=1, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 }, + /* -PLL2_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, /* TX2.A: pre-scale att.=0 dB */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 }, + /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 }, + {} /* Terminator */ +}; + /****************************************************************************** * CS8409 Patch Driver Structs * Arrays Used for all projects using CS8409 @@ -557,7 +621,6 @@ const struct hda_quirk cs8409_fixup_tbl[] = { {} /* terminator */ }; -/* Dell Inspiron models with cs8409/cs42l42 */ const struct hda_model_fixup cs8409_models[] = { { .id = CS8409_BULLSEYE, .name = "bullseye" }, { .id = CS8409_WARLOCK, .name = "warlock" }, @@ -566,6 +629,7 @@ const struct hda_model_fixup cs8409_models[] = { { .id = CS8409_CYBORG, .name = "cyborg" }, { .id = CS8409_DOLPHIN, .name = "dolphin" }, { .id = CS8409_ODIN, .name = "odin" }, + { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" }, {} }; @@ -620,4 +684,14 @@ const struct hda_fixup cs8409_fixups[] = { .chained = true, .chain_id = CS8409_FIXUPS, }, + [CS8409_CDB35L56_FOUR_HD] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cdb35l56_four_pincfgs, + .chained = true, + .chain_id = CS8409_CDB35L56_FOUR_HD_FIXUP, + }, + [CS8409_CDB35L56_FOUR_HD_FIXUP] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs8409_cdb35l56_four_autodet_fixup, + }, }; diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c index 2c02d3be89ee..61b6a15d6291 100644 --- a/sound/hda/codecs/cirrus/cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -6,14 +6,19 @@ * Cirrus Logic International Semiconductor Ltd. */ +#include +#include +#include #include #include #include +#include #include #include #include #include "cs8409.h" +#include "../side-codecs/hda_component.h" /****************************************************************************** * CS8409 Specific Functions @@ -1216,6 +1221,172 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, } } +static int cs8409_comp_bind(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct cs8409_spec *spec = codec->spec; + + return hda_component_manager_bind(codec, &spec->comps); +} + +static void cs8409_comp_unbind(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct cs8409_spec *spec = codec->spec; + + hda_component_manager_unbind(codec, &spec->comps); +} + +static const struct component_master_ops cs8409_comp_master_ops = { + .bind = cs8409_comp_bind, + .unbind = cs8409_comp_unbind, +}; + +static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + struct snd_pcm_substream *sub, int action) +{ + struct cs8409_spec *spec = codec->spec; + + hda_component_manager_playback_hook(&spec->comps, action); +} + +static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec) +{ + const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg; + + for (; seq->nid; seq++) + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); +} + +static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = !spec->speaker_muted; + + return 0; +} + +static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + bool muted = !ucontrol->value.integer.value[0]; + + if (muted == spec->speaker_muted) + return 0; + + spec->speaker_muted = muted; + + return 1; +} + +static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ctl_boolean_mono_info, + .get = cs8409_spk_sw_get, + .put = cs8409_spk_sw_put, +}; + +void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct device *dev = hda_codec_dev(codec); + struct cs8409_spec *spec = codec->spec; + struct acpi_device *adev; + const char *bus = NULL; + static const struct { + const char *hid; + const char *name; + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, + { "CSC3556", "cs35l56-hda" }, + { "CSC3557", "cs35l57-hda" }}; + char *match; + int i, count = 0, count_devindex = 0; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: { + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); + if (adev) + break; + } + if (!adev) { + dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n"); + return; + } + + count = i2c_acpi_client_count(adev); + if (count > 0) { + bus = "i2c"; + } else { + count = acpi_spi_count_resources(adev); + if (count > 0) + bus = "spi"; + } + + struct fwnode_handle *fwnode __free(fwnode_handle) = + fwnode_handle_get(acpi_fwnode_handle(adev)); + acpi_dev_put(adev); + + if (!bus) { + dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid); + return; + } + + if (!fwnode) { + dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid); + return; + } + + /* + * When available the cirrus,dev-index property is an accurate + * count of the amps in a system and is used in preference to + * the count of bus devices that can contain additional address + * alias entries. + */ + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); + if (count_devindex > 0) + count = count_devindex; + + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); + if (!match) + return; + dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); + + ret = hda_component_manager_init(codec, &spec->comps, count, bus, + acpi_ids[i].hid, match, + &cs8409_comp_master_ops); + if (ret) + return; + + spec->gen.pcm_playback_hook = cs8409_comp_playback_hook; + + snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs); + snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs); + break; + } + case HDA_FIXUP_ACT_PROBE: + spec->speaker_muted = 0; /* speakers begin enabled */ + snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", + &cs8409_spk_sw_component_ctrl); + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; + snd_hda_codec_set_name(codec, "CS8409/CS35L56"); + break; + case HDA_FIXUP_ACT_INIT: + cs8409_cdb35l56_four_hw_init(codec); + break; + case HDA_FIXUP_ACT_FREE: + hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops); + break; + } +} + /****************************************************************************** * Dolphin Specific Functions * CS8409/ 2 X CS42L42 @@ -1473,3 +1644,4 @@ module_hda_codec_driver(cs8409_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h index 7fe56f4a73bc..be1714a84fff 100644 --- a/sound/hda/codecs/cirrus/cs8409.h +++ b/sound/hda/codecs/cirrus/cs8409.h @@ -18,6 +18,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "../generic.h" +#include "../side-codecs/hda_component.h" /* CS8409 Specific Definitions */ @@ -271,6 +272,8 @@ enum { CS8409_DOLPHIN, CS8409_DOLPHIN_FIXUPS, CS8409_ODIN, + CS8409_CDB35L56_FOUR_HD, + CS8409_CDB35L56_FOUR_HD_FIXUP, }; enum { @@ -341,12 +344,17 @@ struct cs8409_spec { unsigned int capture_started:1; unsigned int init_done:1; unsigned int build_ctrl_done:1; + unsigned int speaker_muted:1; /* verb exec op override */ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res); /* unsol_event op override */ void (*unsol_event)(struct hda_codec *codec, unsigned int res); + + /* component binding */ + struct component_match *match; + struct hda_component_parent comps; }; extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; @@ -374,4 +382,9 @@ extern struct sub_codec dolphin_cs42l42_1; void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); +extern const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[]; +extern const struct hda_verb cs8409_cdb35l56_four_init_verbs[]; +void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, const struct hda_fixup *fix, + int action); + #endif From 72919c57a055f6d7b79d66731dc398e9b433f47c Mon Sep 17 00:00:00 2001 From: Bharat Dev Burman Date: Tue, 13 Jan 2026 00:12:40 +0530 Subject: [PATCH 175/341] ALSA: hda/realtek: add HP Victus 16-e0xxx mute LED quirk HP Victus 16-e0xxx with ALC245 codec does not handle the toggling of the mute LED. This patch adds a quirk entry for subsystem ID 0x88eb using a new ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT fixup, enabling correct mute LED behavior. Signed-off-by: Bharat Dev Burman Link: https://patch.msgid.link/20260112184253.33376-1-bharat.singh7924@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e63a555eda65..9d962a85acab 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1551,6 +1551,22 @@ static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec, } } +static void alc245_fixup_hp_mute_led_v2_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 1 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 0; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} + /* turn on/off mic-mute LED per capture hook by coef bit */ static int coef_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -3816,6 +3832,7 @@ enum { ALC287_FIXUP_YOGA7_14ARB7_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, + ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, ALC287_FIXUP_THINKPAD_I2S_SPK, ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, @@ -6132,6 +6149,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc245_fixup_hp_mute_led_v1_coefbit, }, + [ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_v2_coefbit, + }, [ALC245_FIXUP_HP_X360_MUTE_LEDS] = { .type = HDA_FIXUP_FUNC, .v.func = alc245_fixup_hp_mute_led_coefbit, @@ -6620,6 +6641,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), From 3ce03297baff0ba116769044e4594fb324d4a551 Mon Sep 17 00:00:00 2001 From: fenugrec Date: Sun, 11 Jan 2026 16:36:40 -0500 Subject: [PATCH 176/341] ALSA: usb-audio: presonus s18xx uses little-endian Use __le32 types for USB control transfers Signed-off-by: fenugrec Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260111-preso_clean1-v2-1-44b4e5129a75@mail.com --- sound/usb/mixer_s1810c.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 6e09e074c0e7..93510aa0dc5e 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -82,13 +82,13 @@ * mixer and output but a different set for device. */ struct s1810c_ctl_packet { - u32 a; - u32 b; - u32 fixed1; - u32 fixed2; - u32 c; - u32 d; - u32 e; + __le32 a; + __le32 b; + __le32 fixed1; + __le32 fixed2; + __le32 c; + __le32 d; + __le32 e; }; #define SC1810C_CTL_LINE_SW 0 @@ -118,7 +118,7 @@ struct s1810c_ctl_packet { * being zero and different f1/f2. */ struct s1810c_state_packet { - u32 fields[63]; + __le32 fields[63]; }; #define SC1810C_STATE_48V_SW 58 @@ -140,14 +140,14 @@ snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a, struct s1810c_ctl_packet pkt = { 0 }; int ret = 0; - pkt.fixed1 = SC1810C_CMD_F1; - pkt.fixed2 = SC1810C_CMD_F2; + pkt.fixed1 = __cpu_to_le32(SC1810C_CMD_F1); + pkt.fixed2 = __cpu_to_le32(SC1810C_CMD_F2); - pkt.a = a; - pkt.b = b; - pkt.c = c; - pkt.d = d; - pkt.e = e; + pkt.a = __cpu_to_le32(a); + pkt.b = __cpu_to_le32(b); + pkt.c = __cpu_to_le32(c); + pkt.d = __cpu_to_le32(d); + pkt.e = __cpu_to_le32(e); ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SC1810C_CMD_REQ, @@ -176,8 +176,8 @@ snd_sc1810c_get_status_field(struct usb_device *dev, struct s1810c_state_packet pkt_in = { { 0 } }; int ret = 0; - pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1; - pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2; + pkt_out.fields[SC1810C_STATE_F1_IDX] = __cpu_to_le32(SC1810C_SET_STATE_F1); + pkt_out.fields[SC1810C_STATE_F2_IDX] = __cpu_to_le32(SC1810C_SET_STATE_F2); ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SC1810C_SET_STATE_REQ, SC1810C_SET_STATE_REQTYPE, @@ -197,7 +197,7 @@ snd_sc1810c_get_status_field(struct usb_device *dev, return ret; } - (*field) = pkt_in.fields[field_idx]; + (*field) = __le32_to_cpu(pkt_in.fields[field_idx]); (*seqnum)++; return 0; } From 78e35b0156c3d98e9a61c673fd585a9a01acc6dc Mon Sep 17 00:00:00 2001 From: fenugrec Date: Sun, 11 Jan 2026 16:36:41 -0500 Subject: [PATCH 177/341] ALSA: usb-audio: clean up presonus s1810 consts - Reorder some #define blocks - Document mixer/volume levels - Document some Ctl request fields (tag, len) - replace some magic numbers with macros No functional change intended. The information is based on reverse engineering. Signed-off-by: fenugrec Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260111-preso_clean1-v2-2-44b4e5129a75@mail.com --- sound/usb/mixer_s1810c.c | 202 ++++++++++++++++++++++++--------------- 1 file changed, 123 insertions(+), 79 deletions(-) diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 93510aa0dc5e..b4ce89afb25b 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -25,12 +25,6 @@ #include "helper.h" #include "mixer_s1810c.h" -#define SC1810C_CMD_REQ 160 -#define SC1810C_CMD_REQTYPE \ - (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT) -#define SC1810C_CMD_F1 0x50617269 -#define SC1810C_CMD_F2 0x14 - /* * DISCLAIMER: These are just guesses based on the * dumps I got. @@ -42,9 +36,8 @@ * * b selects an input channel (see below). * * c selects an output channel pair (see below). * * d selects left (0) or right (1) of that pair. - * * e 0-> disconnect, 0x01000000-> connect, - * 0x0109-> used for stereo-linking channels, - * e is also used for setting volume levels + * * e level : see MIXER_LEVEL_* defines below. + * Also used for setting volume levels * in which case b is also set so I guess * this way it is possible to set the volume * level from the specified input to the @@ -75,52 +68,98 @@ * For output (0x65): * * b is the output channel (see above). * * c is zero. - * * e I guess the same as with mixer except 0x0109 - * which I didn't see in my dumps. + * * e I guess the same as with mixer * - * The two fixed fields have the same values for - * mixer and output but a different set for device. + */ +/** struct s1810c_ctl_packet - basic vendor request + * @selector: device/mixer/output + * @b: request-dependant field b + * @tag: fixed value identifying type of request + * @len: sizeof this struct - 8 (excludes first 2 fields) + * i.e. for basic struct s1810c_ctl_packet: len is 5*4=0x14 + * @c: request-dependant field c + * @d: request-dependant field d + * @e: request-dependant field e + * + * See longer description above. This could be combined + * (as a union?) with the longer struct s1810c_state_packet */ struct s1810c_ctl_packet { - __le32 a; + __le32 selector; __le32 b; - __le32 fixed1; - __le32 fixed2; + __le32 tag; + __le32 len; __le32 c; __le32 d; __le32 e; }; +/** selectors for CMD request + */ +#define SC1810C_SEL_DEVICE 0 +#define SC1810C_SEL_MIXER 0x64 +#define SC1810C_SEL_OUTPUT 0x65 + + +/** control ids */ #define SC1810C_CTL_LINE_SW 0 #define SC1810C_CTL_MUTE_SW 1 #define SC1824C_CTL_MONO_SW 2 #define SC1810C_CTL_AB_SW 3 #define SC1810C_CTL_48V_SW 4 +/* USB Control (vendor) requests + */ +#define SC1810C_CMD_REQ 160 +#define SC1810C_CMD_REQTYPE \ + (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT) +#define SC1810C_CMD_TAG 0x50617269 +#define SC1810C_CMD_LEN 0x14 + #define SC1810C_SET_STATE_REQ 161 #define SC1810C_SET_STATE_REQTYPE SC1810C_CMD_REQTYPE -#define SC1810C_SET_STATE_F1 0x64656D73 -#define SC1810C_SET_STATE_F2 0xF4 +#define SC1810C_SET_STATE_TAG 0x64656D73 +#define SC1810C_SET_STATE_LEN 0xF4 #define SC1810C_GET_STATE_REQ 162 #define SC1810C_GET_STATE_REQTYPE \ (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN) -#define SC1810C_GET_STATE_F1 SC1810C_SET_STATE_F1 -#define SC1810C_GET_STATE_F2 SC1810C_SET_STATE_F2 +#define SC1810C_GET_STATE_TAG SC1810C_SET_STATE_TAG +#define SC1810C_GET_STATE_LEN SC1810C_SET_STATE_LEN -#define SC1810C_STATE_F1_IDX 2 -#define SC1810C_STATE_F2_IDX 3 +/** Mixer levels normally range from 0 (off) to 0x0100 0000 (0 dB). + * raw_level = 2^24 * 10^(db_level / 20), thus + * -3dB = 0xb53bf0 (technically, half-power -3.01...dB would be 0xb504f3) + * -96dB = 0x109 + * -99dB = 0xBC + * PC software sliders cover -96 to +10dB (0x0329 8b08), + * but the value 0 (-inf dB) can be used when e.g. Mixer Bypass is enabled. + * Unclear what the hardware's maximum value is. + * + * Note, when a channel is panned to two channels (stereo), + * the mixer level is set to slider value (by default -96dB) minus 3dB, + * which explains the -99dB value seen in USB captures. + */ +#define MIXER_LEVEL_MUTE 0 +#define MIXER_LEVEL_N99DB 0xbc +#define MIXER_LEVEL_N3DB 0xb53bf0 +#define MIXER_LEVEL_0DB 0x1000000 -/* +/** * This packet includes mixer volumes and * various other fields, it's an extended * version of ctl_packet, with a and b - * being zero and different f1/f2. + * being zero and different tag/length. */ struct s1810c_state_packet { __le32 fields[63]; }; +/** indices into s1810c_state_packet.fields[] + */ +#define SC1810C_STATE_TAG_IDX 2 +#define SC1810C_STATE_LEN_IDX 3 + #define SC1810C_STATE_48V_SW 58 #define SC1810C_STATE_LINE_SW 59 #define SC1810C_STATE_MUTE_SW 60 @@ -134,16 +173,16 @@ struct s1810_mixer_state { }; static int -snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a, +snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 sel, u32 b, u32 c, u32 d, u32 e) { struct s1810c_ctl_packet pkt = { 0 }; int ret = 0; - pkt.fixed1 = __cpu_to_le32(SC1810C_CMD_F1); - pkt.fixed2 = __cpu_to_le32(SC1810C_CMD_F2); + pkt.tag = __cpu_to_le32(SC1810C_CMD_TAG); + pkt.len = __cpu_to_le32(SC1810C_CMD_LEN); - pkt.a = __cpu_to_le32(a); + pkt.selector = __cpu_to_le32(sel); pkt.b = __cpu_to_le32(b); pkt.c = __cpu_to_le32(c); pkt.d = __cpu_to_le32(d); @@ -176,8 +215,8 @@ snd_sc1810c_get_status_field(struct usb_device *dev, struct s1810c_state_packet pkt_in = { { 0 } }; int ret = 0; - pkt_out.fields[SC1810C_STATE_F1_IDX] = __cpu_to_le32(SC1810C_SET_STATE_F1); - pkt_out.fields[SC1810C_STATE_F2_IDX] = __cpu_to_le32(SC1810C_SET_STATE_F2); + pkt_out.fields[SC1810C_STATE_TAG_IDX] = __cpu_to_le32(SC1810C_SET_STATE_TAG); + pkt_out.fields[SC1810C_STATE_LEN_IDX] = __cpu_to_le32(SC1810C_SET_STATE_LEN); ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SC1810C_SET_STATE_REQ, SC1810C_SET_STATE_REQTYPE, @@ -216,8 +255,8 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) switch (chip->usb_id) { case USB_ID(0x194f, 0x010c): /* 1810c */ /* Set initial volume levels ? */ - a = 0x64; - e = 0xbc; + a = SC1810C_SEL_MIXER; + e = MIXER_LEVEL_N99DB; for (n = 0; n < 2; n++) { off = n * 18; for (b = off; b < 18 + off; b++) { @@ -234,22 +273,21 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) * I noticed on UC that DAW channels have different * initial volumes, so this makes sense. */ - e = 0xb53bf0; + e = MIXER_LEVEL_N3DB; } /* Connect analog outputs ? */ - a = 0x65; - e = 0x01000000; + a = SC1810C_SEL_OUTPUT; for (b = 1; b < 3; b++) { - snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, MIXER_LEVEL_0DB); } - snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e); + snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, MIXER_LEVEL_0DB); /* Set initial volume levels for S/PDIF mappings ? */ - a = 0x64; - e = 0xbc; + a = SC1810C_SEL_MIXER; + e = MIXER_LEVEL_N99DB; c = 3; for (n = 0; n < 2; n++) { off = n * 18; @@ -257,26 +295,23 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); } - e = 0xb53bf0; + e = MIXER_LEVEL_N3DB; } /* Connect S/PDIF output ? */ - a = 0x65; - e = 0x01000000; - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + a = SC1810C_SEL_OUTPUT; + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB); /* Connect all outputs (again) ? */ - a = 0x65; - e = 0x01000000; + a = SC1810C_SEL_OUTPUT; for (b = 0; b < 4; b++) { - snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, MIXER_LEVEL_0DB); } /* Basic routing to get sound out of the device */ - a = 0x64; - e = 0x01000000; + a = SC1810C_SEL_MIXER; for (c = 0; c < 4; c++) { for (b = 0; b < 36; b++) { if ((c == 0 && b == 18) || /* DAW1/2 -> Main */ @@ -284,23 +319,29 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) (c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */ (c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */ /* Left */ - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_MUTE); b++; /* Right */ - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_MUTE); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_0DB); } else { /* Leave the rest disconnected */ - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_MUTE); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_MUTE); } } } /* Set initial volume levels for S/PDIF (again) ? */ - a = 0x64; - e = 0xbc; + a = SC1810C_SEL_MIXER; + e = MIXER_LEVEL_N99DB; c = 3; for (n = 0; n < 2; n++) { off = n * 18; @@ -308,29 +349,26 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); } - e = 0xb53bf0; + e = MIXER_LEVEL_N3DB; } /* Connect S/PDIF outputs (again) ? */ - a = 0x65; - e = 0x01000000; - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + a = SC1810C_SEL_OUTPUT; + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB); /* Again ? */ - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e); - snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB); break; case USB_ID(0x194f, 0x010d): /* 1824c */ /* Set all output faders to unity gain */ - a = 0x65; + a = SC1810C_SEL_OUTPUT; c = 0x00; - e = 0x01000000; - for (b = 0; b < 9; b++) { - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e); + snd_s1810c_send_ctl_packet(dev, a, b, c, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, a, b, c, 1, MIXER_LEVEL_0DB); } /* Set @@ -345,7 +383,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) * Daw 17 -> ADAT out 7, (left) Daw 18 -> ADAT out 8 (right) * Everything else muted */ - a = 0x64; + a = SC1810C_SEL_MIXER; /* The first Daw channel is channel 18 */ left = 18; @@ -354,14 +392,20 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) for (b = 0; b < 36; b++) { if (b == left) { - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x01000000); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_0DB); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_MUTE); } else if (b == right) { - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x01000000); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_MUTE); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_0DB); } else { - snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00); - snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 0, MIXER_LEVEL_MUTE); + snd_s1810c_send_ctl_packet(dev, + a, b, c, 1, MIXER_LEVEL_MUTE); } } left += 2; From 24175015263d3c72166902a5a4451c18dc836d56 Mon Sep 17 00:00:00 2001 From: Emil-Juhl Date: Tue, 13 Jan 2026 11:58:48 +0100 Subject: [PATCH 178/341] ASoC: tlv320adcx140: power on/off the device on demand The tlv320adcx140 can be connected to controllable AVDD/IOVDD regulators which when disabled will reset the registers to their default. In preparation for that switch to register writes to cache only when powered off and sync the cached values to the registers when powered back on. Signed-off-by: Emil-Juhl Signed-off-by: Sascha Hauer Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-5-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 89 +++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index fdf4a9add852..444c0e80f090 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -121,6 +121,34 @@ static const struct reg_default adcx140_reg_defaults[] = { { ADCX140_DEV_STS1, 0x80 }, }; +static const struct regmap_range adcx140_wr_ranges[] = { + regmap_reg_range(ADCX140_PAGE_SELECT, ADCX140_SLEEP_CFG), + regmap_reg_range(ADCX140_SHDN_CFG, ADCX140_SHDN_CFG), + regmap_reg_range(ADCX140_ASI_CFG0, ADCX140_ASI_CFG2), + regmap_reg_range(ADCX140_ASI_CH1, ADCX140_MST_CFG1), + regmap_reg_range(ADCX140_CLK_SRC, ADCX140_CLK_SRC), + regmap_reg_range(ADCX140_PDMCLK_CFG, ADCX140_GPO_CFG3), + regmap_reg_range(ADCX140_GPO_VAL, ADCX140_GPO_VAL), + regmap_reg_range(ADCX140_GPI_CFG0, ADCX140_GPI_CFG1), + regmap_reg_range(ADCX140_GPI_MON, ADCX140_GPI_MON), + regmap_reg_range(ADCX140_INT_CFG, ADCX140_INT_MASK0), + regmap_reg_range(ADCX140_BIAS_CFG, ADCX140_CH4_CFG4), + regmap_reg_range(ADCX140_CH5_CFG2, ADCX140_CH5_CFG4), + regmap_reg_range(ADCX140_CH6_CFG2, ADCX140_CH6_CFG4), + regmap_reg_range(ADCX140_CH7_CFG2, ADCX140_CH7_CFG4), + regmap_reg_range(ADCX140_CH8_CFG2, ADCX140_CH8_CFG4), + regmap_reg_range(ADCX140_DSP_CFG0, ADCX140_DRE_CFG0), + regmap_reg_range(ADCX140_AGC_CFG0, ADCX140_AGC_CFG0), + regmap_reg_range(ADCX140_IN_CH_EN, ADCX140_PWR_CFG), + regmap_reg_range(ADCX140_PHASE_CALIB, ADCX140_PHASE_CALIB), + regmap_reg_range(0x7e, 0x7e), +}; + +static const struct regmap_access_table adcx140_wr_table = { + .yes_ranges = adcx140_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(adcx140_wr_ranges), +}; + static const struct regmap_range_cfg adcx140_ranges[] = { { .range_min = 0, @@ -156,6 +184,7 @@ static const struct regmap_config adcx140_i2c_regmap = { .num_ranges = ARRAY_SIZE(adcx140_ranges), .max_register = 12 * 128, .volatile_reg = adcx140_volatile, + .wr_table = &adcx140_wr_table, }; /* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ @@ -1073,19 +1102,73 @@ out: return ret; } +static int adcx140_pwr_off(struct adcx140_priv *adcx140) +{ + regcache_cache_only(adcx140->regmap, true); + regcache_mark_dirty(adcx140->regmap); + + /* Assert the reset GPIO */ + gpiod_set_value_cansleep(adcx140->gpio_reset, 0); + + /* + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, + * TLV320ADC6140 Rev. A 8.4.1: + * wait for hw shutdown (25ms) + >= 1ms + */ + usleep_range(30000, 100000); + + return 0; +} + +static int adcx140_pwr_on(struct adcx140_priv *adcx140) +{ + int ret; + + /* De-assert the reset GPIO */ + gpiod_set_value_cansleep(adcx140->gpio_reset, 1); + + /* + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, + * TLV320ADC6140 Rev. A 8.4.2: + * wait >= 10 ms after entering sleep mode. + */ + usleep_range(10000, 100000); + + regcache_cache_only(adcx140->regmap, false); + + /* Flush the regcache */ + ret = regcache_sync(adcx140->regmap); + if (ret) { + dev_err(adcx140->dev, "Failed to restore register map: %d\n", + ret); + return ret; + } + + return 0; +} + static int adcx140_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level prev_level + = snd_soc_component_get_bias_level(component); switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: + if (prev_level == SND_SOC_BIAS_STANDBY) + adcx140_pwr_ctrl(adcx140, true); + break; case SND_SOC_BIAS_STANDBY: - adcx140_pwr_ctrl(adcx140, true); + if (prev_level == SND_SOC_BIAS_PREPARE) + adcx140_pwr_ctrl(adcx140, false); + if (prev_level == SND_SOC_BIAS_OFF) + return adcx140_pwr_on(adcx140); break; case SND_SOC_BIAS_OFF: - adcx140_pwr_ctrl(adcx140, false); + if (prev_level == SND_SOC_BIAS_STANDBY) + return adcx140_pwr_off(adcx140); break; } @@ -1186,6 +1269,8 @@ static int adcx140_i2c_probe(struct i2c_client *i2c) return ret; } + regcache_cache_only(adcx140->regmap, true); + i2c_set_clientdata(i2c, adcx140); return devm_snd_soc_register_component(&i2c->dev, From 57be1f67401005e33e8c88db6707b4482b509589 Mon Sep 17 00:00:00 2001 From: Emil-Juhl Date: Tue, 13 Jan 2026 11:58:49 +0100 Subject: [PATCH 179/341] ASoC: tlv320adcx140: add avdd and iovdd supply The datasheet, under "10 Power Supply Recommendations" section, specifies that both the AVDD and IOVDD supplies must be up and stable for at least 100us before the SHDNZ can be released. After that, the chip is ready to receive commands after another 2ms. Currently the driver doesn't contain any options to bind AVDD and IOVDD supplies to the tlv320adcx140. This commit adds bindings for AVDD and IOVDD supplies which the driver will enable when used. Signed-off-by: Emil-Juhl Signed-off-by: Sascha Hauer Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-6-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 444c0e80f090..a7200e149e5f 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -22,8 +22,16 @@ #include "tlv320adcx140.h" +static const char *const adcx140_supply_names[] = { + "avdd", + "iovdd", +}; + +#define ADCX140_NUM_SUPPLIES ARRAY_SIZE(adcx140_supply_names) + struct adcx140_priv { struct regulator *supply_areg; + struct regulator_bulk_data supplies[ADCX140_NUM_SUPPLIES]; struct gpio_desc *gpio_reset; struct regmap *regmap; struct device *dev; @@ -1104,6 +1112,8 @@ out: static int adcx140_pwr_off(struct adcx140_priv *adcx140) { + int ret; + regcache_cache_only(adcx140->regmap, true); regcache_mark_dirty(adcx140->regmap); @@ -1117,6 +1127,14 @@ static int adcx140_pwr_off(struct adcx140_priv *adcx140) */ usleep_range(30000, 100000); + /* Power off the regulators, `avdd` and `iovdd` */ + ret = regulator_bulk_disable(ARRAY_SIZE(adcx140->supplies), + adcx140->supplies); + if (ret) { + dev_err(adcx140->dev, "Failed to disable supplies: %d\n", ret); + return ret; + } + return 0; } @@ -1124,6 +1142,14 @@ static int adcx140_pwr_on(struct adcx140_priv *adcx140) { int ret; + /* Power on the regulators, `avdd` and `iovdd` */ + ret = regulator_bulk_enable(ARRAY_SIZE(adcx140->supplies), + adcx140->supplies); + if (ret) { + dev_err(adcx140->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + /* De-assert the reset GPIO */ gpiod_set_value_cansleep(adcx140->gpio_reset, 1); @@ -1234,6 +1260,16 @@ static int adcx140_i2c_probe(struct i2c_client *i2c) adcx140->phase_calib_on = false; adcx140->dev = &i2c->dev; + for (int i = 0; i < ADCX140_NUM_SUPPLIES; i++) + adcx140->supplies[i].supply = adcx140_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ADCX140_NUM_SUPPLIES, + adcx140->supplies); + if (ret) { + dev_err_probe(&i2c->dev, ret, "Failed to request supplies\n"); + return ret; + } + adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(adcx140->gpio_reset)) From 5682093fc80674ee7f4a96dd9f0f1919111ab16d Mon Sep 17 00:00:00 2001 From: Emil-Juhl Date: Tue, 13 Jan 2026 11:58:50 +0100 Subject: [PATCH 180/341] ASoC: dt-bindings: clarify areg-supply documentation The documentation for areg-supply could cause confusion mainly in terms of the relationship between AREG and AVDD. According to the datasheet[1] the AREG can be one of two cases: 1) an external 1.8V supply 2) generated by an internal regulator (hence a 1.8V output) [1] https://www.ti.com/lit/ds/symlink/tlv320adc5140.pdf Signed-off-by: Emil-Juhl Signed-off-by: Sascha Hauer Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-7-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml index 876fa97bfbcd..b34ea7824360 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml @@ -41,8 +41,8 @@ properties: areg-supply: description: | - Regulator with AVDD at 3.3V. If not defined then the internal regulator - is enabled. + External supply of 1.8V. If not defined then the internal regulator is + enabled instead. ti,mic-bias-source: description: | From 4a1bc07e6d9ecd29b95c41e34402793619f1874a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 13 Jan 2026 11:58:51 +0100 Subject: [PATCH 181/341] ASoC: dt-bindings: add avdd and iovdd supply Add bindings for the avdd-supply and iovdd-supply which are named after the corresponding pins on the tlv320adcx140 chips. Acked-by: Krzysztof Kozlowski Signed-off-by: Sascha Hauer Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-8-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml index b34ea7824360..a93de2debbb4 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml @@ -44,6 +44,9 @@ properties: External supply of 1.8V. If not defined then the internal regulator is enabled instead. + avdd-supply: true + iovdd-supply: true + ti,mic-bias-source: description: | Indicates the source for MIC Bias. From 2219823f7d6ac01c8eb55b90e954b4466146c397 Mon Sep 17 00:00:00 2001 From: Emil-Juhl Date: Tue, 13 Jan 2026 11:58:52 +0100 Subject: [PATCH 182/341] ASoC: tlv320adcx140: add kcontrol for num biquads The tlv320adcx140 chips have a configurable amount of biquad filters enabled per input channel. Currently this number is always left at the default value of 2 biquads per channel. This commit adds a kcontrol to allow runtime configuration of the amount of biquads per channel. The configuration is controlled by bits [5-6] in the DSP_CFG1 register. Signed-off-by: Emil-Juhl Signed-off-by: Sascha Hauer Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-9-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index a7200e149e5f..e7c607af642f 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -709,6 +709,8 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = { SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2, 0, 0xff, 0, dig_vol_tlv), ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"), + + SOC_SINGLE("Biquads Per Channel", ADCX140_DSP_CFG1, 5, 3, 0), }; static int adcx140_reset(struct adcx140_priv *adcx140) From 8a98e7f55f975360975083166e21982ef307b8fd Mon Sep 17 00:00:00 2001 From: Emil Svendsen Date: Tue, 13 Jan 2026 11:58:53 +0100 Subject: [PATCH 183/341] ASoC: tlv320adcx140: add channel sum control Add control for channel summation. 3 modes are supported: 1. "Disabled": Normal operation 2. "2 Channel": Every two channels are summed and divided by 2 Out 1 <- (CH1 + CH2) / 2 Out 2 <- (CH1 + CH2) / 2 Out 3 <- (CH3 + CH4) / 2 Out 4 <- (CH3 + CH4) / 2 3. "4 Channel": Every four channels are summed and divided by 4 Out 1 <- (CH1 + CH2 + CH3 + CH4) / 4 Out 2 <- (CH1 + CH2 + CH3 + CH4) / 4 Out 3 <- (CH1 + CH2 + CH3 + CH4) / 4 Out 4 <- (CH1 + CH2 + CH3 + CH4) / 4 Signed-off-by: Emil Svendsen Signed-off-by: Sascha Hauer Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-10-8f7ecec525c8@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index e7c607af642f..ac6aab8d7224 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -222,6 +222,13 @@ static const struct snd_kcontrol_new decimation_filter_controls[] = { SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum), }; +static const char * const channel_summation_text[] = { + "Disabled", "2 Channel", "4 Channel" +}; + +static SOC_ENUM_SINGLE_DECL(channel_summation_enum, ADCX140_DSP_CFG0, 2, + channel_summation_text); + static const char * const pdmclk_text[] = { "2.8224 MHz", "1.4112 MHz", "705.6 kHz", "5.6448 MHz" }; @@ -711,6 +718,8 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = { ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"), SOC_SINGLE("Biquads Per Channel", ADCX140_DSP_CFG1, 5, 3, 0), + + SOC_ENUM("Channel Summation", channel_summation_enum), }; static int adcx140_reset(struct adcx140_priv *adcx140) From 18d524de812ff37e7de12a2acddfe7eee6b4ca3c Mon Sep 17 00:00:00 2001 From: Bharadwaj Raju Date: Wed, 14 Jan 2026 16:03:24 +0530 Subject: [PATCH 184/341] ASoC: dt-bindings: document dvdd-supply property for awinic,aw88261 Add (and require) the dvdd-supply property for awinic,aw88261 in the awinic,aw88395.yaml binding. The chip needs DVDD to power on, and currently there are no users of this compatible in the kernel device trees, so we should be fine to change the ABI in this case. Signed-off-by: Bharadwaj Raju Link: https://patch.msgid.link/20260114-aw88261-dvdd-v2-1-ef485b82a7a7@machinesoul.in Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/awinic,aw88395.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index bb92d6ca3144..994d68c074a9 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -33,6 +33,8 @@ properties: reset-gpios: maxItems: 1 + dvdd-supply: true + awinic,audio-channel: description: It is used to distinguish multiple PA devices, so that different @@ -65,6 +67,17 @@ allOf: then: properties: reset-gpios: false + - if: + properties: + compatible: + contains: + const: awinic,aw88261 + then: + required: + - dvdd-supply + else: + properties: + dvdd-supply: false unevaluatedProperties: false From 519d0a6b2ca5a891340b6c24a4c40545f518e1a8 Mon Sep 17 00:00:00 2001 From: Bharadwaj Raju Date: Wed, 14 Jan 2026 16:03:25 +0530 Subject: [PATCH 185/341] ASoC: codecs: aw88261: use dvdd-supply regulator The AW88261 needs the DVDD pin to be powered on to start up. Get and enable the dvdd-supply regulator. Signed-off-by: Bharadwaj Raju Link: https://patch.msgid.link/20260114-aw88261-dvdd-v2-2-ef485b82a7a7@machinesoul.in Signed-off-by: Mark Brown --- sound/soc/codecs/aw88261.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 810c90f5e783..29b3fc8a1ea4 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "aw88261.h" #include "aw88395/aw88395_data_type.h" @@ -1190,6 +1191,10 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct unsigned int chip_id; int ret; + ret = devm_regulator_get_enable(&i2c->dev, "dvdd"); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to enable dvdd supply\n"); + /* read chip id */ ret = regmap_read(regmap, AW88261_ID_REG, &chip_id); if (ret) { From 9e3d4f794cbe9a4e286b3052cb97908005807aee Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 9 Jan 2026 14:52:03 +0000 Subject: [PATCH 186/341] ASoC: SDCA: Add SDCA IRQ enable/disable helpers Add helpers to enable and disable the SDCA IRQs by Function. These are useful to sequence the powering down and up around system suspend. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260109145206.3456151-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 7 +++ sound/soc/sdca/sdca_interrupts.c | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 8f13417d129a..9bcb5d8fd592 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -84,4 +84,11 @@ int sdca_irq_populate(struct sdca_function_data *function, struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, struct regmap *regmap, int irq); +void sdca_irq_enable_early(struct sdca_function_data *function, + struct sdca_interrupt_info *info); +void sdca_irq_enable(struct sdca_function_data *function, + struct sdca_interrupt_info *info); +void sdca_irq_disable(struct sdca_function_data *function, + struct sdca_interrupt_info *info); + #endif diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index ff3a7e405fdc..afef7bbf613c 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -541,3 +541,79 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, return info; } EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); + +static void irq_enable_flags(struct sdca_function_data *function, + struct sdca_interrupt_info *info, bool early) +{ + struct sdca_interrupt *interrupt; + int i; + + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + interrupt = &info->irqs[i]; + + if (!interrupt || interrupt->function != function) + continue; + + switch (SDCA_CTL_TYPE(interrupt->entity->type, + interrupt->control->sel)) { + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + if (early) + enable_irq(interrupt->irq); + break; + default: + if (!early) + enable_irq(interrupt->irq); + break; + } + } +} + +/** + * sdca_irq_enable_early - Re-enable early SDCA IRQs for a given function + * @function: Pointer to the SDCA Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * The early version of the IRQ enable allows enabling IRQs which may be + * necessary to bootstrap functionality for other IRQs, such as the FDL + * process. + */ +void sdca_irq_enable_early(struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + irq_enable_flags(function, info, true); +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_enable_early, "SND_SOC_SDCA"); + +/** + * sdca_irq_enable - Re-enable SDCA IRQs for a given function + * @function: Pointer to the SDCA Function. + * @info: Pointer to the SDCA interrupt info for this device. + */ +void sdca_irq_enable(struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + irq_enable_flags(function, info, false); +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_enable, "SND_SOC_SDCA"); + +/** + * sdca_irq_disable - Disable SDCA IRQs for a given function + * @function: Pointer to the SDCA Function. + * @info: Pointer to the SDCA interrupt info for this device. + */ +void sdca_irq_disable(struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + struct sdca_interrupt *interrupt; + int i; + + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + interrupt = &info->irqs[i]; + + if (!interrupt || interrupt->function != function) + continue; + + disable_irq(interrupt->irq); + } +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_disable, "SND_SOC_SDCA"); From 7a5214f769c7c953c58971027a762f2e191057d4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 9 Jan 2026 14:52:04 +0000 Subject: [PATCH 187/341] ASoC: SDCA: Add basic system suspend support Add basic system suspend support. Disable the IRQs and force runtime suspend, during system suspend, because the device will likely fully power down during suspend. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260109145206.3456151-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 33 ++++++++++++++++++ sound/soc/sdca/sdca_class_function.c | 51 ++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 349d32933ba8..6d19a183683e 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -238,6 +238,38 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id return 0; } +static int class_suspend(struct device *dev) +{ + struct sdca_class_drv *drv = dev_get_drvdata(dev); + int ret; + + disable_irq(drv->sdw->irq); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + dev_err(dev, "failed to force suspend: %d\n", ret); + return ret; + } + + return 0; +} + +static int class_resume(struct device *dev) +{ + struct sdca_class_drv *drv = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "failed to force resume: %d\n", ret); + return ret; + } + + enable_irq(drv->sdw->irq); + + return 0; +} + static int class_runtime_suspend(struct device *dev) { struct sdca_class_drv *drv = dev_get_drvdata(dev); @@ -278,6 +310,7 @@ err: } static const struct dev_pm_ops class_pm_ops = { + SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume) RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL) }; diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 416948cfb5cb..7d0a6c0adbfb 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -33,6 +33,7 @@ struct class_function_drv { struct sdca_class_drv *core; struct sdca_function_data *function; + bool suspended; }; static void class_function_regmap_lock(void *data) @@ -417,6 +418,14 @@ static int class_function_runtime_resume(struct device *dev) regcache_mark_dirty(drv->regmap); regcache_cache_only(drv->regmap, false); + if (drv->suspended) { + sdca_irq_enable_early(drv->function, drv->core->irq_info); + /* TODO: Add FDL process between early and late IRQs */ + sdca_irq_enable(drv->function, drv->core->irq_info); + + drv->suspended = false; + } + ret = regcache_sync(drv->regmap); if (ret) { dev_err(drv->dev, "failed to restore register cache: %d\n", ret); @@ -431,7 +440,49 @@ err: return ret; } +static int class_function_suspend(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); + int ret; + + drv->suspended = true; + + /* Ensure runtime resume runs on resume */ + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "failed to resume for suspend: %d\n", ret); + return ret; + } + + sdca_irq_disable(drv->function, drv->core->irq_info); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + dev_err(dev, "failed to force suspend: %d\n", ret); + return ret; + } + + pm_runtime_put_noidle(dev); + + return 0; +} + +static int class_function_resume(struct device *dev) +{ + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "failed to force resume: %d\n", ret); + return ret; + } + + return 0; +} + static const struct dev_pm_ops class_function_pm_ops = { + SYSTEM_SLEEP_PM_OPS(class_function_suspend, class_function_resume) RUNTIME_PM_OPS(class_function_runtime_suspend, class_function_runtime_resume, NULL) }; From ffd7e8a101110cba86925a2906d925e0db7102f3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 9 Jan 2026 14:52:05 +0000 Subject: [PATCH 188/341] ASoC: SDCA: Device boot into the system suspend process When system suspending the device may be powered off, this means all state will be lost and the firmware may need to be re-downloaded. Add the necessary calls to bring the device back up. This also requires that that the FDL (firmware download) IRQ handler is modified to allow it to run before runtime PM has been fully restored. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260109145206.3456151-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 80 +++++++++++++++++++--------- sound/soc/sdca/sdca_interrupts.c | 17 ++++-- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 7d0a6c0adbfb..bbf486d9a3d0 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -211,6 +211,31 @@ static const struct snd_soc_component_driver class_function_component_drv = { .endianness = 1, }; +static int class_function_init_device(struct class_function_drv *drv, + unsigned int status) +{ + int ret; + + if (!(status & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) { + dev_dbg(drv->dev, "reset function device\n"); + + ret = sdca_reset_function(drv->dev, drv->function, drv->regmap); + if (ret) + return ret; + } + + if (status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) { + dev_dbg(drv->dev, "write initialisation\n"); + + ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap, + drv->function); + if (ret) + return ret; + } + + return 0; +} + static int class_function_boot(struct class_function_drv *drv) { unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr, @@ -225,31 +250,9 @@ static int class_function_boot(struct class_function_drv *drv) return ret; } - if (!(val & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) { - dev_dbg(drv->dev, "reset function device\n"); - - ret = sdca_reset_function(drv->dev, drv->function, drv->regmap); - if (ret) - return ret; - } - - if (val & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) { - dev_dbg(drv->dev, "write initialisation\n"); - - ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap, - drv->function); - if (ret) - return ret; - - ret = regmap_write(drv->regmap, reg, - SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); - if (ret < 0) { - dev_err(drv->dev, - "failed to clear function init status: %d\n", - ret); - return ret; - } - } + ret = class_function_init_device(drv, val); + if (ret) + return ret; /* Start FDL process */ ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function, @@ -419,10 +422,35 @@ static int class_function_runtime_resume(struct device *dev) regcache_cache_only(drv->regmap, false); if (drv->suspended) { + unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr, + SDCA_ENTITY_TYPE_ENTITY_0, + SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0); + unsigned int val; + + ret = regmap_read(drv->regmap, reg, &val); + if (ret < 0) { + dev_err(drv->dev, "failed to read function status: %d\n", ret); + goto err; + } + + ret = class_function_init_device(drv, val); + if (ret) + goto err; + sdca_irq_enable_early(drv->function, drv->core->irq_info); - /* TODO: Add FDL process between early and late IRQs */ + + ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info); + if (ret) + goto err; + sdca_irq_enable(drv->function, drv->core->irq_info); + ret = regmap_write(drv->regmap, reg, 0xFF); + if (ret < 0) { + dev_err(drv->dev, "failed to clear function status: %d\n", ret); + goto err; + } + drv->suspended = false; } diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index afef7bbf613c..cc40732c30cc 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -205,10 +205,16 @@ static irqreturn_t fdl_owner_handler(int irq, void *data) irqreturn_t irqret = IRQ_NONE; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "failed to resume for fdl: %d\n", ret); - goto error; + /* + * FDL has to run from the system resume handler, at which point + * we can't wait for the pm runtime. + */ + if (completion_done(&dev->power.completion)) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "failed to resume for fdl: %d\n", ret); + goto error; + } } ret = sdca_fdl_process(interrupt); @@ -217,7 +223,8 @@ static irqreturn_t fdl_owner_handler(int irq, void *data) irqret = IRQ_HANDLED; error: - pm_runtime_put(dev); + if (completion_done(&dev->power.completion)) + pm_runtime_put(dev); return irqret; } From da7afdc79cba00f952df12cd579e44832d829c0a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 9 Jan 2026 14:52:06 +0000 Subject: [PATCH 189/341] ASoC: SDCA: Add lock to serialise the Function initialisation To avoid issues on some devices serialise the boot of each SDCA Function from the others. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260109145206.3456151-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 1 + sound/soc/sdca/sdca_class.h | 2 ++ sound/soc/sdca/sdca_class_function.c | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 6d19a183683e..918b638acb57 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -205,6 +205,7 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id drv->dev = dev; drv->sdw = sdw; mutex_init(&drv->regmap_lock); + mutex_init(&drv->init_lock); dev_set_drvdata(drv->dev, drv); diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h index bb4c9dd12429..6f24ea2bbd38 100644 --- a/sound/soc/sdca/sdca_class.h +++ b/sound/soc/sdca/sdca_class.h @@ -28,6 +28,8 @@ struct sdca_class_drv { struct sdca_interrupt_info *irq_info; struct mutex regmap_lock; + /* Serialise function initialisations */ + struct mutex init_lock; struct work_struct boot_work; struct completion device_attach; diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index bbf486d9a3d0..0afa41c1ee93 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -244,6 +245,8 @@ static int class_function_boot(struct class_function_drv *drv) unsigned int val; int ret; + guard(mutex)(&drv->core->init_lock); + ret = regmap_read(drv->regmap, reg, &val); if (ret < 0) { dev_err(drv->dev, "failed to read function status: %d\n", ret); @@ -418,6 +421,8 @@ static int class_function_runtime_resume(struct device *dev) struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); int ret; + guard(mutex)(&drv->core->init_lock); + regcache_mark_dirty(drv->regmap); regcache_cache_only(drv->regmap, false); From 850c9884b917f440e659a09dd309a46a136adada Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 9 Jan 2026 01:15:11 +0000 Subject: [PATCH 190/341] ASoC: intel: convert to snd_soc_dapm_xxx() This patch uses snd_soc_card_to_dapm() to get dapm from card Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87tswv1t9c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_ti_common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sof_ti_common.c b/sound/soc/intel/boards/sof_ti_common.c index 218c3536723c..e527bdeb787e 100644 --- a/sound/soc/intel/boards/sof_ti_common.c +++ b/sound/soc/intel/boards/sof_ti_common.c @@ -40,9 +40,10 @@ static struct snd_soc_dai_link_component tas2563_dai_link_components[] = { static int tas2563_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); int ret; - ret = snd_soc_dapm_new_controls(&card->dapm, tas2563_spk_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, tas2563_spk_dapm_widgets, ARRAY_SIZE(tas2563_spk_dapm_widgets)); if (ret) { dev_err(rtd->dev, "unable to add dapm widgets, ret %d\n", ret); @@ -56,7 +57,7 @@ static int tas2563_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, tas2563_spk_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, tas2563_spk_dapm_routes, ARRAY_SIZE(tas2563_spk_dapm_routes)); if (ret) dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); From 10303b32519f52a5afd40593a507543143c8ec6a Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Tue, 13 Jan 2026 09:26:01 +0000 Subject: [PATCH 191/341] dt-bindings: sound: google,goldfish-audio: Convert to DT schema Convert the Android Goldfish Audio binding to DT schema format. Move the file to the sound directory to match the subsystem. Update the example node name to 'sound' to comply with generic node naming standards. Signed-off-by: Kuan-Wei Chiu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260113092602.3197681-6-visitorckw@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/goldfish/audio.txt | 17 --------- .../bindings/sound/google,goldfish-audio.yaml | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+), 17 deletions(-) delete mode 100644 Documentation/devicetree/bindings/goldfish/audio.txt create mode 100644 Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml diff --git a/Documentation/devicetree/bindings/goldfish/audio.txt b/Documentation/devicetree/bindings/goldfish/audio.txt deleted file mode 100644 index d043fda433ba..000000000000 --- a/Documentation/devicetree/bindings/goldfish/audio.txt +++ /dev/null @@ -1,17 +0,0 @@ -Android Goldfish Audio - -Android goldfish audio device generated by android emulator. - -Required properties: - -- compatible : should contain "google,goldfish-audio" to match emulator -- reg : -- interrupts : - -Example: - - goldfish_audio@9030000 { - compatible = "google,goldfish-audio"; - reg = <0x9030000 0x100>; - interrupts = <0x4>; - }; diff --git a/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml new file mode 100644 index 000000000000..d395a5cbc945 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/google,goldfish-audio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Android Goldfish Audio + +maintainers: + - Kuan-Wei Chiu + +description: + Android goldfish audio device generated by Android emulator. + +properties: + compatible: + const: google,goldfish-audio + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + sound@9030000 { + compatible = "google,goldfish-audio"; + reg = <0x9030000 0x100>; + interrupts = <4>; + }; From 61d2a7699ab39d448f44919ef15c16187e6f70ec Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Jan 2026 11:46:07 +0000 Subject: [PATCH 192/341] ASoC: SDCA: Tidy up some memory allocations It is slightly better to deference the type being allocate for a sizeof rather than manually using the type. Saves effort if types change in the future. This results in no functional changes, just tidies up the style of the code a little. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260115114607.271990-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_fdl.c | 2 +- sound/soc/sdca/sdca_functions.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 3180ebd07c40..8bee9f23c473 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -487,7 +487,7 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) struct device *dev = interrupt->dev; struct fdl_state *fdl_state; - fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL); + fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL); if (!fdl_state) return -ENOMEM; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index acac066f1d8d..80c71116e6d4 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -952,7 +952,7 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti } control->values = devm_kcalloc(dev, hweight64(control->cn_list), - sizeof(int), GFP_KERNEL); + sizeof(*control->values), GFP_KERNEL); if (!control->values) return -ENOMEM; @@ -2048,7 +2048,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list", filesets_list, num_sets); - sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL); + sets = devm_kcalloc(dev, num_sets, sizeof(*sets), GFP_KERNEL); if (!sets) return -ENOMEM; @@ -2074,7 +2074,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, dev_dbg(dev, "fileset: %#x\n", filesets_list[i]); files = devm_kcalloc(dev, num_entries / mult_fileset, - sizeof(struct sdca_fdl_file), GFP_KERNEL); + sizeof(*files), GFP_KERNEL); if (!files) return -ENOMEM; From 4711b292b440a8404833df0e0ba96dad86600a84 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Fri, 16 Jan 2026 00:13:59 +0800 Subject: [PATCH 193/341] ASoC: es8328: Propagate error codes from regmap updates In es8328_hw_params(), the return value of snd_soc_component_update_bits() was ignored. This could lead to silent failures where the hardware is left in an inconsistent state if a regmap write fails. Check the return value of regmap updates and propagate any errors back to the ALSA core. Return 0 on success to match the DAI ops convention. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260115161359.41979-1-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index bd74e896d602..38340f292282 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -461,6 +461,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + int ret; int i; int reg; int wl; @@ -494,9 +495,12 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, es8328->mclkdiv2 = 0; } - snd_soc_component_update_bits(component, ES8328_MASTERMODE, - ES8328_MASTERMODE_MCLKDIV2, - es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0); + ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MCLKDIV2, + es8328->mclkdiv2 ? + ES8328_MASTERMODE_MCLKDIV2 : 0); + if (ret < 0) + return ret; switch (params_width(params)) { case 16: @@ -519,18 +523,26 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - snd_soc_component_update_bits(component, ES8328_DACCONTROL1, - ES8328_DACCONTROL1_DACWL_MASK, - wl << ES8328_DACCONTROL1_DACWL_SHIFT); + ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACWL_MASK, + wl << ES8328_DACCONTROL1_DACWL_SHIFT); + if (ret < 0) + return ret; es8328->playback_fs = params_rate(params); es8328_set_deemph(component); - } else - snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, - ES8328_ADCCONTROL4_ADCWL_MASK, - wl << ES8328_ADCCONTROL4_ADCWL_SHIFT); + } else { + ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCWL_MASK, + wl << ES8328_ADCCONTROL4_ADCWL_SHIFT); + if (ret < 0) + return ret; + } - return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio); + ret = snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio); + if (ret < 0) + return ret; + return 0; } static int es8328_set_sysclk(struct snd_soc_dai *codec_dai, From 60e8451be1f7af4b51540f2cfd65c9c85af752e9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 15 Jan 2026 13:56:22 +0100 Subject: [PATCH 194/341] ASoC: dt-bindings: mt8192-afe-pcm: Fix clocks and clock-names Both clocks and clock-names are missing (a lot of) entries: add all the used audio clocks and their description and also fix the example node. Signed-off-by: AngeloGioacchino Del Regno Fixes: c861af7861aa ("ASoC: dt-bindings: mediatek: mt8192: re-add audio afe document") Link: https://patch.msgid.link/20260115125624.73598-3-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../bindings/sound/mt8192-afe-pcm.yaml | 176 ++++++++++++++++-- 1 file changed, 162 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml index 8ddf49b0040d..16ae3328f70d 100644 --- a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml @@ -47,16 +47,118 @@ properties: - description: AFE clock - description: ADDA DAC clock - description: ADDA DAC pre-distortion clock - - description: audio infra sys clock - - description: audio infra 26M clock + - description: ADDA ADC clock + - description: ADDA6 ADC clock + - description: Audio low-jitter 22.5792m clock + - description: Audio low-jitter 24.576m clock + - description: Audio PLL1 tuner clock + - description: Audio PLL2 tuner clock + - description: Audio Time-Division Multiplexing interface clock + - description: ADDA ADC Sine Generator clock + - description: audio Non-LE clock + - description: Audio DAC High-Resolution clock + - description: Audio High-Resolution ADC clock + - description: Audio High-Resolution ADC SineGen clock + - description: Audio ADDA6 High-Resolution ADC clock + - description: Tertiary ADDA DAC clock + - description: Tertiary ADDA DAC pre-distortion clock + - description: Tertiary ADDA DAC Sine Generator clock + - description: Tertiary ADDA DAC High-Resolution clock + - description: Audio infra sys clock + - description: Audio infra 26M clock + - description: Mux for audio clock + - description: Mux for audio internal bus clock + - description: Mux main divider by 4 + - description: Primary audio mux + - description: Primary audio PLL + - description: Secondary audio mux + - description: Secondary audio PLL + - description: Primary audio en-generator clock + - description: Primary PLL divider by 4 for IEC + - description: Secondary audio en-generator clock + - description: Secondary PLL divider by 4 for IEC + - description: Mux selector for I2S port 0 + - description: Mux selector for I2S port 1 + - description: Mux selector for I2S port 2 + - description: Mux selector for I2S port 3 + - description: Mux selector for I2S port 4 + - description: Mux selector for I2S port 5 + - description: Mux selector for I2S port 6 + - description: Mux selector for I2S port 7 + - description: Mux selector for I2S port 8 + - description: Mux selector for I2S port 9 + - description: APLL1 and APLL2 divider for I2S port 0 + - description: APLL1 and APLL2 divider for I2S port 1 + - description: APLL1 and APLL2 divider for I2S port 2 + - description: APLL1 and APLL2 divider for I2S port 3 + - description: APLL1 and APLL2 divider for I2S port 4 + - description: APLL1 and APLL2 divider for IEC + - description: APLL1 and APLL2 divider for I2S port 5 + - description: APLL1 and APLL2 divider for I2S port 6 + - description: APLL1 and APLL2 divider for I2S port 7 + - description: APLL1 and APLL2 divider for I2S port 8 + - description: APLL1 and APLL2 divider for I2S port 9 + - description: Top mux for audio subsystem + - description: 26MHz clock for audio subsystem clock-names: items: - const: aud_afe_clk - const: aud_dac_clk - const: aud_dac_predis_clk + - const: aud_adc_clk + - const: aud_adda6_adc_clk + - const: aud_apll22m_clk + - const: aud_apll24m_clk + - const: aud_apll1_tuner_clk + - const: aud_apll2_tuner_clk + - const: aud_tdm_clk + - const: aud_tml_clk + - const: aud_nle + - const: aud_dac_hires_clk + - const: aud_adc_hires_clk + - const: aud_adc_hires_tml + - const: aud_adda6_adc_hires_clk + - const: aud_3rd_dac_clk + - const: aud_3rd_dac_predis_clk + - const: aud_3rd_dac_tml + - const: aud_3rd_dac_hires_clk - const: aud_infra_clk - const: aud_infra_26m_clk + - const: top_mux_audio + - const: top_mux_audio_int + - const: top_mainpll_d4_d4 + - const: top_mux_aud_1 + - const: top_apll1_ck + - const: top_mux_aud_2 + - const: top_apll2_ck + - const: top_mux_aud_eng1 + - const: top_apll1_d4 + - const: top_mux_aud_eng2 + - const: top_apll2_d4 + - const: top_i2s0_m_sel + - const: top_i2s1_m_sel + - const: top_i2s2_m_sel + - const: top_i2s3_m_sel + - const: top_i2s4_m_sel + - const: top_i2s5_m_sel + - const: top_i2s6_m_sel + - const: top_i2s7_m_sel + - const: top_i2s8_m_sel + - const: top_i2s9_m_sel + - const: top_apll12_div0 + - const: top_apll12_div1 + - const: top_apll12_div2 + - const: top_apll12_div3 + - const: top_apll12_div4 + - const: top_apll12_divb + - const: top_apll12_div5 + - const: top_apll12_div6 + - const: top_apll12_div7 + - const: top_apll12_div8 + - const: top_apll12_div9 + - const: top_mux_audio_h + - const: top_clk26m_clk required: - compatible @@ -83,23 +185,69 @@ examples: afe: mt8192-afe-pcm { compatible = "mediatek,mt8192-audio"; interrupts = ; + clocks = <&audsys CLK_AUD_AFE>, <&audsys CLK_AUD_DAC>, + <&audsys CLK_AUD_DAC_PREDIS>, <&audsys CLK_AUD_ADC>, + <&audsys CLK_AUD_ADDA6_ADC>, <&audsys CLK_AUD_22M>, + <&audsys CLK_AUD_24M>, <&audsys CLK_AUD_APLL_TUNER>, + <&audsys CLK_AUD_APLL2_TUNER>, <&audsys CLK_AUD_TDM>, + <&audsys CLK_AUD_TML>, <&audsys CLK_AUD_NLE>, + <&audsys CLK_AUD_DAC_HIRES>, <&audsys CLK_AUD_ADC_HIRES>, + <&audsys CLK_AUD_ADC_HIRES_TML>, <&audsys CLK_AUD_ADDA6_ADC_HIRES>, + <&audsys CLK_AUD_3RD_DAC>, <&audsys CLK_AUD_3RD_DAC_PREDIS>, + <&audsys CLK_AUD_3RD_DAC_TML>, <&audsys CLK_AUD_3RD_DAC_HIRES>, + <&infracfg CLK_INFRA_AUDIO>, <&infracfg CLK_INFRA_AUDIO_26M_B>, + <&topckgen CLK_TOP_AUDIO_SEL>, <&topckgen CLK_TOP_AUD_INTBUS_SEL>, + <&topckgen CLK_TOP_MAINPLL_D4_D4>, <&topckgen CLK_TOP_AUD_1_SEL>, + <&topckgen CLK_TOP_APLL1>, <&topckgen CLK_TOP_AUD_2_SEL>, + <&topckgen CLK_TOP_APLL2>, <&topckgen CLK_TOP_AUD_ENGEN1_SEL>, + <&topckgen CLK_TOP_APLL1_D4>, <&topckgen CLK_TOP_AUD_ENGEN2_SEL>, + <&topckgen CLK_TOP_APLL2_D4>, <&topckgen CLK_TOP_APLL_I2S0_M_SEL>, + <&topckgen CLK_TOP_APLL_I2S1_M_SEL>, <&topckgen CLK_TOP_APLL_I2S2_M_SEL>, + <&topckgen CLK_TOP_APLL_I2S3_M_SEL>, <&topckgen CLK_TOP_APLL_I2S4_M_SEL>, + <&topckgen CLK_TOP_APLL_I2S5_M_SEL>, <&topckgen CLK_TOP_APLL_I2S6_M_SEL>, + <&topckgen CLK_TOP_APLL_I2S7_M_SEL>, <&topckgen CLK_TOP_APLL_I2S8_M_SEL>, + <&topckgen CLK_TOP_APLL_I2S9_M_SEL>, <&topckgen CLK_TOP_APLL12_DIV0>, + <&topckgen CLK_TOP_APLL12_DIV1>, <&topckgen CLK_TOP_APLL12_DIV2>, + <&topckgen CLK_TOP_APLL12_DIV3>, <&topckgen CLK_TOP_APLL12_DIV4>, + <&topckgen CLK_TOP_APLL12_DIVB>, <&topckgen CLK_TOP_APLL12_DIV5>, + <&topckgen CLK_TOP_APLL12_DIV6>, <&topckgen CLK_TOP_APLL12_DIV7>, + <&topckgen CLK_TOP_APLL12_DIV8>, <&topckgen CLK_TOP_APLL12_DIV9>, + <&topckgen CLK_TOP_AUDIO_H_SEL>, <&clk26m>; + clock-names = "aud_afe_clk", "aud_dac_clk", + "aud_dac_predis_clk", "aud_adc_clk", + "aud_adda6_adc_clk", "aud_apll22m_clk", + "aud_apll24m_clk", "aud_apll1_tuner_clk", + "aud_apll2_tuner_clk", "aud_tdm_clk", + "aud_tml_clk", "aud_nle", + "aud_dac_hires_clk", "aud_adc_hires_clk", + "aud_adc_hires_tml", "aud_adda6_adc_hires_clk", + "aud_3rd_dac_clk", "aud_3rd_dac_predis_clk", + "aud_3rd_dac_tml", "aud_3rd_dac_hires_clk", + "aud_infra_clk", "aud_infra_26m_clk", + "top_mux_audio", "top_mux_audio_int", + "top_mainpll_d4_d4", "top_mux_aud_1", + "top_apll1_ck", "top_mux_aud_2", + "top_apll2_ck", "top_mux_aud_eng1", + "top_apll1_d4", "top_mux_aud_eng2", + "top_apll2_d4", "top_i2s0_m_sel", + "top_i2s1_m_sel", "top_i2s2_m_sel", + "top_i2s3_m_sel", "top_i2s4_m_sel", + "top_i2s5_m_sel", "top_i2s6_m_sel", + "top_i2s7_m_sel", "top_i2s8_m_sel", + "top_i2s9_m_sel", "top_apll12_div0", + "top_apll12_div1", "top_apll12_div2", + "top_apll12_div3", "top_apll12_div4", + "top_apll12_divb", "top_apll12_div5", + "top_apll12_div6", "top_apll12_div7", + "top_apll12_div8", "top_apll12_div9", + "top_mux_audio_h", "top_clk26m_clk"; + memory-region = <&afe_dma_mem>; + power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>; resets = <&watchdog MT8192_TOPRGU_AUDIO_SW_RST>; reset-names = "audiosys"; mediatek,apmixedsys = <&apmixedsys>; mediatek,infracfg = <&infracfg>; mediatek,topckgen = <&topckgen>; - power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>; - clocks = <&audsys CLK_AUD_AFE>, - <&audsys CLK_AUD_DAC>, - <&audsys CLK_AUD_DAC_PREDIS>, - <&infracfg CLK_INFRA_AUDIO>, - <&infracfg CLK_INFRA_AUDIO_26M_B>; - clock-names = "aud_afe_clk", - "aud_dac_clk", - "aud_dac_predis_clk", - "aud_infra_clk", - "aud_infra_26m_clk"; - memory-region = <&afe_dma_mem>; }; ... From 7a3d1b04d938f31e112fe09c0ffc1af830ba1f6d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Jan 2026 14:11:06 +0000 Subject: [PATCH 195/341] ASoC: SDCA: Handle CONFIG_PM_SLEEP not being set If CONFIG_PM_SLEEP is not set the completion used will not exist. Update the code to avoid the build error this introduces, without PM_SLEEP it should be safe to always run the conditional code. Fixes: ffd7e8a10111 ("ASoC: SDCA: Device boot into the system suspend process") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601151803.XY7KryHC-lkp@intel.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260115141107.564929-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index cc40732c30cc..d9e22cf40f77 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -198,6 +198,18 @@ error: return irqret; } +#ifdef CONFIG_PM_SLEEP +static bool no_pm_in_progress(struct device *dev) +{ + return completion_done(&dev->power.completion); +} +#else +static bool no_pm_in_progress(struct device *dev) +{ + return true; +} +#endif + static irqreturn_t fdl_owner_handler(int irq, void *data) { struct sdca_interrupt *interrupt = data; @@ -209,7 +221,7 @@ static irqreturn_t fdl_owner_handler(int irq, void *data) * FDL has to run from the system resume handler, at which point * we can't wait for the pm runtime. */ - if (completion_done(&dev->power.completion)) { + if (no_pm_in_progress(dev)) { ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "failed to resume for fdl: %d\n", ret); @@ -223,7 +235,7 @@ static irqreturn_t fdl_owner_handler(int irq, void *data) irqret = IRQ_HANDLED; error: - if (completion_done(&dev->power.completion)) + if (no_pm_in_progress(dev)) pm_runtime_put(dev); return irqret; } From b799d165bc572b86000a2862713e275d69e6d78c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 15 Jan 2026 14:39:20 +0000 Subject: [PATCH 196/341] ALSA: hda/cirrus_scodec_test: Use faux_device instead of platform_device The dummy GPIO driver doesn't need to be a platform device. So make it a faux_device driver. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260115143920.1553783-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai --- .../codecs/side-codecs/cirrus_scodec_test.c | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c index dc35932b6b22..50527c5fa374 100644 --- a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c +++ b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c @@ -5,14 +5,13 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. -#include #include #include #include +#include #include #include #include -#include #include "cirrus_scodec.h" @@ -29,7 +28,7 @@ struct cirrus_scodec_test_gpio { struct cirrus_scodec_test_priv { struct faux_device *amp_dev; - struct platform_device *gpio_pdev; + struct faux_device *gpio_dev; struct cirrus_scodec_test_gpio *gpio_priv; }; @@ -92,59 +91,57 @@ static const struct gpio_chip cirrus_scodec_test_gpio_chip = { .ngpio = 32, }; -static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev) +/* software_node referencing the gpio driver */ +static const struct software_node cirrus_scodec_test_gpio_swnode = { + .name = "cirrus_scodec_test_gpio", +}; + +static int cirrus_scodec_test_gpio_probe(struct faux_device *fdev) { struct cirrus_scodec_test_gpio *gpio_priv; int ret; - gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL); + gpio_priv = devm_kzalloc(&fdev->dev, sizeof(*gpio_priv), GFP_KERNEL); if (!gpio_priv) return -ENOMEM; + ret = device_add_software_node(&fdev->dev, &cirrus_scodec_test_gpio_swnode); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&fdev->dev, device_remove_software_node_wrapper, + &fdev->dev); + if (ret) + return ret; + /* GPIO core modifies our struct gpio_chip so use a copy */ gpio_priv->chip = cirrus_scodec_test_gpio_chip; - gpio_priv->chip.parent = &pdev->dev; - ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv); + gpio_priv->chip.parent = &fdev->dev; + ret = devm_gpiochip_add_data(&fdev->dev, &gpio_priv->chip, gpio_priv); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n"); + return dev_err_probe(&fdev->dev, ret, "Failed to add gpiochip\n"); - dev_set_drvdata(&pdev->dev, gpio_priv); + dev_set_drvdata(&fdev->dev, gpio_priv); return 0; } -static struct platform_driver cirrus_scodec_test_gpio_driver = { - .driver.name = "cirrus_scodec_test_gpio_drv", - .driver.owner = THIS_MODULE, +static const struct faux_device_ops cirrus_scodec_test_gpio_driver_ops = { .probe = cirrus_scodec_test_gpio_probe, }; -/* software_node referencing the gpio driver */ -static const struct software_node cirrus_scodec_test_gpio_swnode = { - .name = "cirrus_scodec_test_gpio", -}; - static void cirrus_scodec_test_create_gpio(struct kunit *test) { struct cirrus_scodec_test_priv *priv = test->priv; - KUNIT_ASSERT_EQ(test, 0, - kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver)); - - priv->gpio_pdev = kunit_platform_device_alloc(test, - cirrus_scodec_test_gpio_driver.driver.name, - PLATFORM_DEVID_NONE); - KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev); - - KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev, - &cirrus_scodec_test_gpio_swnode)); + priv->gpio_dev = faux_device_create("cirrus_scodec_test_gpio_drv", NULL, + &cirrus_scodec_test_gpio_driver_ops); + KUNIT_ASSERT_NOT_NULL(test, priv->gpio_dev); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, - device_remove_software_node_wrapper, - &priv->gpio_pdev->dev)); + faux_device_destroy_wrapper, + priv->gpio_dev)); - KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev)); - - priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev); + priv->gpio_priv = dev_get_drvdata(&priv->gpio_dev->dev); KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv); } From 08c09899960118ffb01417242e659eb6cc067d6a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Jan 2026 17:17:48 +0800 Subject: [PATCH 197/341] ASoC: soc-acpi-intel-arl-match: change rt722 amp endpoint to aggregated rt722 is aggregated with rt1320 amp in arl_rt722_l0_rt1320_l2 and it is the only audio configuration in the ARL platform. Set .aggregated = 1 to represent the fact and avoid unexpected issue. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260119091749.1752088-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-arl-match.c | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) 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 6bf7a6250ddc..c952f7d2b2c0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -45,23 +45,22 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = { .group_id = 1, }; -/* - * RT722 is a multi-function codec, three endpoints are created for - * its headset, amp and dmic functions. - */ -static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { +static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = { + /* Jack Endpoint */ { .num = 0, .aggregated = 0, .group_position = 0, .group_id = 0, }, + /* Amp Endpoint, work as spk_l_endpoint */ { .num = 1, - .aggregated = 0, + .aggregated = 1, .group_position = 0, - .group_id = 0, + .group_id = 1, }, + /* DMIC Endpoint */ { .num = 2, .aggregated = 0, @@ -229,11 +228,11 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; -static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { +static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = { { .adr = 0x000030025D072201ull, - .num_endpoints = ARRAY_SIZE(rt722_endpoints), - .endpoints = rt722_endpoints, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints, .name_prefix = "rt722" } }; @@ -394,8 +393,8 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = { static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = { { .mask = BIT(0), - .num_adr = ARRAY_SIZE(rt722_0_single_adr), - .adr_d = rt722_0_single_adr, + .num_adr = ARRAY_SIZE(rt722_0_agg_adr), + .adr_d = rt722_0_agg_adr, }, { .mask = BIT(2), From 4fbd3b2ec04dc6ef93090ec24733a5c5671fb71f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Jan 2026 17:17:49 +0800 Subject: [PATCH 198/341] ASoC: soc-acpi-intel-ptl-match: use aggregated endpoint in ptl_rt722_l0_rt1320_l23 The rt722 amp and rt1320 amps are aggregated in this case. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260119091749.1752088-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 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 060955825fe0..9e3c459d22e6 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -383,6 +383,15 @@ static const struct snd_soc_acpi_link_adr ptl_rt721_l3[] = { {}, }; +static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = { + { + .adr = 0x000030025d072201ull, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints, + .name_prefix = "rt722" + } +}; + static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { { .adr = 0x000030025d072201ull, @@ -536,8 +545,8 @@ static const struct snd_soc_acpi_link_adr ptl_rt722_l3[] = { static const struct snd_soc_acpi_link_adr ptl_rt722_l0_rt1320_l23[] = { { .mask = BIT(0), - .num_adr = ARRAY_SIZE(rt722_0_single_adr), - .adr_d = rt722_0_single_adr, + .num_adr = ARRAY_SIZE(rt722_0_agg_adr), + .adr_d = rt722_0_agg_adr, }, { .mask = BIT(2), From 0cccfe65895d42fdc1f04aae7f85d8189ad80fdf Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Sat, 17 Jan 2026 17:18:34 +0800 Subject: [PATCH 199/341] ASoC: codecs: es8323: Replace magic numbers with defined macros According to the manual, add more detailed register bitfield definitions to replace the magic numbers in the register settings in es8323 codec. No functional change intended. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/7e8953f2d36a86e9d507d6bb01c72b64846e0829.1768641428.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8323.c | 203 ++++++++++++++++++++------------------ sound/soc/codecs/es8323.h | 82 +++++++++++++-- 2 files changed, 182 insertions(+), 103 deletions(-) diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index eb85b71e87f3..8647ddc1c285 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -12,6 +12,7 @@ // Further cleanup and restructuring by: // Binbin Zhou +#include #include #include #include @@ -32,7 +33,6 @@ struct es8323_priv { struct clk *mclk; struct regmap *regmap; struct snd_pcm_hw_constraint_list *sysclk_constraints; - struct snd_soc_component *component; }; /* es8323 register cache */ @@ -52,7 +52,7 @@ static const struct reg_default es8323_reg_defaults[] = { { ES8323_ADCCONTROL4, 0x00 }, { ES8323_ADCCONTROL5, 0x06 }, { ES8323_ADCCONTROL6, 0x30 }, - { ES8323_ADC_MUTE, 0x30 }, + { ES8323_ADCCONTROL7, 0x30 }, { ES8323_LADC_VOL, 0xc0 }, { ES8323_RADC_VOL, 0xc0 }, { ES8323_ADCCONTROL10, 0x38 }, @@ -62,7 +62,7 @@ static const struct reg_default es8323_reg_defaults[] = { { ES8323_ADCCONTROL14, 0x00 }, { ES8323_DACCONTROL1, 0x00 }, { ES8323_DACCONTROL2, 0x06 }, - { ES8323_DAC_MUTE, 0x30 }, + { ES8323_DACCONTROL3, 0x30 }, { ES8323_LDAC_VOL, 0xc0 }, { ES8323_RDAC_VOL, 0xc0 }, { ES8323_DACCONTROL6, 0x08 }, @@ -119,29 +119,43 @@ static const struct snd_kcontrol_new es8323_snd_controls[] = { SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum), SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum), SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum), - SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0), - SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0), - SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0), - SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0), - SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0), - SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0), - SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0), - SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8, - 0, es8323_bypass_tlv), - SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0, - 8, 0, es8323_bypass_tlv), - SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3, - 7, 1, es8323_bypass_tlv2), + + SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, + ES8323_ADCCONTROL13_ALCZC_OFF, 1, 0), + SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, + ES8323_ADCCONTROL12_ALCDCY_OFF, 15, 0), + SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, + ES8323_ADCCONTROL12_ALCATK_OFF, 15, 0), + SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, + ES8323_ADCCONTROL14_NGTH_OFF, 31, 0), + SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, + ES8323_ADCCONTROL14_NGAT_OFF, 1, 0), + SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, + ES8323_ADCCONTROL13_TIMEOUT_OFF, 1, 0), + SOC_SINGLE("Capture Mute Switch", ES8323_ADCCONTROL7, + ES8323_ADCCONTROL7_ADCMUTE_OFF, 1, 0), + + SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, + ES8323_ADCCONTROL1_MICAMPL_OFF, 8, 0, es8323_bypass_tlv), + SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, + ES8323_ADCCONTROL1_MICAMPR_OFF, 8, 0, es8323_bypass_tlv), + SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, + ES8323_DACCONTROL17_LI2LOVOL_OFF, 7, 1, es8323_bypass_tlv2), SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20, - 3, 7, 1, es8323_bypass_tlv2), - SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL, + ES8323_DACCONTROL20_RI2ROVOL_OFF, 7, 1, es8323_bypass_tlv2), + + SOC_DOUBLE_R_TLV("PCM Volume", + ES8323_LDAC_VOL, ES8323_RDAC_VOL, 0, 192, 1, es8323_dac_tlv), - SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL, - ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv), - SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL, - ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv), - SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL, - ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv), + SOC_DOUBLE_R_TLV("Capture Digital Volume", + ES8323_LADC_VOL, ES8323_RADC_VOL, + 0, 192, 1, es8323_adc_tlv), + SOC_DOUBLE_R_TLV("Output 1 Playback Volume", + ES8323_LOUT1_VOL, ES8323_ROUT1_VOL, + 0, 33, 0, es8323_out_tlv), + SOC_DOUBLE_R_TLV("Output 2 Playback Volume", + ES8323_LOUT2_VOL, ES8323_ROUT2_VOL, + 0, 33, 0, es8323_out_tlv), }; /* Left DAC Route */ @@ -211,8 +225,10 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1), SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1), - SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1), - SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", + ES8323_DACPOWER, ES8323_DACPOWER_PDNDACR_OFF, 1), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", + ES8323_DACPOWER, ES8323_DACPOWER_PDNDACL_OFF, 1), SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, &es8323_left_mixer_controls[0], @@ -223,12 +239,12 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0), SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0), - SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0), - SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, ES8323_DACPOWER_ROUT2_OFF, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, ES8323_DACPOWER_LOUT2_OFF, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, ES8323_DACPOWER_ROUT1_OFF, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, ES8323_DACPOWER_LOUT1_OFF, 0, NULL, 0), + SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPL_OFF, 0, NULL, 0), + SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPR_OFF, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LOUT1"), SND_SOC_DAPM_OUTPUT("ROUT1"), @@ -423,16 +439,18 @@ static int es8323_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; - u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE); - u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE); - u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE); + u8 format_mode, inv_mode; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_BC_FP: - iface |= 0x80; + /* Master serial port mode */ + snd_soc_component_update_bits(component, ES8323_MASTERMODE, + ES8323_MASTERMODE_MSC, ES8323_MASTERMODE_MSC); break; case SND_SOC_DAIFMT_BC_FC: - iface &= 0x7f; + /* Slave serial port mode */ + snd_soc_component_update_bits(component, ES8323_MASTERMODE, + ES8323_MASTERMODE_MSC, 0); break; default: return -EINVAL; @@ -441,55 +459,47 @@ static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - adciface &= 0xfc; - daciface &= 0xf8; + format_mode = ES8323_FMT_I2S; break; case SND_SOC_DAIFMT_LEFT_J: - adciface &= 0xfd; - daciface &= 0xf9; + format_mode = ES8323_FMT_LEFT_J; break; case SND_SOC_DAIFMT_RIGHT_J: - adciface &= 0xfe; - daciface &= 0xfa; + format_mode = ES8323_FMT_RIGHT_J; break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: - adciface &= 0xff; - daciface &= 0xfb; + format_mode = ES8323_FMT_DSP; break; default: return -EINVAL; } + snd_soc_component_write_field(component, ES8323_ADCCONTROL4, + ES8323_ADCCONTROL4_ADCFORMAT, format_mode); + snd_soc_component_write_field(component, ES8323_DACCONTROL1, + ES8323_DACCONTROL1_DACFORMAT, format_mode); + /* clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: - iface &= 0xdf; - adciface &= 0xdf; - daciface &= 0xbf; + case SND_SOC_DAIFMT_IB_NF: + inv_mode = 0; break; case SND_SOC_DAIFMT_IB_IF: - iface |= 0x20; - adciface |= 0x20; - daciface |= 0x40; - break; - case SND_SOC_DAIFMT_IB_NF: - iface |= 0x20; - adciface &= 0xdf; - daciface &= 0xbf; - break; case SND_SOC_DAIFMT_NB_IF: - iface &= 0xdf; - adciface |= 0x20; - daciface |= 0x40; + inv_mode = 1; break; default: return -EINVAL; } - snd_soc_component_write(component, ES8323_MASTERMODE, iface); - snd_soc_component_write(component, ES8323_ADC_IFACE, adciface); - snd_soc_component_write(component, ES8323_DAC_IFACE, daciface); + snd_soc_component_update_bits(component, ES8323_MASTERMODE, + ES8323_MASTERMODE_BCLKINV, inv_mode); + snd_soc_component_update_bits(component, ES8323_ADCCONTROL4, + ES8323_ADCCONTROL4_ADCLRP, inv_mode); + snd_soc_component_update_bits(component, ES8323_DACCONTROL1, + ES8323_DACCONTROL1_DACLRP, inv_mode); return 0; } @@ -515,52 +525,56 @@ static int es8323_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component); - u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80; - u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3; - u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7; + u8 wl_mode, fs; int coeff; coeff = get_coeff(es8323->sysclk, params_rate(params)); if (coeff < 0) { coeff = get_coeff(es8323->sysclk / 2, params_rate(params)); - srate |= 0x40; + if (coeff < 0) { + dev_err(component->dev, + "Unable to configure sample rate %dHz with %dHz MCLK\n", + params_rate(params), es8323->sysclk); + return coeff; + } + + snd_soc_component_update_bits(component, ES8323_MASTERMODE, + ES8323_MASTERMODE_MCLKDIV2, + ES8323_MASTERMODE_MCLKDIV2); } - if (coeff < 0) { - dev_err(component->dev, - "Unable to configure sample rate %dHz with %dHz MCLK\n", - params_rate(params), es8323->sysclk); - return coeff; - } + fs = FIELD_PREP(ES8323_DACCONTROL2_DACFSMODE, es8323_coeff_div[coeff].usb) + | FIELD_PREP(ES8323_DACCONTROL2_DACFSRATIO, es8323_coeff_div[coeff].sr); - /* bit size */ + snd_soc_component_write_field(component, ES8323_ADCCONTROL5, + ES8323_ADCCONTROL5_ADCFS_MASK, fs); + + snd_soc_component_write_field(component, ES8323_DACCONTROL2, + ES8323_DACCONTROL2_DACFS_MASK, fs); + + /* serial audio data word length */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - adciface |= 0xc; - daciface |= 0x18; + wl_mode = ES8323_S16_LE; break; case SNDRV_PCM_FORMAT_S20_3LE: - adciface |= 0x4; - daciface |= 0x8; + wl_mode = ES8323_S20_LE; break; case SNDRV_PCM_FORMAT_S24_LE: + wl_mode = ES8323_S24_LE; break; case SNDRV_PCM_FORMAT_S32_LE: - adciface |= 0x10; - daciface |= 0x20; + wl_mode = ES8323_S32_LE; break; + default: + return -EINVAL; } - snd_soc_component_write(component, ES8323_DAC_IFACE, daciface); - snd_soc_component_write(component, ES8323_ADC_IFACE, adciface); + snd_soc_component_write_field(component, ES8323_ADCCONTROL4, + ES8323_ADCCONTROL4_ADCWL, wl_mode); - snd_soc_component_write(component, ES8323_MASTERMODE, srate); - snd_soc_component_write(component, ES8323_ADCCONTROL5, - es8323_coeff_div[coeff].sr | - (es8323_coeff_div[coeff].usb) << 4); - snd_soc_component_write(component, ES8323_DACCONTROL2, - es8323_coeff_div[coeff].sr | - (es8323_coeff_div[coeff].usb) << 4); + snd_soc_component_write_field(component, ES8323_DACCONTROL1, + ES8323_DACCONTROL1_DACWL, wl_mode); snd_soc_component_write(component, ES8323_DACPOWER, 0x3c); @@ -569,12 +583,9 @@ static int es8323_pcm_hw_params(struct snd_pcm_substream *substream, static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { - struct snd_soc_component *component = dai->component; - u32 val = mute ? 0x6 : 0x2; - - snd_soc_component_write(component, ES8323_DAC_MUTE, val); - - return 0; + return snd_soc_component_update_bits(dai->component, ES8323_DACCONTROL3, + ES8323_DACCONTROL3_DACMUTE, + mute ? ES8323_DACCONTROL3_DACMUTE : 0); } static const struct snd_soc_dai_ops es8323_ops = { @@ -613,8 +624,6 @@ static int es8323_probe(struct snd_soc_component *component) struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component); int ret; - es8323->component = component; - es8323->mclk = devm_clk_get_optional(component->dev, "mclk"); if (IS_ERR(es8323->mclk)) { dev_err(component->dev, "unable to get mclk\n"); diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h index f986c9301dc6..d79955f871c9 100644 --- a/sound/soc/codecs/es8323.h +++ b/sound/soc/codecs/es8323.h @@ -18,31 +18,99 @@ #define ES8323_CHIPPOWER 0x02 #define ES8323_ADCPOWER 0x03 #define ES8323_DACPOWER 0x04 + +#define ES8323_DACPOWER_ROUT2_OFF 2 +#define ES8323_DACPOWER_LOUT2_OFF 3 +#define ES8323_DACPOWER_ROUT1_OFF 4 +#define ES8323_DACPOWER_LOUT1_OFF 5 +#define ES8323_DACPOWER_PDNDACR_OFF 6 +#define ES8323_DACPOWER_PDNDACL_OFF 7 + #define ES8323_CHIPLOPOW1 0x05 #define ES8323_CHIPLOPOW2 0x06 #define ES8323_ANAVOLMANAG 0x07 #define ES8323_MASTERMODE 0x08 +#define ES8323_MASTERMODE_BCLKDIV GENMASK(4, 0) +#define ES8323_MASTERMODE_BCLKINV BIT(5) +#define ES8323_MASTERMODE_MCLKDIV2 BIT(6) +#define ES8323_MASTERMODE_MSC BIT(7) + /* ADC Control */ #define ES8323_ADCCONTROL1 0x09 + +#define ES8323_ADCCONTROL1_MICAMPR_OFF 0 +#define ES8323_ADCCONTROL1_MICAMPL_OFF 4 + #define ES8323_ADCCONTROL2 0x0a #define ES8323_ADCCONTROL3 0x0b #define ES8323_ADCCONTROL4 0x0c + +#define ES8323_ADCCONTROL4_ADCFORMAT GENMASK(1, 0) +#define ES8323_FMT_I2S 0x0 +#define ES8323_FMT_LEFT_J 0x1 +#define ES8323_FMT_RIGHT_J 0x2 +#define ES8323_FMT_DSP 0x3 +#define ES8323_ADCCONTROL4_ADCWL GENMASK(4, 2) +#define ES8323_S24_LE 0x0 +#define ES8323_S20_LE 0x1 +#define ES8323_S18_LE 0x2 +#define ES8323_S16_LE 0x3 +#define ES8323_S32_LE 0x4 +#define ES8323_ADCCONTROL4_ADCLRP BIT(5) +#define ES8323_ADCCONTROL4_DATSEL GENMASK(7, 6) + #define ES8323_ADCCONTROL5 0x0d + +#define ES8323_ADCCONTROL5_ADCFSRATIO GENMASK(4, 0) +#define ES8323_ADCCONTROL5_ADCFSMODE BIT(5) +#define ES8323_ADCCONTROL5_ADCFS_MASK (ES8323_ADCCONTROL5_ADCFSRATIO |\ + ES8323_ADCCONTROL5_ADCFSMODE) + #define ES8323_ADCCONTROL6 0x0e -#define ES8323_ADC_MUTE 0x0f +#define ES8323_ADCCONTROL7 0x0f + +#define ES8323_ADCCONTROL7_ADCMUTE_OFF 2 + #define ES8323_LADC_VOL 0x10 #define ES8323_RADC_VOL 0x11 #define ES8323_ADCCONTROL10 0x12 #define ES8323_ADCCONTROL11 0x13 #define ES8323_ADCCONTROL12 0x14 + +#define ES8323_ADCCONTROL12_ALCATK_OFF 0 +#define ES8323_ADCCONTROL12_ALCDCY_OFF 4 + #define ES8323_ADCCONTROL13 0x15 + +#define ES8323_ADCCONTROL13_TIMEOUT_OFF 5 +#define ES8323_ADCCONTROL13_ALCZC_OFF 6 + #define ES8323_ADCCONTROL14 0x16 +#define ES8323_ADCCONTROL14_NGAT_OFF 0 +#define ES8323_ADCCONTROL14_NGG_OFF 1 +#define ES8323_ADCCONTROL14_NGTH_OFF 3 + /* DAC Control */ #define ES8323_DACCONTROL1 0x17 + +#define ES8323_DACCONTROL1_DACFORMAT GENMASK(1, 0) +#define ES8323_DACCONTROL1_DACWL GENMASK(5, 3) +#define ES8323_DACCONTROL1_DACLRP BIT(6) +#define ES8323_DACCONTROL1_DACLRSWAP BIT(7) + #define ES8323_DACCONTROL2 0x18 -#define ES8323_DAC_MUTE 0x19 + +#define ES8323_DACCONTROL2_DACFSRATIO GENMASK(4, 0) +#define ES8323_DACCONTROL2_DACFSMODE BIT(5) +#define ES8323_DACCONTROL2_DACFS_MASK (ES8323_DACCONTROL2_DACFSRATIO |\ + ES8323_DACCONTROL2_DACFSMODE) + +#define ES8323_DACCONTROL3 0x19 + +#define ES8323_DACCONTROL3_DACMUTE BIT(2) + #define ES8323_LDAC_VOL 0x1a #define ES8323_RDAC_VOL 0x1b #define ES8323_DACCONTROL6 0x1c @@ -57,9 +125,15 @@ #define ES8323_DACCONTROL15 0x25 #define ES8323_DACCONTROL16 0x26 #define ES8323_DACCONTROL17 0x27 + +#define ES8323_DACCONTROL17_LI2LOVOL_OFF 3 + #define ES8323_DACCONTROL18 0x28 #define ES8323_DACCONTROL19 0x29 #define ES8323_DACCONTROL20 0x2a + +#define ES8323_DACCONTROL20_RI2ROVOL_OFF 3 + #define ES8323_DACCONTROL21 0x2b #define ES8323_DACCONTROL22 0x2c #define ES8323_DACCONTROL23 0x2d @@ -71,8 +145,4 @@ #define ES8323_DACCONTROL29 0x33 #define ES8323_DACCONTROL30 0x34 -#define ES8323_ADC_IFACE ES8323_ADCCONTROL4 -#define ES8323_ADC_SRATE ES8323_ADCCONTROL5 -#define ES8323_DAC_IFACE ES8323_DACCONTROL1 -#define ES8323_DAC_SRATE ES8323_DACCONTROL2 #endif From 9dd2719a9ad3e18c3a5007466ca5922937bd4c5e Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Sat, 17 Jan 2026 17:18:35 +0800 Subject: [PATCH 200/341] ASoC: codecs: es8323: remove DAC enablement write from es8323_set_bias_level Since commit daf855f76a12 ("ASoC: es8323: enable DAPM power widgets for playback DAC"), the DAC is handled by the DAPM subsystem. Remove initialization of the DAC enablement bits from the es8323_set_bias_level routine Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/d2823ad7ae226a83d49bcb860240c0ddc4080f14.1768641428.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8323.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index 8647ddc1c285..4a589949cc7e 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -576,8 +576,6 @@ static int es8323_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_component_write_field(component, ES8323_DACCONTROL1, ES8323_DACCONTROL1_DACWL, wl_mode); - snd_soc_component_write(component, ES8323_DACPOWER, 0x3c); - return 0; } @@ -659,7 +657,6 @@ static int es8323_set_bias_level(struct snd_soc_component *component, snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0); usleep_range(18000, 20000); - snd_soc_component_write(component, ES8323_DACPOWER, 0x3c); snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c); snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00); @@ -679,7 +676,6 @@ static int es8323_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_OFF: clk_disable_unprepare(es8323->mclk); snd_soc_component_write(component, ES8323_ADCPOWER, 0xff); - snd_soc_component_write(component, ES8323_DACPOWER, 0xC0); snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff); snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff); From 95814d4b8d478c76505188e45e81682206e41fe6 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Sat, 17 Jan 2026 17:18:36 +0800 Subject: [PATCH 201/341] ASoC: codecs: es8323: Enable proper DAPM widgets for capture ADC Add proper DAPM capture ADC widgets to DAPM, which really should be handled by the DAPM subsystem. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/9d93040f4cea77054e669c50325e080dffa7a7f1.1768641428.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8323.c | 37 +++++++++++++++++++++---------------- sound/soc/codecs/es8323.h | 13 +++++++++++++ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index 4a589949cc7e..001e63b4e345 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -212,19 +212,29 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_INPUT("RINPUT1"), SND_SOC_DAPM_INPUT("RINPUT2"), - SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1), + SND_SOC_DAPM_SUPPLY("Mic Bias", ES8323_ADCPOWER, + ES8323_ADCPOWER_PDNMICB_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8323_ADCPOWER, + ES8323_ADCPOWER_PDNADCBIS_OFF, 1, NULL, 0), /* Muxes */ - SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls), - SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls), + SND_SOC_DAPM_MUX("Left PGA Mux", ES8323_ADCPOWER, + ES8323_ADCPOWER_PDNAINL_OFF, 1, &es8323_left_dac_mux_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", ES8323_ADCPOWER, + ES8323_ADCPOWER_PDNAINR_OFF, 1, &es8323_right_dac_mux_controls), + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls), SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls), + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls), SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls), - SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1), - SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", + ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCR_OFF, 1), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", + ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCL_OFF, 1), SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, ES8323_DACPOWER_PDNDACR_OFF, 1), SND_SOC_DAPM_DAC("Left DAC", "Left Playback", @@ -237,8 +247,6 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { &es8323_right_mixer_controls[0], ARRAY_SIZE(es8323_right_mixer_controls)), - SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0), - SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0), SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, ES8323_DACPOWER_ROUT2_OFF, 0, NULL, 0), SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, ES8323_DACPOWER_LOUT2_OFF, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, ES8323_DACPOWER_ROUT1_OFF, 0, NULL, 0), @@ -268,18 +276,16 @@ static const struct snd_soc_dapm_route es8323_dapm_routes[] = { {"Differential Mux", "Line 2", "LINPUT2"}, {"Differential Mux", "Line 2", "RINPUT2"}, - {"Left ADC Mux", "Stereo", "Right PGA Mux"}, {"Left ADC Mux", "Stereo", "Left PGA Mux"}, {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, - {"Right ADC Mux", "Stereo", "Left PGA Mux"}, {"Right ADC Mux", "Stereo", "Right PGA Mux"}, {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, - {"Left ADC Power", NULL, "Left ADC Mux"}, - {"Right ADC Power", NULL, "Right ADC Mux"}, - {"Left ADC", NULL, "Left ADC Power"}, - {"Right ADC", NULL, "Right ADC Power"}, + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, + + { "Mic Bias", NULL, "Mic Bias Gen" }, {"Left Line Mux", "Line 1L", "LINPUT1"}, {"Left Line Mux", "Line 2L", "LINPUT2"}, @@ -661,8 +667,9 @@ static int es8323_set_bias_level(struct snd_soc_component *component, snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00); snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); - snd_soc_component_write(component, ES8323_ADCPOWER, 0x09); snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00); + snd_soc_component_update_bits(component, ES8323_ADCPOWER, + ES8323_ADCPOWER_PDNADCBIS, 0); break; case SND_SOC_BIAS_PREPARE: break; @@ -671,11 +678,9 @@ static int es8323_set_bias_level(struct snd_soc_component *component, snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00); snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); - snd_soc_component_write(component, ES8323_ADCPOWER, 0x59); break; case SND_SOC_BIAS_OFF: clk_disable_unprepare(es8323->mclk); - snd_soc_component_write(component, ES8323_ADCPOWER, 0xff); snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff); snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff); diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h index d79955f871c9..4d4ac4f2e4ae 100644 --- a/sound/soc/codecs/es8323.h +++ b/sound/soc/codecs/es8323.h @@ -17,6 +17,19 @@ #define ES8323_CONTROL2 0x01 #define ES8323_CHIPPOWER 0x02 #define ES8323_ADCPOWER 0x03 + +#define ES8323_ADCPOWER_INT1LP BIT(0) +#define ES8323_ADCPOWER_FLASHLP BIT(1) +#define ES8323_ADCPOWER_PDNADCBIS BIT(2) +#define ES8323_ADCPOWER_PDNMICB BIT(3) + +#define ES8323_ADCPOWER_PDNADCBIS_OFF 2 +#define ES8323_ADCPOWER_PDNMICB_OFF 3 +#define ES8323_ADCPOWER_PDNADCR_OFF 4 +#define ES8323_ADCPOWER_PDNADCL_OFF 5 +#define ES8323_ADCPOWER_PDNAINR_OFF 6 +#define ES8323_ADCPOWER_PDNAINL_OFF 7 + #define ES8323_DACPOWER 0x04 #define ES8323_DACPOWER_ROUT2_OFF 2 From 3c5ddd56aa93048314c64533c21e731a44b0f067 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Sat, 17 Jan 2026 17:20:15 +0800 Subject: [PATCH 202/341] ASoC: codecs: es8323: Enable proper DAPM widgets for chip power Remove initialization of the chip power register from the es8323_set_bias_level routine, and add proper DAPM power supply widgets to DAPM, which really should be handled by the DAPM subsystem. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/db47e56e7c72bb900a84a33ee07d4ffcf0a908a3.1768641428.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8323.c | 50 +++++++++++++++++++++++++++------------ sound/soc/codecs/es8323.h | 10 ++++++++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index 001e63b4e345..605375b154c8 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -217,6 +217,23 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCBIS_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC STM", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_DACSTM_RESET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC STM", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC DIG", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_DACDIG_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DIG", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC DLL", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_DACDLL_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DLL", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8323_CHIPPOWER, + ES8323_CHIPPOWER_DACVREF_OFF, 1, NULL, 0), + /* Muxes */ SND_SOC_DAPM_MUX("Left PGA Mux", ES8323_ADCPOWER, ES8323_ADCPOWER_PDNAINL_OFF, 1, &es8323_left_dac_mux_controls), @@ -287,6 +304,20 @@ static const struct snd_soc_dapm_route es8323_dapm_routes[] = { { "Mic Bias", NULL, "Mic Bias Gen" }, + { "ADC DIG", NULL, "ADC STM" }, + { "ADC DIG", NULL, "ADC Vref" }, + { "ADC DIG", NULL, "ADC DLL" }, + + { "Left ADC", NULL, "ADC DIG" }, + { "Right ADC", NULL, "ADC DIG" }, + + { "DAC DIG", NULL, "DAC STM" }, + { "DAC DIG", NULL, "DAC Vref" }, + { "DAC DIG", NULL, "DAC DLL" }, + + { "Left DAC", NULL, "DAC DIG" }, + { "Right DAC", NULL, "DAC DIG" }, + {"Left Line Mux", "Line 1L", "LINPUT1"}, {"Left Line Mux", "Line 2L", "LINPUT2"}, {"Left Line Mux", "MicL", "Left PGA Mux"}, @@ -644,7 +675,7 @@ static int es8323_probe(struct snd_soc_component *component) } snd_soc_component_write(component, ES8323_CONTROL2, 0x60); - snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); + snd_soc_component_write(component, ES8323_DACCONTROL21, 0x80); return 0; } @@ -657,34 +688,23 @@ static int es8323_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: ret = clk_prepare_enable(es8323->mclk); if (ret) return ret; - snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0); - usleep_range(18000, 20000); - snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c); snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00); - snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); - snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00); snd_soc_component_update_bits(component, ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCBIS, 0); break; - case SND_SOC_BIAS_PREPARE: - break; case SND_SOC_BIAS_STANDBY: - snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c); - snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00); - snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00); - snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); break; case SND_SOC_BIAS_OFF: - clk_disable_unprepare(es8323->mclk); snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff); snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff); - snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff); - snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b); + clk_disable_unprepare(es8323->mclk); break; } diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h index 4d4ac4f2e4ae..6e37e0480f04 100644 --- a/sound/soc/codecs/es8323.h +++ b/sound/soc/codecs/es8323.h @@ -16,6 +16,16 @@ #define ES8323_CONTROL1 0x00 #define ES8323_CONTROL2 0x01 #define ES8323_CHIPPOWER 0x02 + +#define ES8323_CHIPPOWER_DACVREF_OFF 0 +#define ES8323_CHIPPOWER_ADCVREF_OFF 1 +#define ES8323_CHIPPOWER_DACDLL_OFF 2 +#define ES8323_CHIPPOWER_ADCDLL_OFF 3 +#define ES8323_CHIPPOWER_DACSTM_RESET 4 +#define ES8323_CHIPPOWER_ADCSTM_RESET 5 +#define ES8323_CHIPPOWER_DACDIG_OFF 6 +#define ES8323_CHIPPOWER_ADCDIG_OFF 7 + #define ES8323_ADCPOWER 0x03 #define ES8323_ADCPOWER_INT1LP BIT(0) From a18467a50eddbd7c6548b53b25b68e5454ceb587 Mon Sep 17 00:00:00 2001 From: Runrun Liu Date: Mon, 19 Jan 2026 10:53:02 +0800 Subject: [PATCH 203/341] ASoC: realtek: fix misspelling of "minimum" in comments Fix the typo "miniumum" -> "minimum" in comments in rt5659, rt5665, rt5668, and rt5682-i2c codec drivers. This typo is already listed in scripts/spelling.txt by commit 8c3200265787 ("scripts/spelling.txt: add several more common spelling mistakes"). Suggested-by: Cryolitia PukNgae Signed-off-by: Runrun Liu Link: https://patch.msgid.link/3D20FA99934F2891+20260119025302.1288888-1-liurunrun@uniontech.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5659.c | 2 +- sound/soc/codecs/rt5665.c | 2 +- sound/soc/codecs/rt5668.c | 2 +- sound/soc/codecs/rt5682-i2c.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index f5957470652c..3590ebd41c27 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4118,7 +4118,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) rt5659->gpiod_reset = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); - /* Sleep for 300 ms miniumum */ + /* Sleep for 300 ms minimum */ msleep(300); rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap); diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index c7beccd54b16..38fb3a277e26 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4688,7 +4688,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) return PTR_ERR(rt5665->gpiod_ldo1_en); } - /* Sleep for 300 ms miniumum */ + /* Sleep for 300 ms minimum */ usleep_range(300000, 350000); rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap); diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 5fcdb50d5184..c551696ae11a 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2458,7 +2458,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) return PTR_ERR(rt5668->ldo1_en); } - /* Sleep for 300 ms miniumum */ + /* Sleep for 300 ms minimum */ usleep_range(300000, 350000); regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1); diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index bba987308e15..e556a365adc8 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -173,7 +173,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) if (ret) return ret; - /* Sleep for 300 ms miniumum */ + /* Sleep for 300 ms minimum */ usleep_range(300000, 350000); regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1); From 53dfb2ad6fcff111f58aa506afbbf6fd82f32cb1 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 19 Jan 2026 21:52:49 +0200 Subject: [PATCH 204/341] ASoC: renesas: rz-ssi: Simplify the logic in rz_ssi_stream_is_play() The code in rz_ssi_stream_is_play() checks whether substream->stream is different from SNDRV_PCM_STREAM_PLAYBACK and returns the capture struct rz_ssi_stream in that case. The logic is easier to follow if substream->stream is compared directly against SNDRV_PCM_STREAM_CAPTURE and return the capture struct rz_ssi_stream. Use the conditional operator to simplify the code. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260119195252.3362486-2-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index f4dc2f68dead..f144cbc89fad 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -178,12 +178,7 @@ static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream) static inline struct rz_ssi_stream * rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream) { - struct rz_ssi_stream *stream = &ssi->playback; - - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - stream = &ssi->capture; - - return stream; + return (ssi->playback.substream == substream) ? &ssi->playback : &ssi->capture; } static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) From 9e0e337cc6c7f30a5986dd44dac5c0a0e30d971f Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 19 Jan 2026 21:52:50 +0200 Subject: [PATCH 205/341] ASoC: renesas: rz-ssi: Drop unnecessary if condition The is_stopped variable can be initialized directly at declaration, removing the need for an extra if condition. Drop the if condition and initialize is_stopped at declaration. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260119195252.3362486-3-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index f144cbc89fad..46a22debebeb 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -566,7 +566,8 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data) return IRQ_HANDLED; /* Left over TX/RX interrupt */ if (irq == ssi->irq_int) { /* error or idle */ - bool is_stopped = false; + bool is_stopped = !!(ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | + SSISR_TUIRQ | SSISR_TOIRQ)); int i, count; if (rz_ssi_is_dma_enabled(ssi)) @@ -574,9 +575,6 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data) else count = 1; - if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ)) - is_stopped = true; - if (ssi->capture.substream && is_stopped) { if (ssisr & SSISR_RUIRQ) strm_capture->uerr_num++; From c7a4c368e7135046c358ff15c4f7897e2522dedc Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 19 Jan 2026 21:52:51 +0200 Subject: [PATCH 206/341] ASoC: renesas: rz-ssi: Drop the & operator in front of function name There is no need for & operator in front of the function name. Drop it. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260119195252.3362486-4-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 46a22debebeb..74e87102d902 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -1153,7 +1153,7 @@ static int rz_ssi_probe(struct platform_device *pdev) goto err_release_dma_chs; } - ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt, + ret = devm_request_irq(dev, ssi->irq_int, rz_ssi_interrupt, 0, dev_name(dev), ssi); if (ret < 0) { dev_err_probe(dev, ret, "irq request error (int_req)\n"); @@ -1170,7 +1170,7 @@ static int rz_ssi_probe(struct platform_device *pdev) return ssi->irq_rt; ret = devm_request_irq(dev, ssi->irq_rt, - &rz_ssi_interrupt, 0, + rz_ssi_interrupt, 0, dev_name(dev), ssi); if (ret < 0) return dev_err_probe(dev, ret, @@ -1183,14 +1183,14 @@ static int rz_ssi_probe(struct platform_device *pdev) return ssi->irq_rx; ret = devm_request_irq(dev, ssi->irq_tx, - &rz_ssi_interrupt, 0, + rz_ssi_interrupt, 0, dev_name(dev), ssi); if (ret < 0) return dev_err_probe(dev, ret, "irq request error (dma_tx)\n"); ret = devm_request_irq(dev, ssi->irq_rx, - &rz_ssi_interrupt, 0, + rz_ssi_interrupt, 0, dev_name(dev), ssi); if (ret < 0) return dev_err_probe(dev, ret, From cafadbf430f4b2f3ca4158de48ef6ba4d97fbf17 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 19 Jan 2026 21:52:52 +0200 Subject: [PATCH 207/341] ASoC: renesas: rz-ssi: Drop goto label There is no need to jump to a label just to return. Return directly instead. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260119195252.3362486-5-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rz-ssi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 74e87102d902..da45e9ad1a2f 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -843,7 +843,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, for (i = 0; i < num_transfer; i++) { ret = strm->transfer(ssi, strm); if (ret) - goto done; + return ret; } ret = rz_ssi_start(ssi, strm); @@ -859,7 +859,6 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; } -done: return ret; } From 8d38c275f7ffe257d21bea224d4288eef183817d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 20 Jan 2026 14:56:58 +0800 Subject: [PATCH 208/341] ASoC: sdw_utils: remove dai registered check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking for a registered DAI for non-existing endpoints causes the following error. The driver will always return -EPROBE_DEFER if the codec driver doesn't register the DAI of the unexist endpoint. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260120065658.1806027-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index e4ce952e56aa..477eba80e90a 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1394,29 +1394,14 @@ static int is_sdca_endpoint_present(struct device *dev, const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; - struct snd_soc_dai_link_component *dlc; - struct snd_soc_dai *codec_dai; struct sdw_slave *slave; struct device *sdw_dev; const char *sdw_codec_name; int ret, i; - dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); - if (!dlc) - return -ENOMEM; - adr_end = &adr_dev->endpoints[end_index]; dai_info = &codec_info->dais[adr_end->num]; - dlc->dai_name = dai_info->dai_name; - codec_dai = snd_soc_find_dai_with_mutex(dlc); - if (!codec_dai) { - dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); - kfree(dlc); - return -EPROBE_DEFER; - } - kfree(dlc); - sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index); if (!sdw_codec_name) return -ENOMEM; From 1616c41e469cf5263e756161ed2e35664ff7d978 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Tue, 20 Jan 2026 09:38:25 +0530 Subject: [PATCH 209/341] ASoC: tas2783A: fw loading for devices without pci bus Currently, there is compilation error when the CONFIG_PCI is not enabled which is used for creating firmware name. This commit address this issue by adding fallback mechanism to construct unqiue name by using SounWire slave's link and unique ids alone when the CONFIG_PCI is not available. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601190756.IpoMY5AJ-lkp@intel.com/ Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20260120040825.1460-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 43 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index af812f95a4ba..3c1fbf523529 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -27,7 +27,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_PCI) #include +#endif #include #include #include @@ -1106,21 +1108,33 @@ static const struct dev_pm_ops tas2783_sdca_pm = { RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL) }; -static struct pci_dev *tas_get_pci_dev(struct sdw_slave *peripheral) +static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t size) { - struct device *dev = &peripheral->dev; + struct sdw_bus *bus = slave->bus; + u8 unique_id = slave->id.unique_id; + bool pci_found = false; +#if IS_ENABLED(CONFIG_PCI) + struct device *dev = &slave->dev; + struct pci_dev *pci = NULL; - for (; dev; dev = dev->parent) - if (dev->bus == &pci_bus_type) - return to_pci_dev(dev); + for (; dev; dev = dev->parent) { + if (dev->bus == &pci_bus_type) { + pci = to_pci_dev(dev); + scnprintf(name, size, "%04X-%1X-%1X.bin", + pci->subsystem_device, bus->link_id, unique_id); + pci_found = true; + break; + } + } +#endif - return NULL; + if (!pci_found) + scnprintf(name, size, "tas2783-%1X-%1X.bin", + bus->link_id, unique_id); } static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) { - struct pci_dev *pci; - struct sdw_bus *bus; struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; @@ -1128,13 +1142,6 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) if (tas_dev->hw_init) return 0; - pci = tas_get_pci_dev(slave); - if (!pci) { - dev_err(dev, "pci device id can't be read"); - return -EINVAL; - } - - bus = slave->bus; tas_dev->fw_dl_task_done = false; tas_dev->fw_dl_success = false; @@ -1145,10 +1152,8 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) } usleep_range(2000, 2200); - /* subsystem_id-link_id-unique_id */ - scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname), - "%04X-%1X-%1X.bin", pci->subsystem_device, bus->link_id, - unique_id); + tas_generate_fw_name(slave, tas_dev->rca_binaryname, + sizeof(tas_dev->rca_binaryname)); ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, From 00ca2dd431fa2acb07b3ecc6ce49fb9a0a4d72cb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Jan 2026 10:19:35 +0200 Subject: [PATCH 210/341] ASoC: SOF: Intel: hda: Remove MODULE_SOFTDEP for snd-hda-codec-hdmi The sofdep no longer works due to the reworked HDA audio stack and it was an incorrect way to try to work around system security policy blocking request_module use, even if they are legitimate. Drop the softdep to stop the whack-a-mole hacking around system configuration issues. Revert "ASoC: SOF: Intel: hda: add softdep pre to snd-hda-codec-hdmi module" This reverts commit 33b7dc7843dbdc9b90c91d11ba30b107f9138ffd. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260120081935.11005-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-codec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 37674ea452d6..fd371850b0d6 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -451,7 +451,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915"); -MODULE_SOFTDEP("pre: snd-hda-codec-hdmi"); #endif MODULE_LICENSE("Dual BSD/GPL"); From b190870e0e0cfb375c0d4da02761c32083f3644d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Jan 2026 21:35:04 +0200 Subject: [PATCH 211/341] PCI: Add Intel Nova Lake audio Device ID Add Nova Lake (NVL) audio Device ID The ID will be used by HDA legacy, SOF audio stack and the driver to determine which audio stack should be used (intel-dsp-config). Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Acked-by: Bjorn Helgaas Acked-by: Takashi Iwai Link: https://patch.msgid.link/20260120193507.14019-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/linux/pci_ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 84b830036fb4..5ed7846639bf 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3144,6 +3144,7 @@ #define PCI_DEVICE_ID_INTEL_HDA_CML_S 0xa3f0 #define PCI_DEVICE_ID_INTEL_HDA_LNL_P 0xa828 #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 +#define PCI_DEVICE_ID_INTEL_HDA_NVL 0xd328 #define PCI_DEVICE_ID_INTEL_HDA_BMG 0xe2f7 #define PCI_DEVICE_ID_INTEL_HDA_PTL_H 0xe328 #define PCI_DEVICE_ID_INTEL_HDA_PTL 0xe428 From 1800bcdc68ead7209451085431d80aa95ea7cd03 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Jan 2026 21:35:05 +0200 Subject: [PATCH 212/341] ASoC: SOF: Intel: add support for Nova Lake NVL Add support for Nova Lake (NVL). The core count for NVL is different compared to NVL-S (4 vs 2) Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Acked-by: Mark Brown Acked-by: Takashi Iwai Link: https://patch.msgid.link/20260120193507.14019-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/nvl.c | 24 ++++++++++++++++++++++++ sound/soc/sof/intel/pci-nvl.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 562fe8be79c1..3be39c229c9f 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -936,6 +936,7 @@ extern const struct sof_intel_dsp_desc arl_s_chip_info; extern const struct sof_intel_dsp_desc lnl_chip_info; extern const struct sof_intel_dsp_desc ptl_chip_info; extern const struct sof_intel_dsp_desc wcl_chip_info; +extern const struct sof_intel_dsp_desc nvl_chip_info; extern const struct sof_intel_dsp_desc nvl_s_chip_info; /* Probes support */ diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c index ff215151af2a..0d763998558f 100644 --- a/sound/soc/sof/intel/nvl.c +++ b/sound/soc/sof/intel/nvl.c @@ -26,6 +26,30 @@ int sof_nvl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) }; EXPORT_SYMBOL_NS(sof_nvl_set_ops, "SND_SOC_SOF_INTEL_NVL"); +const struct sof_intel_dsp_desc nvl_chip_info = { + .cores_num = 4, + .init_core_mask = BIT(0), + .host_managed_cores_mask = BIT(0), + .ipc_req = MTL_DSP_REG_HFIPCXIDR, + .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, + .ipc_ack = MTL_DSP_REG_HFIPCXIDA, + .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, + .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, + .rom_status_reg = LNL_DSP_REG_HFDSC, + .rom_init_timeout = 300, + .ssp_count = MTL_SSP_COUNT, + .d0i3_offset = MTL_HDA_VS_D0I3C, + .read_sdw_lcount = hda_sdw_check_lcount_ext, + .check_sdw_irq = lnl_dsp_check_sdw_irq, + .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, + .sdw_process_wakeen = hda_sdw_process_wakeen_common, + .check_ipc_irq = mtl_dsp_check_ipc_irq, + .cl_init = mtl_dsp_cl_init, + .power_down_dsp = mtl_power_down_dsp, + .disable_interrupts = lnl_dsp_disable_interrupts, + .hw_ip_version = SOF_INTEL_ACE_4_0, +}; + const struct sof_intel_dsp_desc nvl_s_chip_info = { .cores_num = 2, .init_core_mask = BIT(0), diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c index f75aa996a5bd..bb3c29ef5477 100644 --- a/sound/soc/sof/intel/pci-nvl.c +++ b/sound/soc/sof/intel/pci-nvl.c @@ -26,6 +26,36 @@ static int sof_nvl_ops_init(struct snd_sof_dev *sdev) return sof_nvl_set_ops(sdev, &sof_nvl_ops); } +static const struct sof_dev_desc nvl_desc = { + .use_acpi_target_states = true, + .machines = snd_soc_acpi_intel_nvl_machines, + .alt_machines = snd_soc_acpi_intel_nvl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &nvl_chip_info, + .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), + .ipc_default = SOF_IPC_TYPE_4, + .dspless_mode_supported = true, + .on_demand_dsp_boot = true, + .default_fw_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl", + }, + .default_lib_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/nvl", + }, + .default_tplg_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", + }, + .default_fw_filename = { + [SOF_IPC_TYPE_4] = "sof-nvl.ri", + }, + .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", + .ops = &sof_nvl_ops, + .ops_init = sof_nvl_ops_init, +}; + static const struct sof_dev_desc nvl_s_desc = { .use_acpi_target_states = true, .machines = snd_soc_acpi_intel_nvl_machines, @@ -58,6 +88,7 @@ static const struct sof_dev_desc nvl_s_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, HDA_NVL, &nvl_desc) }, /* NVL */ { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, &nvl_s_desc) }, /* NVL-S */ { 0, } }; From a094d8a50679356fbf045af274ccab85c9a644fb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Jan 2026 21:35:06 +0200 Subject: [PATCH 213/341] ALSA: hda: core: intel-dsp-config: Add support for NVL Add entry for NVL variant of Nova Lake family. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Acked-by: Takashi Iwai Link: https://patch.msgid.link/20260120193507.14019-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/hda/core/intel-dsp-config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c index ddb8db3e8e39..f0a44fd111f3 100644 --- a/sound/hda/core/intel-dsp-config.c +++ b/sound/hda/core/intel-dsp-config.c @@ -580,6 +580,10 @@ static const struct config_entry config_table[] = { /* Nova Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_NOVALAKE) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = PCI_DEVICE_ID_INTEL_HDA_NVL, + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = PCI_DEVICE_ID_INTEL_HDA_NVL_S, From 7f428282fde34f06f3ab898b8a9081bf93a41f22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Jan 2026 21:35:07 +0200 Subject: [PATCH 214/341] ALSA: hda: controllers: intel: add support for Nova Lake Add NVL to the PCI-ID list. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Acked-by: Takashi Iwai Link: https://patch.msgid.link/20260120193507.14019-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/hda/controllers/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index bb9a64d41580..85324c7c796d 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2551,6 +2551,7 @@ static const struct pci_device_id azx_ids[] = { /* Wildcat Lake */ { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Nova Lake */ + { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, From 00fd40bc7acecf9f41d645aa0b35ddc7fd7679b6 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 21 Jan 2026 13:22:39 +0000 Subject: [PATCH 215/341] ASoC: cs-amp-lib: Support Dell SSIDExV2 UEFI variable Add a function cs_amp_devm_get_vendor_specific_variant_id() to return a vendor-specific hardware identifier string (if there is one) and use it to fetch an identifier from Dell SSIDExV2 UEFI variable content. Dell use the same PCI SSID on multiple products that might have different audio hardware and thus need different firmware for the amplifier DSP. The SSIDExV2 string contains additional system identifiers, and the second field is a 2-character audio hardware identifier. There are older Dell models with Cirrus Logic amplifiers that have the SSIDExV2 UEFI variable but do not have the 2-character audio ID in the second field. The SSIDExV2 is ignored if the second field is not 2 characters. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260121132243.1256019-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs-amp-lib.h | 3 + sound/soc/codecs/cs-amp-lib.c | 111 ++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h index 61e00017c9aa..e9aa86d76049 100644 --- a/include/sound/cs-amp-lib.h +++ b/include/sound/cs-amp-lib.h @@ -58,6 +58,9 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_ int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, const struct cirrus_amp_cal_data *in_data); int cs_amp_get_vendor_spkid(struct device *dev); +const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, + int ssid_vendor, + int ssid_device); struct dentry *cs_amp_create_debugfs(struct device *dev); static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data) diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index b4d183e7501d..9f8c99dfb798 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,10 @@ #define HP_CALIBRATION_EFI_GUID \ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93) +#define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data" +#define DELL_SSIDEXV2_EFI_GUID \ + EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0) + static const struct cs_amp_lib_cal_efivar { efi_char16_t *name; efi_guid_t *guid; @@ -304,6 +309,29 @@ static int cs_amp_convert_efi_status(efi_status_t status) } } +static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + u32 *returned_attr) +{ + efi_status_t status; + unsigned long size = 0; + + status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return ERR_PTR(cs_amp_convert_efi_status(status)); + + /* Over-alloc to ensure strings are always NUL-terminated */ + void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf); + if (status != EFI_SUCCESS) + return ERR_PTR(cs_amp_convert_efi_status(status)); + + return_ptr(buf); +} + static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, efi_char16_t **name, efi_guid_t **guid, @@ -705,6 +733,89 @@ int cs_amp_get_vendor_spkid(struct device *dev) } EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB"); +static const char *cs_amp_devm_get_dell_ssidex(struct device *dev, + int ssid_vendor, int ssid_device) +{ + unsigned int hex_prefix; + char audio_id[4]; + char delim; + char *p; + int ret; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return ERR_PTR(-ENOENT); + + char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME, + &DELL_SSIDEXV2_EFI_GUID, + NULL); + ret = PTR_ERR_OR_ZERO(ssidex_buf); + if (ret == -ENOENT) + return ERR_PTR(-ENOENT); + else if (ret < 0) + return ssidex_buf; + + /* + * SSIDExV2 string is a series of underscore delimited fields. + * First field is all or part of the SSID. Second field should be + * a 2-character audio hardware id, followed by other identifiers. + * Older models did not have the 2-character audio id, so reject + * the string if the second field is not 2 characters. + */ + ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim); + if (ret < 2) + return ERR_PTR(-ENOENT); + + if ((ret == 3) && (delim != '_')) + return ERR_PTR(-ENOENT); + + if (strlen(audio_id) != 2) + return ERR_PTR(-ENOENT); + + p = devm_kstrdup(dev, audio_id, GFP_KERNEL); + if (!p) + return ERR_PTR(-ENOMEM); + + return p; +} + +/** + * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string + * @dev: pointer to struct device + * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown) + * @ssid_device: PCI Subsystem Device (-1 if unknown) + * + * Known vendor-specific hardware identifiers are checked and if one is + * found its content is returned as a NUL-terminated string. The returned + * string is devm-managed. + * + * The returned string is not guaranteed to be globally unique. + * Generally it should be combined with some other qualifier, such as + * PCI SSID, to create a globally unique ID. + * + * If the caller has a PCI SSID it should pass it in @ssid_vendor and + * @ssid_device. If the vendor-spefic ID contains this SSID it will be + * stripped from the returned string to prevent duplication. + * + * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and + * @ssid_device. + * + * Return: + * * a pointer to a devm-managed string + * * ERR_PTR(-ENOENT) if no vendor-specific qualifier + * * ERR_PTR error value + */ +const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, + int ssid_vendor, + int ssid_device) +{ + if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0)) + return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device); + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB"); + /** * cs_amp_create_debugfs - create a debugfs directory for a device * From 51b069172a4ef2c6278c64c603a41a7f20329921 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 21 Jan 2026 13:22:40 +0000 Subject: [PATCH 216/341] ASoC: cs35l56: Use vendor-specific qualifier in firmware file search If cs_amp_devm_get_vendor_specific_variant_id() returns a string, use it as part of the firmware filename. If this firmware isn't found, fall back to the standard firmware name. This re-uses the fwf_suffix fallback mechanism that was introduced in commit e5d5b3aebdc8 ("ASoC: cs35l56: Use SoundWire address as alternate firmware suffix on L56 B0"). This is for handling vendors that use the same PCI SSID on systems with various audio hardware and have a custom vendor-specific way to identify the hardware variant. This is currently used on Dell laptops. Dell create a UEFI variable that indicates varations to the base hardware. This variance can be any part of the hardware (not necessarily affecting the audio). It would be impractical to publish many aliases for the same firmware files to match every possible variance to that base hardware. Hence the fallback to the standard firmware name. This allows alternate firmware files to be published only for variants that need it. For all other variants the fallback will load the firmware for the base SSID. This is not done for CS35L56 B0 because the fallback mechanism is already used for a different purpose for these parts. None of the products with the older L56 B0 silicon revision need the additional vendor-specific descriptor. For SoundWire the resulting firmware searches with a variant descriptor will be: 1. cs35l??-dsp1-misc-SSID-VARIANT-l?u?.wmfw 2. cs35l??-dsp1-misc-SSID-VARIANT.wmfw 3. cs35l??-dsp1-misc-SSID-VARIANT-l?u?.bin 4. cs35l??-dsp1-misc-SSID-VARIANT.bin If this doesn't find a wmfw and bin file it will then fallback to: 5. cs35l??-dsp1-misc-SSID-l?u?.wmfw 6. cs35l??-dsp1-misc-SSID.wmfw 7. cs35l??-dsp1-misc-SSID-l?u?.bin 8. cs35l??-dsp1-misc-SSID.bin With the typical published firmware names and qualifiers (a single wmfw but amp-specific bin file) this will load either: cs35l??-dsp1-misc-SSID-VARIANT.wmfw and cs35l??-dsp1-misc-SSID-VARIANT-l?u?.bin or cs35l??-dsp1-misc-SSID.wmfw and cs35l??-dsp1-misc-SSID-l?u?.bin For non-Soundwire (I2S/TDM) systems the searches and fallbacks are as above except that the "l?u?" component of the name is instead the ALSA name prefix, usually of the form "AMPn". Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260121132243.1256019-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56.c | 69 ++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 55b4d0d55712..abea782bcd3f 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1109,27 +1109,68 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) { + unsigned short vendor, device; + const char *vendor_id; + int ret; + if (cs35l56->dsp.fwf_suffix) return 0; - if (!cs35l56->sdw_peripheral) - return 0; + if (cs35l56->sdw_peripheral) { + cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, + "l%uu%u", + cs35l56->sdw_link_num, + cs35l56->sdw_unique_id); + if (!cs35l56->dsp.fwf_suffix) + return -ENOMEM; - cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, - "l%uu%u", - cs35l56->sdw_link_num, - cs35l56->sdw_unique_id); - if (!cs35l56->dsp.fwf_suffix) - return -ENOMEM; + /* + * There are published firmware files for L56 B0 silicon using + * the ALSA prefix as the filename suffix. Default to trying these + * first, with the new SoundWire suffix as a fallback. + * None of these older systems use a vendor-specific ID. + */ + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) { + cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; + cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix; + + return 0; + } + } /* - * There are published firmware files for L56 B0 silicon using - * the ALSA prefix as the filename suffix. Default to trying these - * first, with the new name as an alternate. + * Some manufacturers use the same SSID on multiple products and have + * a vendor-specific qualifier to distinguish different models. + * Models with the same SSID but different qualifier might require + * different audio firmware, or they might all have the same audio + * firmware. + * Try searching for a firmware with this qualifier first, else + * fallback to standard naming. */ - if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) { - cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; - cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix; + if (snd_soc_card_get_pci_ssid(cs35l56->component->card, &vendor, &device) < 0) { + vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, -1, -1); + } else { + vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, + vendor, device); + } + ret = PTR_ERR_OR_ZERO(vendor_id); + if (ret == -ENOENT) + return 0; + else if (ret) + return ret; + + if (vendor_id) { + if (cs35l56->dsp.fwf_suffix) + cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; + else + cs35l56->fallback_fw_suffix = cs35l56->component->name_prefix; + + cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, + "%s-%s", + vendor_id, + cs35l56->fallback_fw_suffix); + if (!cs35l56->dsp.fwf_suffix) + return -ENOMEM; } return 0; From d70fa6b569a95e71cc25b86ac3cc221bea541f53 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 21 Jan 2026 13:22:41 +0000 Subject: [PATCH 217/341] ASoC: cs-amp-lib-test: Tests for reading SSIDExV2 Adds test cases for getting a Dell SSIDExV2 string by calling cs_amp_devm_get_vendor_specific_variant_id(). This is a fairly simple test harness, the redirected call to cs_amp_get_efi_variable() returns a test string to simulate reading a string from UEFI. The returned string should be the second field in the SSIDExV2 string (if valid). Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260121132243.1256019-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib-test.c | 169 +++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index 923f1857e45b..2b1529343f11 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,8 @@ struct cs_amp_lib_test_ctl_write_entry { struct cs_amp_lib_test_param { int num_amps; int amp_index; + char *vendor_sysid; + char *expected_sysid; }; static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test) @@ -2305,6 +2308,98 @@ static void cs_amp_lib_test_spkid_hp_31(struct kunit *test) KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev)); } +static efi_status_t cs_amp_lib_test_get_efi_vendor_sysid(efi_char16_t *name, + efi_guid_t *guid, + u32 *returned_attr, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct cs_amp_lib_test_param *param = test->param_value; + unsigned int len; + + KUNIT_ASSERT_NOT_NULL(test, param->vendor_sysid); + len = strlen(param->vendor_sysid); + + if (*size < len) { + *size = len; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_NOT_NULL(test, buf); + memcpy(buf, param->vendor_sysid, len); + + return EFI_SUCCESS; +} + +/* Fetch SSIDExV2 string from UEFI */ +static void cs_amp_lib_test_ssidexv2_fetch(struct kunit *test) +{ + const struct cs_amp_lib_test_param *param = test->param_value; + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + const char *got; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_vendor_sysid); + + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, got); + KUNIT_EXPECT_STREQ(test, got, param->expected_sysid); +} + +/* Invalid SSIDExV2 string should be ignored */ +static void cs_amp_lib_test_ssidexv2_fetch_invalid(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + const char *got; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_vendor_sysid); + + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); + KUNIT_EXPECT_NOT_NULL(test, got); + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); +} + +static void cs_amp_lib_test_ssidexv2_not_dell(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + const char *got; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_vendor_sysid); + + /* Not returned if SSID vendor is not Dell */ + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_CIRRUS, 0xabcd); + KUNIT_EXPECT_NOT_NULL(test, got); + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); +} + +static void cs_amp_lib_test_vendor_variant_id_not_found(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + const char *got; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_none); + + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); + KUNIT_EXPECT_NOT_NULL(test, got); + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); + + got = cs_amp_devm_get_vendor_specific_variant_id(dev, -1, -1); + KUNIT_EXPECT_NOT_NULL(test, got); + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); +} + static int cs_amp_lib_test_case_init(struct kunit *test) { struct cs_amp_lib_test_priv *priv; @@ -2375,6 +2470,71 @@ static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_para KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases, cs_amp_lib_test_get_cal_param_desc); +static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_param_cases[] = { + { .vendor_sysid = "abcd_00", .expected_sysid = "00" }, + { .vendor_sysid = "abcd_01", .expected_sysid = "01" }, + { .vendor_sysid = "abcd_XY", .expected_sysid = "XY" }, + + { .vendor_sysid = "1028abcd_00", .expected_sysid = "00" }, + { .vendor_sysid = "1028abcd_01", .expected_sysid = "01" }, + { .vendor_sysid = "1028abcd_XY", .expected_sysid = "XY" }, + + { .vendor_sysid = "abcd_00_WF", .expected_sysid = "00" }, + { .vendor_sysid = "abcd_01_WF", .expected_sysid = "01" }, + { .vendor_sysid = "abcd_XY_WF", .expected_sysid = "XY" }, + + { .vendor_sysid = "1028abcd_00_WF", .expected_sysid = "00" }, + { .vendor_sysid = "1028abcd_01_WF", .expected_sysid = "01" }, + { .vendor_sysid = "1028abcd_XY_WF", .expected_sysid = "XY" }, + + { .vendor_sysid = "abcd_00_AA_BB", .expected_sysid = "00" }, + { .vendor_sysid = "abcd_01_AA_BB", .expected_sysid = "01" }, + { .vendor_sysid = "abcd_XY_AA_BB", .expected_sysid = "XY" }, + + { .vendor_sysid = "1028abcd_00_AA_BB", .expected_sysid = "00" }, + { .vendor_sysid = "1028abcd_01_AA_BB", .expected_sysid = "01" }, + { .vendor_sysid = "1028abcd_XY_A_BB", .expected_sysid = "XY" }, +}; + +static void cs_amp_lib_test_ssidexv2_param_desc(const struct cs_amp_lib_test_param *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "vendor_sysid:'%s' expected_sysid:'%s'", + param->vendor_sysid, param->expected_sysid); +} + +KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2, cs_amp_lib_test_ssidexv2_param_cases, + cs_amp_lib_test_ssidexv2_param_desc); + +static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_invalid_param_cases[] = { + { .vendor_sysid = "abcd" }, + { .vendor_sysid = "abcd_0" }, + { .vendor_sysid = "abcd_1" }, + { .vendor_sysid = "abcd_0_1" }, + { .vendor_sysid = "abcd_1_1" }, + { .vendor_sysid = "abcd_1_X" }, + { .vendor_sysid = "abcd_1_X" }, + { .vendor_sysid = "abcd_000" }, + { .vendor_sysid = "abcd_010" }, + { .vendor_sysid = "abcd_000_01" }, + { .vendor_sysid = "abcd_000_01" }, + + { .vendor_sysid = "1234abcd" }, + { .vendor_sysid = "1234abcd_0" }, + { .vendor_sysid = "1234abcd_1" }, + { .vendor_sysid = "1234abcd_0_1" }, + { .vendor_sysid = "1234abcd_1_1" }, + { .vendor_sysid = "1234abcd_1_X" }, + { .vendor_sysid = "1234abcd_1_X" }, + { .vendor_sysid = "1234abcd_000" }, + { .vendor_sysid = "1234abcd_010" }, + { .vendor_sysid = "1234abcd_000_01" }, + { .vendor_sysid = "1234abcd_000_01" }, +}; + +KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2_invalid, cs_amp_lib_test_ssidexv2_invalid_param_cases, + cs_amp_lib_test_ssidexv2_param_desc); + static struct kunit_case cs_amp_lib_test_cases[] = { /* Tests for getting calibration data from EFI */ KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test), @@ -2434,6 +2594,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = { KUNIT_CASE(cs_amp_lib_test_spkid_hp_30), KUNIT_CASE(cs_amp_lib_test_spkid_hp_31), + /* Test cases for SSIDExV2 */ + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch, + cs_amp_lib_test_ssidexv2_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch_invalid, + cs_amp_lib_test_ssidexv2_invalid_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_not_dell, + cs_amp_lib_test_ssidexv2_gen_params), + KUNIT_CASE(cs_amp_lib_test_vendor_variant_id_not_found), + { } /* terminator */ }; From 3f086a4f277af799467af6c74902000461e60a33 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 21 Jan 2026 13:22:42 +0000 Subject: [PATCH 218/341] ASoC: cs-amp-lib: Add a Kconfig symbol for enabling test hooks Add Kconfig symbol CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS to enable calling into functions that would normally abort because of missing EFI functionality. Before this the code paths were only enabled if the KUnit test for cs-amp-lib was enabled. This change allows KUnit tests for clients of cs-amp-lib to install redirection hooks in cs-amp-lib. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260121132243.1256019-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 +++++ sound/soc/codecs/cs-amp-lib.c | 19 ++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 90f09d4bdf8e..21d5b79f079d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -786,9 +786,14 @@ config SND_SOC_CROS_EC_CODEC config SND_SOC_CS_AMP_LIB tristate +config SND_SOC_CS_AMP_LIB_TEST_HOOKS + bool + depends on SND_SOC_CS_AMP_LIB + config SND_SOC_CS_AMP_LIB_TEST tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS depends on SND_SOC_CS_AMP_LIB && KUNIT + select SND_SOC_CS_AMP_LIB_TEST_HOOKS default KUNIT_ALL_TESTS help This builds KUnit tests for the Cirrus Logic common diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 9f8c99dfb798..f8c7f594d54a 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -211,7 +211,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, const struct cirrus_amp_cal_data *data) { - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return _cs_amp_write_cal_coeffs(dsp, controls, data); else return -ENODEV; @@ -230,7 +230,7 @@ int cs_amp_read_cal_coeffs(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, struct cirrus_amp_cal_data *data) { - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return _cs_amp_read_cal_coeffs(dsp, controls, data); else return -ENODEV; @@ -249,10 +249,7 @@ int cs_amp_write_ambient_temp(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, u32 temp) { - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) - return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); - else - return -ENODEV; + return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); } EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB"); @@ -611,7 +608,7 @@ err: int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, struct cirrus_amp_cal_data *out_data) { - if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); else return -ENOENT; @@ -647,7 +644,7 @@ EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, const struct cirrus_amp_cal_data *in_data) { - if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) { + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) { scoped_guard(mutex, &cs_amp_efi_cal_write_lock) { return _cs_amp_set_efi_calibration_data(dev, amp_index, num_amps, in_data); @@ -720,7 +717,7 @@ int cs_amp_get_vendor_spkid(struct device *dev) int i, ret; if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && - !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return -ENOENT; for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) { @@ -743,7 +740,7 @@ static const char *cs_amp_devm_get_dell_ssidex(struct device *dev, int ret; if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && - !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) return ERR_PTR(-ENOENT); char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME, @@ -849,7 +846,7 @@ static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { }; const struct cs_amp_test_hooks * const cs_amp_test_hooks = - PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); + PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs); EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB"); MODULE_DESCRIPTION("Cirrus Logic amplifier library"); From d0ab89951197f0fc509a0cd732d830880e79c2d4 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 21 Jan 2026 13:22:43 +0000 Subject: [PATCH 219/341] ASoC: cs35l56: Add KUnit testing of cs35l56_set_fw_suffix() Add a new KUnit test for testing the creation of firmware name qualifiers in the cs35l56 driver. The initial set of test cases are for cs35l56_set_fw_suffix(). Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260121132243.1256019-6-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 13 ++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs-amp-lib.c | 3 + sound/soc/codecs/cs35l56-test.c | 365 ++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.c | 5 +- sound/soc/codecs/cs35l56.h | 4 + 6 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/cs35l56-test.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 21d5b79f079d..d09de0ff5f22 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -933,6 +933,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL On most platforms this is not needed. If unsure select "N". + +config SND_SOC_CS35L56_TEST + tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS + depends on SND_SOC_CS35L56 && KUNIT + default KUNIT_ALL_TESTS + select SND_SOC_CS_AMP_LIB_TEST_HOOKS + help + This builds KUnit tests for the Cirrus Logic cs35l56 + codec driver. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". endmenu config SND_SOC_CS40L50 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a6406bc907a9..c3568de5e0c9 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -81,6 +81,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o snd-soc-cs35l56-i2c-y := cs35l56-i2c.o snd-soc-cs35l56-spi-y := cs35l56-spi.o snd-soc-cs35l56-sdw-y := cs35l56-sdw.o +snd-soc-cs35l56-test-y := cs35l56-test.o snd-soc-cs40l50-y := cs40l50-codec.o snd-soc-cs42l42-y := cs42l42.o snd-soc-cs42l42-i2c-y := cs42l42-i2c.o @@ -516,6 +517,7 @@ obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o +obj-$(CONFIG_SND_SOC_CS35L56_TEST) += snd-soc-cs35l56-test.o obj-$(CONFIG_SND_SOC_CS40L50) += snd-soc-cs40l50.o obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index f8c7f594d54a..8b131975143d 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -806,6 +806,9 @@ const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, int ssid_vendor, int ssid_device) { + KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id, + dev, ssid_vendor, ssid_device); + if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0)) return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device); diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c new file mode 100644 index 000000000000..66d772d7b982 --- /dev/null +++ b/sound/soc/codecs/cs35l56-test.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// KUnit test for the Cirrus Logic cs35l56 driver. +// +// Copyright (C) 2026 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs35l56.h" + +KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, + struct faux_device *) + +struct cs35l56_test_priv { + struct faux_device *amp_dev; + struct cs35l56_private *cs35l56_priv; + + const char *ssidexv2; +}; + +struct cs35l56_test_param { + u8 type; + u8 rev; +}; + +static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev, + int ssid_vendor, + int ssid_device) +{ + return ERR_PTR(-ENOENT); +} + +static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set device type info */ + cs35l56->base.type = 0x56; + cs35l56->base.rev = 0xb0; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + /* Set SoundWire link and UID number */ + cs35l56->sdw_link_num = 1; + cs35l56->sdw_unique_id = 5; + + kunit_activate_static_stub(test, + cs35l56_test_devm_get_vendor_specific_variant_id_none, + cs_amp_devm_get_vendor_specific_variant_id); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Priority suffix should be the legacy ALSA prefix */ + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1"); + + /* Fallback suffix should be the new SoundWire ID */ + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); +} + +static void cs35l56_test_suffix_sdw(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + /* Set SoundWire link and UID number */ + cs35l56->sdw_link_num = 1; + cs35l56->sdw_unique_id = 5; + + kunit_activate_static_stub(test, + cs35l56_test_devm_get_vendor_specific_variant_id_none, + cs_amp_devm_get_vendor_specific_variant_id); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Suffix should be the SoundWire ID without a fallback */ + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "l1u5"); + KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix); +} + +static void cs35l56_test_suffix_i2cspi(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + kunit_activate_static_stub(test, + cs35l56_test_devm_get_vendor_specific_variant_id_none, + cs_amp_devm_get_vendor_specific_variant_id); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Suffix strings should not be set: use default wm_adsp suffixing */ + KUNIT_EXPECT_NULL(test, cs35l56->dsp.fwf_suffix); + KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix); +} + +static efi_status_t cs35l56_test_get_efi_ssidexv2(efi_char16_t *name, + efi_guid_t *guid, + u32 *returned_attr, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + struct cs35l56_test_priv *priv = test->priv; + unsigned int len; + + KUNIT_ASSERT_NOT_NULL(test, priv->ssidexv2); + len = strlen(priv->ssidexv2); + + if (*size < len) { + *size = len; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_NOT_NULL(test, buf); + memcpy(buf, priv->ssidexv2, len); + + return EFI_SUCCESS; +} + +static void cs35l56_test_ssidexv2_suffix_sdw(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + /* Set SoundWire link and UID number */ + cs35l56->sdw_link_num = 1; + cs35l56->sdw_unique_id = 5; + + /* Set a SSID to enable lookup of SSIDExV2 */ + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); + + priv->ssidexv2 = "10281234_01_BB_CC"; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs35l56_test_get_efi_ssidexv2); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Priority suffix should be the SSIDExV2 string with SoundWire ID */ + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-l1u5"); + + /* Fallback suffix should be the SoundWireID */ + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); +} + +static void cs35l56_test_ssidexv2_suffix_i2cspi(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + /* Set a SSID to enable lookup of SSIDExV2 */ + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); + + priv->ssidexv2 = "10281234_01_BB_CC"; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs35l56_test_get_efi_ssidexv2); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Priority suffix should be the SSIDExV2 string with ALSA name prefix */ + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-AMP1"); + + /* Fallback suffix should be the ALSA name prefix */ + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "AMP1"); +} + +/* + * CS35L56 B0 SoundWire should ignore any SSIDExV2 suffix. It isn't needed + * on any products with B0 silicon and would interfere with the fallback + * to legacy naming convention for early B0-based laptops. + */ +static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Set device type info */ + cs35l56->base.type = 0x56; + cs35l56->base.rev = 0xb0; + + /* Set the ALSA name prefix */ + cs35l56->component->name_prefix = "AMP1"; + + /* Set SoundWire link and UID number */ + cs35l56->sdw_link_num = 1; + cs35l56->sdw_unique_id = 5; + + /* Set a SSID to enable lookup of SSIDExV2 */ + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); + + priv->ssidexv2 = "10281234_01_BB_CC"; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs35l56_test_get_efi_ssidexv2); + + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); + + /* Priority suffix should be the legacy ALSA prefix */ + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1"); + + /* Fallback suffix should be the new SoundWire ID */ + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); +} + +static int cs35l56_test_case_init_common(struct kunit *test) +{ + struct cs35l56_test_priv *priv; + const struct cs35l56_test_param *param = test->param_value; + struct cs35l56_private *cs35l56; + + KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + + /* Create dummy amp driver dev */ + priv->amp_dev = faux_device_create("cs35l56_test_drv", NULL, NULL); + KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + faux_device_destroy_wrapper, + priv->amp_dev)); + + /* Construct minimal set of driver structs */ + priv->cs35l56_priv = kunit_kzalloc(test, sizeof(*priv->cs35l56_priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_priv); + cs35l56 = priv->cs35l56_priv; + cs35l56->base.dev = &priv->amp_dev->dev; + + cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cs35l56->component); + cs35l56->component->dev = cs35l56->base.dev; + + cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cs35l56->component->card); + + if (param) { + cs35l56->base.type = param->type; + cs35l56->base.rev = param->rev; + } + + return 0; +} + +static int cs35l56_test_case_init_soundwire(struct kunit *test) +{ + struct cs35l56_test_priv *priv; + struct cs35l56_private *cs35l56; + int ret; + + ret = cs35l56_test_case_init_common(test); + if (ret) + return ret; + + priv = test->priv; + cs35l56 = priv->cs35l56_priv; + + /* Dummy to indicate this is Soundwire */ + cs35l56->sdw_peripheral = kunit_kzalloc(test, sizeof(*cs35l56->sdw_peripheral), + GFP_KERNEL); + if (!cs35l56->sdw_peripheral) + return -ENOMEM; + + + return 0; +} + +static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "type: %02x rev: %02x", + param->type, param->rev); +} + +static const struct cs35l56_test_param cs35l56_test_type_rev_ex_b0_param_cases[] = { + { .type = 0x56, .rev = 0xb2 }, + { .type = 0x57, .rev = 0xb2 }, + { .type = 0x63, .rev = 0xa1 }, +}; +KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_ex_b0, cs35l56_test_type_rev_ex_b0_param_cases, + cs35l56_test_type_rev_param_desc); + + +static const struct cs35l56_test_param cs35l56_test_type_rev_all_param_cases[] = { + { .type = 0x56, .rev = 0xb0 }, + { .type = 0x56, .rev = 0xb2 }, + { .type = 0x57, .rev = 0xb2 }, + { .type = 0x63, .rev = 0xa1 }, +}; +KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cases, + cs35l56_test_type_rev_param_desc); + +static struct kunit_case cs35l56_test_cases_soundwire[] = { + KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw), + KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params), + KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw, + cs35l56_test_type_rev_ex_b0_gen_params), + KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw), + + { } /* terminator */ +}; + +static struct kunit_case cs35l56_test_cases_not_soundwire[] = { + KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params), + KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi, + cs35l56_test_type_rev_all_gen_params), + + { } /* terminator */ +}; + +static struct kunit_suite cs35l56_test_suite_soundwire = { + .name = "snd-soc-cs35l56-test-soundwire", + .init = cs35l56_test_case_init_soundwire, + .test_cases = cs35l56_test_cases_soundwire, +}; + +static struct kunit_suite cs35l56_test_suite_not_soundwire = { + .name = "snd-soc-cs35l56-test-not-soundwire", + .init = cs35l56_test_case_init_common, + .test_cases = cs35l56_test_cases_not_soundwire, +}; + +kunit_test_suites( + &cs35l56_test_suite_soundwire, + &cs35l56_test_suite_not_soundwire, +); + +MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index abea782bcd3f..31dd2f7b2858 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -5,6 +5,8 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include +#include #include #include #include @@ -1107,7 +1109,7 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE), }; -static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) +VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) { unsigned short vendor, device; const char *vendor_id; @@ -1175,6 +1177,7 @@ static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) return 0; } +EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix); static int cs35l56_component_probe(struct snd_soc_component *component) { diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 4c59f92f3206..7187885a13c1 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -74,4 +74,8 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56); int cs35l56_init(struct cs35l56_private *cs35l56); void cs35l56_remove(struct cs35l56_private *cs35l56); +#if IS_ENABLED(CONFIG_KUNIT) +int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56); +#endif + #endif /* ifndef CS35L56_H */ From 702ce71d32f2c30b4f45b7c6b701d87583c58df8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Jan 2026 13:17:26 +0000 Subject: [PATCH 220/341] ASoC: SDCA: Add NO_DIRECT_COMPLETE flag to class driver The SDCA class driver currently expects the device will be fully powered down on system suspend but not on runtime suspend. This is typically required as when audio is not active (ie. runtime suspend) jack detect is expected to still function, but when the whole system is hibernated there is no need to recognise audio jack events. This means the class driver needs to always be informed of a system suspend, so the direct complete optimisation (where PM will skip calling system suspend if the device is runtime suspended) is not appropriate for the SDCA class driver. Add the NO_DIRECT_COMPLETE flag to prevent this optimisation from running against this driver. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart link: https://github.com/thesofproject/linux/blob/ec0e6c69113f4b342ee8eabec286dea33d98a7cc/drivers/soundwire/intel_auxdevice.c#L568 Link: https://patch.msgid.link/20260115131727.373738-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 0afa41c1ee93..98fd3fd1052b 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -377,6 +377,8 @@ static int class_function_probe(struct auxiliary_device *auxdev, if (ret) return ret; + dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); + pm_runtime_set_autosuspend_delay(dev, 200); pm_runtime_use_autosuspend(dev); pm_runtime_set_active(dev); From b35f42a98b9b3abaa93b7be519dd1bb14e471f57 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 23 Jan 2026 08:59:59 +0800 Subject: [PATCH 221/341] ALSA: hda/tas2781: Drop the unused macro definition Niether TASDEVICE_CALIBRATION_REG_ADDRESS nor TASDEV_UEFI_CALI_REG_ADDR_FLG is referenced in the code, drop both. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260123010000.1841-1-shenghao-ding@ti.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index f8412c5df919..5454b7ac22c6 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -2,7 +2,7 @@ // // TAS2781 HDA SPI driver // -// Copyright 2024 - 2025 Texas Instruments, Inc. +// Copyright 2024 - 2026 Texas Instruments, Inc. // // Author: Baojun Xu @@ -41,9 +41,6 @@ #define TASDEVICE_RANGE_MAX_SIZE (256 * 128) #define TASDEVICE_WIN_LEN 128 #define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ) -/* Flag of calibration registers address. */ -#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7) -#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7) /* System Reset Check Register */ #define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c) From 506e0825a4c9b251d141f0f31c6cde1bdc2983ff Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 21 Jan 2026 17:57:56 -0600 Subject: [PATCH 222/341] ASoC: dt-bindings: Convert ti,tas2552 to DT schema Convert the TI TAS2552 codec binding to DT schema format. It's a straight-forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260121235757.370920-1-robh@kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tas2552.txt | 36 ----------- .../devicetree/bindings/sound/ti,tas2552.yaml | 62 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 63 insertions(+), 37 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/tas2552.txt create mode 100644 Documentation/devicetree/bindings/sound/ti,tas2552.yaml diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt deleted file mode 100644 index a7eecad83db1..000000000000 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ /dev/null @@ -1,36 +0,0 @@ -Texas Instruments - tas2552 Codec module - -The tas2552 serial control bus communicates through I2C protocols - -Required properties: - - compatible - One of: - "ti,tas2552" - TAS2552 - - reg - I2C slave address: it can be 0x40 if ADDR pin is 0 - or 0x41 if ADDR pin is 1. - - supply-*: Required supply regulators are: - "vbat" battery voltage - "iovdd" I/O Voltage - "avdd" Analog DAC Voltage - -Optional properties: - - enable-gpio - gpio pin to enable/disable the device - -tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the -internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM -reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. -For system integration the dt-bindings/sound/tas2552.h header file provides -defined values to select and configure the PLL and PDM reference clocks. - -Example: - -tas2552: tas2552@41 { - compatible = "ti,tas2552"; - reg = <0x41>; - vbat-supply = <®_vbat>; - iovdd-supply = <®_iovdd>; - avdd-supply = <®_avdd>; - enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; -}; - -For more product information please see the link below: -https://www.ti.com/product/TAS2552 diff --git a/Documentation/devicetree/bindings/sound/ti,tas2552.yaml b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml new file mode 100644 index 000000000000..10369aa5f0a8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas2552.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS2552 Codec + +maintainers: + - Shenghao Ding + - Kevin Lu + - Baojun Xu + +description: > + The TAS2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or + use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, + the PDM reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. + + For system integration the dt-bindings/sound/tas2552.h header file provides + defined values to select and configure the PLL and PDM reference clocks. + +properties: + compatible: + const: ti,tas2552 + + reg: + maxItems: 1 + + vbat-supply: true + iovdd-supply: true + avdd-supply: true + + enable-gpio: + maxItems: 1 + description: gpio pin to enable/disable the device + +required: + - compatible + - reg + - vbat-supply + - iovdd-supply + - avdd-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@41 { + compatible = "ti,tas2552"; + reg = <0x41>; + vbat-supply = <®_vbat>; + iovdd-supply = <®_iovdd>; + avdd-supply = <®_avdd>; + enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index da9dbc1a4019..d57fbdff319d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25781,7 +25781,7 @@ M: Kevin Lu M: Baojun Xu L: linux-sound@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/sound/tas2552.txt +F: Documentation/devicetree/bindings/sound/ti,tas2552.yaml F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml From 4a393958b6a1f8e476201437dbad2580d9b8700e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Jan 2026 12:36:58 +0100 Subject: [PATCH 223/341] ASoC: codecs: rt56*: Remove IRQF_ONESHOT Passing IRQF_ONESHOT ensures that the interrupt source is masked until the secondary (threaded) handler is done. If only a primary handler is used then the flag makes no sense because the interrupt can not fire (again) while its handler is running. The flag also disallows force-threading of the primary handler and the irq-core will warn about this. Remove IRQF_ONESHOT from irqflags. Cc: Oder Chiou Cc: Liam Girdwood Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: linux-sound@vger.kernel.org Signed-off-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260123113708.416727-13-bigeasy@linutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 4 ++-- sound/soc/codecs/rt5651.c | 2 +- sound/soc/codecs/rt5663.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 4c08c274f50e..db2222e6f2e7 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2564,7 +2564,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640->use_platform_clock = jack_data->use_platform_clock; ret = request_irq(rt5640->irq, rt5640_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); @@ -2618,7 +2618,7 @@ static void rt5640_enable_hda_jack_detect( rt5640->jack = jack; ret = request_irq(rt5640->irq, rt5640_irq, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); + IRQF_TRIGGER_RISING, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640->jack = NULL; diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 9af65a38f0ee..23c4bf3da298 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2262,7 +2262,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c) ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651); + | IRQF_NO_AUTOEN, "rt5651", rt5651); if (ret) { dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n", rt5651->irq, ret); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index e4d8785e64c1..eee1c98cc4aa 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3689,8 +3689,8 @@ static int rt5663_i2c_probe(struct i2c_client *i2c) if (i2c->irq) { ret = request_irq(i2c->irq, rt5663_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - | IRQF_ONESHOT, "rt5663", rt5663); + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5663", rt5663); if (ret) { dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n", __func__, ret); From 74120bc29856509c585c38dbe963801ecdabac46 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 Jan 2026 11:13:54 +0000 Subject: [PATCH 224/341] ASoC: cs35l56-test: Fix missing module namespace import The test must import namespace "EXPORTED_FOR_KUNIT_TESTING". Signed-off-by: Richard Fitzgerald Fixes: d0ab89951197 ("ASoC: cs35l56: Add KUnit testing of cs35l56_set_fw_suffix()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601221843.kS9IMZ0E-lkp@intel.com/ Link: https://patch.msgid.link/20260123111354.1931986-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c index 66d772d7b982..a7b21660c402 100644 --- a/sound/soc/codecs/cs35l56-test.c +++ b/sound/soc/codecs/cs35l56-test.c @@ -360,6 +360,7 @@ kunit_test_suites( ); MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver"); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_LICENSE("GPL"); From 10d366a846be648aa47cdcd3dc7b7346a4143a6c Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Fri, 23 Jan 2026 15:25:04 +0530 Subject: [PATCH 225/341] ASoC: amd: acp: Fix Kconfig dependencies for SND_SOC_ACPI_AMD_SDCA_QUIRKS Fix the following kconfig warning reported by the kernel test robot: kismet: WARNING: unmet direct dependencies detected for SND_SOC_ACPI_AMD_SDCA_QUIRKS when selected by SND_SOC_ACPI_AMD_MATCH Depends on [n]: SOUND [=y] && SND [=y] && SND_SOC [=y] && ACPI [=y] && SND_SOC_SDCA [=n] Selected by [y]: - SND_SOC_ACPI_AMD_MATCH [=y] && SOUND [=y] && SND [=y] && SND_SOC [=y] The issue occurs because SND_SOC_ACPI_AMD_SDCA_QUIRKS depends on SND_SOC_SDCA, which may be disabled, causing unmet dependency warnings. Fix this by adjusting the Kconfig dependency logic accordingly. Fixes: e7c30ac379b4 ("ASoC: amd: acp: soc-acpi: add is_device_rt712_vb() helper") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601131155.RXGj4KHv-lkp@intel.com Signed-off-by: Syed Saba Kareem Link: https://patch.msgid.link/20260123095524.490655-1-syed.sabakareem@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 4 ++-- sound/soc/amd/acp/amd-acp70-acpi-match.c | 2 ++ sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index b17aaf2c6ccb..977e4f2a7a70 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -15,8 +15,8 @@ config SND_SOC_AMD_ACP_COMMON config SND_SOC_ACPI_AMD_MATCH tristate - select SND_SOC_ACPI_AMD_SDCA_QUIRKS - select SND_SOC_ACPI if ACPI + select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA + select SND_SOC_ACPI if ACPI config SND_SOC_ACPI_AMD_SDCA_QUIRKS tristate diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index fa39f18578ca..c5f42bd79548 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -376,4 +376,6 @@ EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines); MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS"); +#endif diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h index 7e345a236da1..0e644e71e76f 100644 --- a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h +++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h @@ -9,6 +9,17 @@ #ifndef _SND_SOC_ACPI_AMD_SDCA_QUIRKS #define _SND_SOC_ACPI_AMD_SDCA_QUIRKS +#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) + bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg); +#else + +static inline bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg) +{ + return false; +} + +#endif + #endif From 34a74d04cd0d8dd552ba2118bf197f09e51e2336 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:07 +0000 Subject: [PATCH 226/341] ASoC: tlv320adcx140: don't use snd_soc_component_get_bias_level() snd_soc_component_get_bias_level() will be removed. Let's use snd_soc_dapm_get_bias_level() instead. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6mpqgxc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index ac6aab8d7224..e4f27a734501 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1188,8 +1188,8 @@ static int adcx140_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); - enum snd_soc_bias_level prev_level - = snd_soc_component_get_bias_level(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + enum snd_soc_bias_level prev_level = snd_soc_dapm_get_bias_level(dapm); switch (level) { case SND_SOC_BIAS_ON: From 19a412b66df7cddbc1fa87e049c56bacf00adb27 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:18 +0000 Subject: [PATCH 227/341] ASoC: soc-component: remove snd_soc_component_xxx() wrapper Now no one is using snd_soc_component_xxx() wrapper for dapm. Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ms29qgx2.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 21 --------- sound/soc/soc-component.c | 82 ----------------------------------- 2 files changed, 103 deletions(-) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index d78cda866888..8b34958395ca 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -368,27 +368,6 @@ snd_soc_component_active(struct snd_soc_component *component) return component->active; } -/* component pin */ -int snd_soc_component_enable_pin(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_disable_pin(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_nc_pin(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_get_pin_status(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_force_enable_pin(struct snd_soc_component *component, - const char *pin); -int snd_soc_component_force_enable_pin_unlocked( - struct snd_soc_component *component, - const char *pin); - /* component controls */ struct snd_kcontrol *snd_soc_component_get_kcontrol(struct snd_soc_component *component, const char * const ctl); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index c815fd1b3fd1..89f236ab3034 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -142,88 +142,6 @@ int snd_soc_component_set_bias_level(struct snd_soc_component *component, return soc_component_ret(component, ret); } -int snd_soc_component_enable_pin(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_enable_pin(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin); - -int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_enable_pin_unlocked(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked); - -int snd_soc_component_disable_pin(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_disable_pin(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin); - -int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_disable_pin_unlocked(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked); - -int snd_soc_component_nc_pin(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_nc_pin(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin); - -int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_nc_pin_unlocked(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked); - -int snd_soc_component_get_pin_status(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_get_pin_status(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status); - -int snd_soc_component_force_enable_pin(struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_force_enable_pin(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin); - -int snd_soc_component_force_enable_pin_unlocked( - struct snd_soc_component *component, - const char *pin) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); -} -EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked); - static void soc_get_kcontrol_name(struct snd_soc_component *component, char *buf, int size, const char * const ctl) { From d8b795f65217dd033daac5147eba6acb73a9a489 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:27 +0000 Subject: [PATCH 228/341] ASoC: soc-component: remove compatibility definition for component All drivers uses new functions. Remove comaptibility definition. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ldhtqgws.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 8b34958395ca..e538784746db 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -271,9 +271,6 @@ static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm( return &component->dapm; } -// FIXME -#define snd_soc_component_get_dapm snd_soc_component_to_dapm - /** * snd_soc_component_cache_sync() - Sync the register cache with the hardware * @component: COMPONENT to sync From 40ff409eacac686bda70ce7720d8b0e7c7401635 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:36 +0000 Subject: [PATCH 229/341] ASoC: soc-dapm: remove compatibility definition for dapm All drivers uses new functions. Remove comaptibility definition. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jyxdqgwk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 75941324886b..7d3ba3826076 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -705,16 +705,6 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, co int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card); -/* - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - */ -#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin -#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked - /* dapm path query */ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list, @@ -730,15 +720,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); -// REMOVE ME !! -#define snd_soc_component_force_bias_level(c, l) snd_soc_dapm_force_bias_level(&(c)->dapm, l) -#define snd_soc_component_get_bias_level(c) snd_soc_dapm_get_bias_level(&(c)->dapm) -#define snd_soc_component_init_bias_level(c, l) snd_soc_dapm_init_bias_level(&(c)->dapm, l) -#define snd_soc_dapm_kcontrol_widget snd_soc_dapm_kcontrol_to_widget -#define snd_soc_dapm_kcontrol_dapm snd_soc_dapm_kcontrol_to_dapm -#define dapm_kcontrol_get_value snd_soc_dapm_kcontrol_get_value -#define snd_soc_dapm_kcontrol_component snd_soc_dapm_kcontrol_to_component - #define for_each_dapm_widgets(list, i, widget) \ for ((i) = 0; \ (i) < list->num_widgets && (widget = list->widgets[i]); \ From cf0e8c555b34b0ea3d2a41edf6dc214239a71c80 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:46 +0000 Subject: [PATCH 230/341] ASoC: soc-dapm: remove dev from snd_soc_dapm_context() We can get dev via snd_soc_dapm_to_dev(). Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ikcxqgw9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7d3ba3826076..010d63db5436 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -585,7 +585,6 @@ struct snd_soc_dapm_context { bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */ - struct device *dev; /* from parent - for debug */ /* REMOVE ME */ struct snd_soc_component *component; /* parent component */ struct snd_soc_card *card; /* parent card */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4d920a59da3c..4c2007c61ca1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4862,12 +4862,8 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; - if (component) { - dapm->dev = component->dev; + if (component) dapm->idle_bias = component->driver->idle_bias_on; - } else { - dapm->dev = card->dev; - } INIT_LIST_HEAD(&dapm->list); /* see for_each_card_dapms */ From 13c84b4c6f218c196c9c72286645247996800427 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:14:54 +0000 Subject: [PATCH 231/341] ASoC: soc-dapm: add snd_soc_dapm_alloc() Because struct snd_soc_dapm_context is soc-dapm framework specific, user driver don't need to access its member directly, we would like to hide them. struct snd_soc_dapm_context will be removed from header in the future. Current card/component are using dapm_context instance. But it will be moved to soc-dapm.c, and we can use will be only pointer. Makes it to pointer. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5shqgw1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 5 ++--- include/sound/soc-dapm.h | 2 ++ include/sound/soc.h | 4 ++-- sound/soc/soc-core.c | 8 ++++++++ sound/soc/soc-dapm.c | 7 ++++++- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index e538784746db..2a2b74b24a60 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -237,8 +237,7 @@ struct snd_soc_component { * the driver will be marked as BROKEN when these fields are removed. */ - /* Don't use these, use snd_soc_component_get_dapm() */ - struct snd_soc_dapm_context dapm; + struct snd_soc_dapm_context *dapm; /* machine specific init */ int (*init)(struct snd_soc_component *component); @@ -268,7 +267,7 @@ struct snd_soc_component { static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm( struct snd_soc_component *component) { - return &component->dapm; + return component->dapm; } /** diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 010d63db5436..6f3e1b57cda3 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -627,6 +627,8 @@ enum snd_soc_dapm_direction { #define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) #define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) +struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev); + int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/include/sound/soc.h b/include/sound/soc.h index aa0fe6b80293..7d8376c8e1be 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1076,7 +1076,7 @@ struct snd_soc_card { struct list_head dobj_list; /* Generic DAPM context for the card */ - struct snd_soc_dapm_context dapm; + struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_stats dapm_stats; #ifdef CONFIG_DEBUG_FS @@ -1136,7 +1136,7 @@ static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card) static inline struct snd_soc_dapm_context *snd_soc_card_to_dapm(struct snd_soc_card *card) { - return &card->dapm; + return card->dapm; } /* SoC machine DAI configuration, glues a codec and cpu DAI together */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e4b21bf39e59..355ccc95f28b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2556,6 +2556,10 @@ int snd_soc_register_card(struct snd_soc_card *card) if (!card->name || !card->dev) return -EINVAL; + card->dapm = snd_soc_dapm_alloc(card->dev); + if (!card->dapm) + return -ENOMEM; + dev_set_drvdata(card->dev, card); INIT_LIST_HEAD(&card->widgets); @@ -2840,6 +2844,10 @@ int snd_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) + return -ENOMEM; + INIT_LIST_HEAD(&component->dai_list); INIT_LIST_HEAD(&component->dobj_list); INIT_LIST_HEAD(&component->card_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4c2007c61ca1..7aef57dcb2a7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -165,6 +165,11 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) kfree(buf); } +struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev) +{ + return devm_kzalloc(dev, sizeof(struct snd_soc_dapm_context), GFP_KERNEL); +} + struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm) { if (dapm->component) @@ -1076,7 +1081,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, if (ret != 0) goto out; - if (dapm != &card->dapm) + if (dapm != card->dapm) ret = snd_soc_dapm_force_bias_level(dapm, level); if (ret != 0) From 5b517f1a5cace3cba9a48491706e330848ecef86 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:15:01 +0000 Subject: [PATCH 232/341] ASoC: soc-dapm: move struct snd_soc_dapm_context All drivers are now using new dapm functions. Move struct snd_soc_dapm_context to soc-dapm.c Suggested-by: Cezary Rojewski Link: https://lore.kernel.org/r/87o6x69h4y.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87fr81qgvu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 22 +--------------------- sound/soc/soc-dapm.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6f3e1b57cda3..49f0fe05db01 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -20,6 +20,7 @@ struct regulator; struct soc_enum; struct snd_pcm_substream; struct snd_soc_pcm_runtime; +struct snd_soc_dapm_context; /* widget has no PM register bit */ #define SND_SOC_NOPM -1 @@ -579,27 +580,6 @@ struct snd_soc_dapm_update { bool has_second_set; }; -/* DAPM context */ -struct snd_soc_dapm_context { - enum snd_soc_bias_level bias_level; - - bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */ - - struct snd_soc_component *component; /* parent component */ - struct snd_soc_card *card; /* parent card */ - - /* used during DAPM updates */ - enum snd_soc_bias_level target_bias_level; - struct list_head list; - - struct snd_soc_dapm_widget *wcache_sink; - struct snd_soc_dapm_widget *wcache_source; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_dapm; -#endif -}; - /* A list of widgets associated with an object, typically a snd_kcontrol */ struct snd_soc_dapm_widget_list { int num_widgets; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7aef57dcb2a7..07370215ea7c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -40,6 +40,27 @@ #include +/* DAPM context */ +struct snd_soc_dapm_context { + enum snd_soc_bias_level bias_level; + + bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */ + + struct snd_soc_component *component; /* parent component */ + struct snd_soc_card *card; /* parent card */ + + /* used during DAPM updates */ + enum snd_soc_bias_level target_bias_level; + struct list_head list; + + struct snd_soc_dapm_widget *wcache_sink; + struct snd_soc_dapm_widget *wcache_source; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_dapm; +#endif +}; + #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; #define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ From af6d53db28e6448b1e0ce9aa314bc5da494d35ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 20 Jan 2026 00:15:08 +0000 Subject: [PATCH 233/341] ASoC: soc-dapm: tidyup function naming Current soc-dapm is using random naming. Unified as dapm_xxx() for static functions. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ecnlqgvn.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 318 +++++++++++++++++++++---------------------- 1 file changed, 157 insertions(+), 161 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 07370215ea7c..c23ccf4a602d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -63,10 +63,10 @@ struct snd_soc_dapm_context { #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; -#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ +#define DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN) -#define snd_soc_dapm_for_each_direction(dir) \ +#define dapm_for_each_direction(dir) \ for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ (dir)++) @@ -159,14 +159,14 @@ static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) snd_soc_dapm_mutex_assert_held(dapm); } -static void pop_wait(u32 pop_time) +static void dapm_pop_wait(u32 pop_time) { if (pop_time) schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); } __printf(3, 4) -static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) +static void dapm_pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) { va_list args; char *buf; @@ -240,7 +240,7 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) static __always_inline void dapm_widget_invalidate_paths( struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir) { - enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir); struct snd_soc_dapm_widget *node; struct snd_soc_dapm_path *p; LIST_HEAD(list); @@ -387,7 +387,7 @@ struct dapm_kcontrol_data { struct snd_soc_dapm_widget_list *wlist; }; -static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) +static unsigned int dapm_read(struct snd_soc_dapm_context *dapm, int reg) { if (!dapm->component) return -EIO; @@ -407,7 +407,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, unsigned int shift = mc->shift; unsigned int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; - unsigned int val = soc_dapm_read(p->sink->dapm, reg); + unsigned int val = dapm_read(p->sink->dapm, reg); /* * The nth_path argument allows this function to know @@ -423,7 +423,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, */ if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { if (reg != mc->rreg) - val = soc_dapm_read(p->sink->dapm, mc->rreg); + val = dapm_read(p->sink->dapm, mc->rreg); val = (val >> mc->rshift) & mask; } else { val = (val >> shift) & mask; @@ -455,7 +455,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, if (e->reg != SND_SOC_NOPM) { unsigned int val; - val = soc_dapm_read(dapm, e->reg); + val = dapm_read(dapm, e->reg); val = (val >> e->shift_l) & e->mask; item = snd_soc_enum_val_to_item(e, val); } else { @@ -543,7 +543,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) break; case snd_soc_dapm_line: ep = 0; - snd_soc_dapm_for_each_direction(dir) { + dapm_for_each_direction(dir) { if (!list_empty(&w->edges[dir])) ep |= SND_SOC_DAPM_DIR_TO_EP(dir); } @@ -555,7 +555,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) w->is_ep = ep; } -static int snd_soc_dapm_check_dynamic_path( +static int dapm_check_dynamic_path( struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, const char *control) @@ -601,7 +601,7 @@ static int snd_soc_dapm_check_dynamic_path( return 0; } -static int snd_soc_dapm_add_path( +static int dapm_add_path( struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -634,7 +634,7 @@ static int snd_soc_dapm_add_path( return -EINVAL; } - ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); + ret = dapm_check_dynamic_path(dapm, wsource, wsink, control); if (ret) return ret; @@ -686,10 +686,10 @@ static int snd_soc_dapm_add_path( list_add(&path->list, &dapm->card->paths); - snd_soc_dapm_for_each_direction(dir) + dapm_for_each_direction(dir) list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); - snd_soc_dapm_for_each_direction(dir) { + dapm_for_each_direction(dir) { dapm_update_widget_flags(path->node[dir]); dapm_mark_dirty(path->node[dir], "Route added"); } @@ -797,10 +797,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, goto err_data; } - snd_soc_dapm_add_path(widget->dapm, data->widget, - widget, NULL, NULL); + dapm_add_path(widget->dapm, data->widget, + widget, NULL, NULL); } else if (e->reg != SND_SOC_NOPM) { - data->value = soc_dapm_read(widget->dapm, e->reg) & + data->value = dapm_read(widget->dapm, e->reg) & (e->mask << e->shift_l); } break; @@ -980,14 +980,14 @@ static void dapm_reset(struct snd_soc_card *card) } } -static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) +static const char *dapm_prefix(struct snd_soc_dapm_context *dapm) { if (!dapm->component) return NULL; return dapm->component->name_prefix; } -static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, +static int dapm_update_bits(struct snd_soc_dapm_context *dapm, int reg, unsigned int mask, unsigned int value) { if (!dapm->component) @@ -996,7 +996,7 @@ static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, mask, value); } -static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, +static int dapm_test_bits(struct snd_soc_dapm_context *dapm, int reg, unsigned int mask, unsigned int value) { if (!dapm->component) @@ -1004,7 +1004,7 @@ static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, return snd_soc_component_test_bits(dapm->component, reg, mask, value); } -static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) +static void dapm_async_complete(struct snd_soc_dapm_context *dapm) { if (dapm->component) snd_soc_component_async_complete(dapm->component); @@ -1175,7 +1175,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, const char *name; int ret = 0; - prefix = soc_dapm_prefix(dapm); + prefix = dapm_prefix(dapm); if (prefix) prefix_len = strlen(prefix) + 1; else @@ -1295,10 +1295,10 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) data = snd_kcontrol_chip(w->kcontrols[i]); if (data->widget) - snd_soc_dapm_add_path(data->widget->dapm, - data->widget, - path->source, - NULL, NULL); + dapm_add_path(data->widget->dapm, + data->widget, + path->source, + NULL, NULL); } } @@ -1402,7 +1402,7 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. */ -static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) +static int dapm_suspend_check(struct snd_soc_dapm_widget *widget) { struct device *dev = snd_soc_dapm_to_dev(widget->dapm); int level = snd_power_get_state(widget->dapm->card->snd_card); @@ -1454,10 +1454,10 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, * widget and all widgets that can be reached via incoming or outcoming paths * from the widget. */ -static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, +static void dapm_invalidate_paths_ep(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir) { - enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir); struct snd_soc_dapm_path *path; widget->endpoints[dir] = -1; @@ -1471,7 +1471,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, if (path->connect) { path->walking = 1; - invalidate_paths_ep(path->node[dir], dir); + dapm_invalidate_paths_ep(path->node[dir], dir); path->walking = 0; } } @@ -1484,7 +1484,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, * generic function and at the same time the fast path of the specialized * functions is significantly smaller than the generic function. */ -static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, +static __always_inline int dapm_is_connected_ep(struct snd_soc_dapm_widget *widget, struct list_head *list, enum snd_soc_dapm_direction dir, int (*fn)(struct snd_soc_dapm_widget *, struct list_head *, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, @@ -1492,7 +1492,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)) { - enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir); struct snd_soc_dapm_path *path; int con = 0; @@ -1511,7 +1511,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, } if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { - widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); + widget->endpoints[dir] = dapm_suspend_check(widget); return widget->endpoints[dir]; } @@ -1547,13 +1547,13 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, * direction as an arguments, it should return true if widgets from that point * in the graph onwards should not be added to the widget list. */ -static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, +static int dapm_is_connected_output_ep(struct snd_soc_dapm_widget *widget, struct list_head *list, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, enum snd_soc_dapm_direction)) { - return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, - is_connected_output_ep, custom_stop_condition); + return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, + dapm_is_connected_output_ep, custom_stop_condition); } /* @@ -1565,13 +1565,13 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, * direction as an arguments, it should return true if the walk should be * stopped and false otherwise. */ -static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, +static int dapm_is_connected_input_ep(struct snd_soc_dapm_widget *widget, struct list_head *list, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, enum snd_soc_dapm_direction)) { - return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, - is_connected_input_ep, custom_stop_condition); + return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, + dapm_is_connected_input_ep, custom_stop_condition); } /** @@ -1607,12 +1607,12 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, snd_soc_dapm_mutex_lock(card); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); - paths = is_connected_output_ep(w, &widgets, + dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); + paths = dapm_is_connected_output_ep(w, &widgets, custom_stop_condition); } else { - invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); - paths = is_connected_input_ep(w, &widgets, + dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); + paths = dapm_is_connected_input_ep(w, &widgets, custom_stop_condition); } @@ -1645,7 +1645,7 @@ int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, struct device *dev = snd_soc_dapm_to_dev(w->dapm); int ret; - soc_dapm_async_complete(w->dapm); + dapm_async_complete(w->dapm); if (SND_SOC_DAPM_EVENT_ON(event)) { if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { @@ -1705,7 +1705,7 @@ int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, if (!w->clk) return -EIO; - soc_dapm_async_complete(w->dapm); + dapm_async_complete(w->dapm); if (SND_SOC_DAPM_EVENT_ON(event)) { return clk_prepare_enable(w->clk); @@ -1740,8 +1740,8 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); - in = is_connected_input_ep(w, NULL, NULL); - out = is_connected_output_ep(w, NULL, NULL); + in = dapm_is_connected_input_ep(w, NULL, NULL); + out = dapm_is_connected_output_ep(w, NULL, NULL); return out != 0 && in != 0; } @@ -1864,9 +1864,9 @@ static void dapm_seq_check_event(struct snd_soc_card *card, if (w->event && (w->event_flags & event)) { int ret; - pop_dbg(dev, card->pop_time, "pop test : %s %s\n", + dapm_pop_dbg(dev, card->pop_time, "pop test : %s %s\n", w->name, ev_name); - soc_dapm_async_complete(w->dapm); + dapm_async_complete(w->dapm); trace_snd_soc_dapm_widget_event_start(w, event); ret = w->event(w, NULL, event); trace_snd_soc_dapm_widget_event_done(w, event); @@ -1901,7 +1901,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, else value |= w->off_val << w->shift; - pop_dbg(dev, card->pop_time, + dapm_pop_dbg(dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", w->name, reg, value, mask); @@ -1915,11 +1915,11 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, * same register. */ - pop_dbg(dev, card->pop_time, + dapm_pop_dbg(dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); - pop_wait(card->pop_time); - soc_dapm_update_bits(dapm, reg, mask, value); + dapm_pop_wait(card->pop_time); + dapm_update_bits(dapm, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -1973,7 +1973,7 @@ static void dapm_seq_run(struct snd_soc_card *card, } if (cur_dapm && w->dapm != cur_dapm) - soc_dapm_async_complete(cur_dapm); + dapm_async_complete(cur_dapm); INIT_LIST_HEAD(&pending); cur_sort = -1; @@ -2034,7 +2034,7 @@ static void dapm_seq_run(struct snd_soc_card *card, } for_each_card_dapms(card, d) - soc_dapm_async_complete(d); + dapm_async_complete(d); } static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_update *update) @@ -2062,14 +2062,14 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up if (!w) return; - ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask, + ret = dapm_update_bits(w->dapm, update->reg, update->mask, update->val); if (ret < 0) dev_err(dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); if (update->has_second_set) { - ret = soc_dapm_update_bits(w->dapm, update->reg2, + ret = dapm_update_bits(w->dapm, update->reg2, update->mask2, update->val2); if (ret < 0) dev_err(dev, @@ -2384,9 +2384,9 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event, return ret; } - pop_dbg(card->dev, card->pop_time, + dapm_pop_dbg(card->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); - pop_wait(card->pop_time); + dapm_pop_wait(card->pop_time); trace_snd_soc_dapm_done(card, event); @@ -2395,7 +2395,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event, #ifdef CONFIG_DEBUG_FS -static const char * const snd_soc_dapm_type_name[] = { +static const char * const dapm_type_name[] = { [snd_soc_dapm_input] = "input", [snd_soc_dapm_output] = "output", [snd_soc_dapm_mux] = "mux", @@ -2448,7 +2448,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, struct snd_soc_dapm_path *p = NULL; const char *c_name; - BUILD_BUG_ON(ARRAY_SIZE(snd_soc_dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT); buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -2456,13 +2456,13 @@ static ssize_t dapm_widget_power_read_file(struct file *file, snd_soc_dapm_mutex_lock_root(w->dapm); - /* Supply widgets are not handled by is_connected_{input,output}_ep() */ + /* Supply widgets are not handled by dapm_is_connected_{input,output}_ep() */ if (w->is_supply) { in = 0; out = 0; } else { - in = is_connected_input_ep(w, NULL, NULL); - out = is_connected_output_ep(w, NULL, NULL); + in = dapm_is_connected_input_ep(w, NULL, NULL); + out = dapm_is_connected_output_ep(w, NULL, NULL); } ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", @@ -2482,10 +2482,10 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->active ? "active" : "inactive"); ret += scnprintf(buf + ret, PAGE_SIZE - ret, " widget-type %s\n", - snd_soc_dapm_type_name[w->id]); + dapm_type_name[w->id]); - snd_soc_dapm_for_each_direction(dir) { - rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + dapm_for_each_direction(dir) { + rdir = DAPM_DIR_REVERSE(dir); snd_soc_dapm_widget_for_each_path(w, dir, p) { if (p->connected && !p->connected(p->source, p->sink)) continue; @@ -2612,13 +2612,13 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* - * soc_dapm_connect_path() - Connects or disconnects a path + * dapm_connect_path() - Connects or disconnects a path * @path: The path to update * @connect: The new connect state of the path. True if the path is connected, * false if it is disconnected. * @reason: The reason why the path changed (for debugging only) */ -static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, +static void dapm_connect_path(struct snd_soc_dapm_path *path, bool connect, const char *reason) { if (path->connect == connect) @@ -2631,10 +2631,10 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, } /* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_card *card, - struct snd_kcontrol *kcontrol, - struct snd_soc_dapm_update *update, - int mux, struct soc_enum *e) +static int dapm_mux_update_power(struct snd_soc_card *card, + struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_update *update, + int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; @@ -2651,7 +2651,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, else connect = false; - soc_dapm_connect_path(path, connect, "mux update"); + dapm_connect_path(path, connect, "mux update"); } if (found) @@ -2668,7 +2668,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret; snd_soc_dapm_mutex_lock(card); - ret = soc_dapm_mux_update_power(card, kcontrol, update, mux, e); + ret = dapm_mux_update_power(card, kcontrol, update, mux, e); snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -2677,10 +2677,10 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_card *card, - struct snd_kcontrol *kcontrol, - struct snd_soc_dapm_update *update, - int connect, int rconnect) +static int dapm_mixer_update_power(struct snd_soc_card *card, + struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_update *update, + int connect, int rconnect) { struct snd_soc_dapm_path *path; int found = 0; @@ -2712,9 +2712,9 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, * channel. */ if (found && rconnect >= 0) - soc_dapm_connect_path(path, rconnect, "mixer update"); + dapm_connect_path(path, rconnect, "mixer update"); else - soc_dapm_connect_path(path, connect, "mixer update"); + dapm_connect_path(path, connect, "mixer update"); found = 1; } @@ -2732,7 +2732,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret; snd_soc_dapm_mutex_lock(card); - ret = soc_dapm_mixer_update_power(card, kcontrol, update, connect, -1); + ret = dapm_mixer_update_power(card, kcontrol, update, connect, -1); snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -2862,7 +2862,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) * While removing the path, remove reference to it from both * source and sink widgets so that path is removed only once. */ - snd_soc_dapm_for_each_direction(dir) { + dapm_for_each_direction(dir) { snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) dapm_free_path(p); } @@ -2899,7 +2899,7 @@ static struct snd_soc_dapm_widget *dapm_find_widget( struct snd_soc_dapm_widget *fallback = NULL; char prefixed_pin[80]; const char *pin_name; - const char *prefix = soc_dapm_prefix(dapm); + const char *prefix = dapm_prefix(dapm); if (prefix) { snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s", @@ -2929,8 +2929,8 @@ static struct snd_soc_dapm_widget *dapm_find_widget( * returns 1 when the value has been updated, 0 when unchanged, or a negative * error code; called from kcontrol put callback */ -static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, - const char *pin, int status) +static int __dapm_set_pin(struct snd_soc_dapm_context *dapm, + const char *pin, int status) { struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); struct device *dev = snd_soc_dapm_to_dev(dapm); @@ -2958,13 +2958,13 @@ static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, } /* - * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful; + * similar as __dapm_set_pin(), but returns 0 when successful; * called from several API functions below */ -static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, +static int dapm_set_pin(struct snd_soc_dapm_context *dapm, const char *pin, int status) { - int ret = __snd_soc_dapm_set_pin(dapm, pin, status); + int ret = __dapm_set_pin(dapm, pin, status); return ret < 0 ? ret : 0; } @@ -3032,9 +3032,9 @@ static int dapm_update_dai_chan(struct snd_soc_dapm_path *p, p->source->name, p->sink->name); if (w->channel < channels) - soc_dapm_connect_path(p, true, "dai update"); + dapm_connect_path(p, true, "dai update"); else - soc_dapm_connect_path(p, false, "dai update"); + dapm_connect_path(p, false, "dai update"); return 0; } @@ -3112,7 +3112,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, unsigned int source_ref = 0; int ret; - prefix = soc_dapm_prefix(dapm); + prefix = dapm_prefix(dapm); if (prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", prefix, route->sink); @@ -3181,7 +3181,7 @@ skip_search: dapm->wcache_sink = wsink; dapm->wcache_source = wsource; - ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, + ret = dapm_add_path(dapm, wsource, wsink, route->control, route->connected); err: if (ret) @@ -3211,7 +3211,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, return -EINVAL; } - prefix = soc_dapm_prefix(dapm); + prefix = dapm_prefix(dapm); if (prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", prefix, route->sink); @@ -3365,7 +3365,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_dapm_read(w->dapm, w->reg); + val = dapm_read(w->dapm, w->reg); val = val >> w->shift; val &= w->mask; if (val == w->on_val) @@ -3409,11 +3409,11 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, snd_soc_dapm_mutex_lock(dapm); if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { - reg_val = soc_dapm_read(dapm, reg); + reg_val = dapm_read(dapm, reg); val = (reg_val >> shift) & mask; if (reg != mc->rreg) - reg_val = soc_dapm_read(dapm, mc->rreg); + reg_val = dapm_read(dapm, mc->rreg); if (snd_soc_volsw_is_stereo(mc)) rval = (reg_val >> mc->rshift) & mask; @@ -3497,12 +3497,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, val = val << shift; rval = rval << mc->rshift; - reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val); + reg_change = dapm_test_bits(dapm, reg, mask << shift, val); if (snd_soc_volsw_is_stereo(mc)) - reg_change |= soc_dapm_test_bits(dapm, mc->rreg, - mask << mc->rshift, - rval); + reg_change |= dapm_test_bits(dapm, mc->rreg, + mask << mc->rshift, + rval); } if (change || reg_change) { @@ -3519,7 +3519,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.val = val; pupdate = &update; } - ret = soc_dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect); + ret = dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect); } snd_soc_dapm_mutex_unlock(card); @@ -3549,7 +3549,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, snd_soc_dapm_mutex_lock(dapm); if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) { - reg_val = soc_dapm_read(dapm, e->reg); + reg_val = dapm_read(dapm, e->reg); } else { reg_val = snd_soc_dapm_kcontrol_get_value(kcontrol); } @@ -3606,7 +3606,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = dapm_kcontrol_set_value(kcontrol, val); if (e->reg != SND_SOC_NOPM) - reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val); + reg_change = dapm_test_bits(dapm, e->reg, mask, val); if (change || reg_change) { if (reg_change) { @@ -3616,7 +3616,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; pupdate = &update; } - ret = soc_dapm_mux_update_power(card, kcontrol, pupdate, item[0], e); + ret = dapm_mux_update_power(card, kcontrol, pupdate, item[0], e); } snd_soc_dapm_mutex_unlock(card); @@ -3699,14 +3699,14 @@ int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_component_pin_switch); -static int __snd_soc_dapm_put_pin_switch(struct snd_soc_dapm_context *dapm, - const char *pin, - struct snd_ctl_elem_value *ucontrol) +static int __dapm_put_pin_switch(struct snd_soc_dapm_context *dapm, + const char *pin, + struct snd_ctl_elem_value *ucontrol) { int ret; snd_soc_dapm_mutex_lock(dapm); - ret = __snd_soc_dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]); + ret = __dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]); snd_soc_dapm_mutex_unlock(dapm); snd_soc_dapm_sync(dapm); @@ -3730,7 +3730,7 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); const char *pin = (const char *)kcontrol->private_value; - return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol); + return __dapm_put_pin_switch(dapm, pin, ucontrol); } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); @@ -3750,7 +3750,7 @@ int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); const char *pin = (const char *)kcontrol->private_value; - return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol); + return __dapm_put_pin_switch(dapm, pin, ucontrol); } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_component_pin_switch); @@ -3763,7 +3763,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w; int ret = -ENOMEM; - w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm)); + w = dapm_cnew_widget(widget, dapm_prefix(dapm)); if (!w) goto cnew_failed; @@ -3878,7 +3878,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, /* see for_each_card_widgets */ list_add_tail(&w->list, &dapm->card->widgets); - snd_soc_dapm_for_each_direction(dir) { + dapm_for_each_direction(dir) { INIT_LIST_HEAD(&w->edges[dir]); w->endpoints[dir] = -1; } @@ -3951,9 +3951,8 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); -static int -snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, - struct snd_pcm_substream *substream) +static int dapm_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, + struct snd_pcm_substream *substream) { struct device *dev = snd_soc_dapm_to_dev(w->dapm); struct snd_soc_dapm_path *path; @@ -4068,8 +4067,8 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, return 0; } -static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int dapm_dai_link_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_dapm_path *path; struct snd_soc_dai *source, *sink; @@ -4082,7 +4081,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - ret = snd_soc_dai_link_event_pre_pmu(w, substream); + ret = dapm_dai_link_event_pre_pmu(w, substream); if (ret < 0) goto out; @@ -4160,8 +4159,8 @@ out: return ret; } -static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int dapm_dai_link_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); struct snd_soc_pcm_runtime *rtd = w->priv; @@ -4171,8 +4170,8 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, return 0; } -static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int dapm_dai_link_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); struct snd_soc_pcm_runtime *rtd = w->priv; @@ -4192,11 +4191,10 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, return 1; } -static void -snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, - unsigned long *private_value, - int num_c2c_params, - const char **w_param_text) +static void dapm_free_kcontrol(struct snd_soc_card *card, + unsigned long *private_value, + int num_c2c_params, + const char **w_param_text) { int count; @@ -4211,7 +4209,7 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, } static struct snd_kcontrol_new * -snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, +dapm_alloc_kcontrol(struct snd_soc_card *card, char *link_name, const struct snd_soc_pcm_stream *c2c_params, int num_c2c_params, const char **w_param_text, @@ -4222,8 +4220,8 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, }; struct snd_kcontrol_new kcontrol_dai_link[] = { SOC_ENUM_EXT(NULL, w_param_enum[0], - snd_soc_dapm_dai_link_get, - snd_soc_dapm_dai_link_put), + dapm_dai_link_get, + dapm_dai_link_put), }; struct snd_kcontrol_new *kcontrol_news; const struct snd_soc_pcm_stream *config = c2c_params; @@ -4274,14 +4272,14 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, return kcontrol_news; outfree_w_param: - snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text); + dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text); + return NULL; } -static struct snd_soc_dapm_widget * -snd_soc_dapm_new_dai(struct snd_soc_card *card, - struct snd_pcm_substream *substream, - char *id) +static struct snd_soc_dapm_widget *dapm_new_dai(struct snd_soc_card *card, + struct snd_pcm_substream *substream, + char *id) { struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -4311,10 +4309,10 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, goto param_fail; num_kcontrols = 1; - kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name, - rtd->dai_link->c2c_params, - rtd->dai_link->num_c2c_params, - w_param_text, &private_value); + kcontrol_news = dapm_alloc_kcontrol(card, link_name, + rtd->dai_link->c2c_params, + rtd->dai_link->num_c2c_params, + w_param_text, &private_value); if (!kcontrol_news) goto param_fail; } @@ -4323,7 +4321,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, template.reg = SND_SOC_NOPM; template.id = snd_soc_dapm_dai_link; template.name = link_name; - template.event = snd_soc_dai_link_event; + template.event = dapm_dai_link_event; template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; template.kcontrol_news = kcontrol_news; @@ -4343,7 +4341,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, outfree_kcontrol_news: devm_kfree(card->dev, (void *)template.kcontrol_news); - snd_soc_dapm_free_kcontrol(card, &private_value, + dapm_free_kcontrol(card, &private_value, rtd->dai_link->num_c2c_params, w_param_text); param_fail: devm_kfree(card->dev, link_name); @@ -4457,7 +4455,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) sink = dai_w; } dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name); - snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL); + dapm_add_path(w->dapm, src, sink, NULL, NULL); } } @@ -4478,11 +4476,11 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm, sink_dai->component->name, sink->name); if (dai) { - snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL); + dapm_add_path(dapm, src, dai, NULL, NULL); src = dai; } - snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL); + dapm_add_path(dapm, src, sink, NULL, NULL); } static void dapm_connect_dai_pair(struct snd_soc_card *card, @@ -4516,8 +4514,8 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, /* special handling for [Codec2Codec] */ if (dai_link->c2c_params && !rtd->c2c_widget[stream]) { struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream; - struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream, - widget_name[stream]); + struct snd_soc_dapm_widget *dai = dapm_new_dai(card, substream, + widget_name[stream]); if (IS_ERR(dai)) continue; @@ -4531,8 +4529,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, } } -static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, - int event) +static void dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event) { struct snd_soc_dapm_widget *w; @@ -4600,14 +4597,13 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) } } -static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, - int event) +static void dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { struct snd_soc_dai *dai; int i; for_each_rtd_dais(rtd, i, dai) - soc_dapm_dai_stream_event(dai, stream, event); + dapm_dai_stream_event(dai, stream, event); dapm_power_widgets(rtd->card, event, NULL); } @@ -4629,7 +4625,7 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, struct snd_soc_card *card = rtd->card; snd_soc_dapm_mutex_lock(card); - soc_dapm_stream_event(rtd, stream, event); + dapm_stream_event(rtd, stream, event); snd_soc_dapm_mutex_unlock(card); } @@ -4672,7 +4668,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop); int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 1); + return dapm_set_pin(dapm, pin, 1); } EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked); @@ -4693,7 +4689,7 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) snd_soc_dapm_mutex_lock(dapm); - ret = snd_soc_dapm_set_pin(dapm, pin, 1); + ret = dapm_set_pin(dapm, pin, 1); snd_soc_dapm_mutex_unlock(dapm); @@ -4789,7 +4785,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 0); + return dapm_set_pin(dapm, pin, 0); } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked); @@ -4810,7 +4806,7 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, snd_soc_dapm_mutex_lock(dapm); - ret = snd_soc_dapm_set_pin(dapm, pin, 0); + ret = dapm_set_pin(dapm, pin, 0); snd_soc_dapm_mutex_unlock(dapm); @@ -4896,7 +4892,7 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, list_add(&dapm->list, &card->dapm_list); } -static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) +static void dapm_shutdown(struct snd_soc_dapm_context *dapm) { struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; @@ -4941,13 +4937,13 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) for_each_card_dapms(card, dapm) { if (dapm != card_dapm) { - soc_dapm_shutdown_dapm(dapm); + dapm_shutdown(dapm); if (dapm->bias_level == SND_SOC_BIAS_STANDBY) snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF); } } - soc_dapm_shutdown_dapm(card_dapm); + dapm_shutdown(card_dapm); if (card_dapm->bias_level == SND_SOC_BIAS_STANDBY) snd_soc_dapm_set_bias_level(card_dapm, SND_SOC_BIAS_OFF); } From 7550d6263b9b2015a806f66bf7ad356b6f25d050 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 23 Jan 2026 16:24:58 +0800 Subject: [PATCH 234/341] ASoC: dt-bindings: fsl,audmix: Add support for i.MX952 platform Add a compatible string for i.MX952 platform. There is a power domain on i.MX952 for the mix system of AUDMIX. But it is enabled by default, AUDMIX device don't need to enable it, so make the power-domains to be optional on i.MX952, and be required on i.MX8QM. Signed-off-by: Shengjiu Wang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260123082501.4050296-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,audmix.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml index 3ad197b3c82c..07b9a38761f2 100644 --- a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml @@ -34,7 +34,9 @@ description: | properties: compatible: - const: fsl,imx8qm-audmix + enum: + - fsl,imx8qm-audmix + - fsl,imx952-audmix reg: maxItems: 1 @@ -80,7 +82,17 @@ required: - reg - clocks - clock-names - - power-domains + +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-audmix + then: + required: + - power-domains unevaluatedProperties: false From 4d3b56b8a3504dab98d5f9a91ed9091431749863 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 23 Jan 2026 16:24:59 +0800 Subject: [PATCH 235/341] ASoC: dt-bindings: fsl,sai: Add AUDMIX mode support on i.MX952 The SAI can connect to AUDMIX, but AUDMIX can be bypassed or not on i.MX952. There are three use cases: 1) SAI -> Codec (No AUDMIX between SAI and Codec) 2) SAI -> Codec (Has AUDMIX, but AUDMIX is bypassed) 3) SAI -> AUDMIX -> Codec (Has AUDMIX and used) So add 'fsl,sai-amix-mode' property for this feature fsl,sai-amix-mode = "none": is for case 1) fsl,sai-amix-mode = "bypass": is for case 2) fsl,sai-amix-mode = "audmix": is for case 3) Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260123082501.4050296-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,sai.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml index 0d733e5b08a4..e35d25edb555 100644 --- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml @@ -132,6 +132,13 @@ properties: - description: dataline mask for 'rx' - description: dataline mask for 'tx' + fsl,sai-amix-mode: + $ref: /schemas/types.yaml#/definitions/string + description: + The audmix module is bypassed from hardware or not. + enum: [none, bypass, audmix] + default: none + fsl,sai-mclk-direction-output: description: SAI will output the SAI MCLK clock. type: boolean @@ -179,6 +186,15 @@ allOf: properties: fsl,sai-synchronous-rx: false + - if: + required: + - fsl,sai-amix-mode + then: + properties: + compatible: + contains: + const: fsl,imx952-sai + required: - compatible - reg From 291f2f908823832e932582500816ad614631d568 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 23 Jan 2026 16:25:00 +0800 Subject: [PATCH 236/341] ASoC: fsl_audmix: Add support for i.MX952 platform Add compatible string to support AUDMIX on i.MX952 Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260123082501.4050296-4-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 7981d598ba13..40a3b7432174 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -444,6 +444,9 @@ static const struct of_device_id fsl_audmix_ids[] = { { .compatible = "fsl,imx8qm-audmix", }, + { + .compatible = "fsl,imx952-audmix", + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_audmix_ids); From 19b08fd23b20593ebe43708308dbddb02507877d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 23 Jan 2026 16:25:01 +0800 Subject: [PATCH 237/341] ASoC: fsl_sai: Add AUDMIX mode support on i.MX952 One of SAI interfaces is connected to AUDMIX in the i.MX952 chip, but AUDMIX can be bypassed or not bypassed on the i.MX952 platform. There are three use cases: 1) SAI -> Codec (No AUDMIX between SAI and Codec) 2) SAI -> Codec (Has AUDMIX, but AUDMIX is bypassed) 3) SAI -> AUDMIX -> Codec (Has AUDMIX and used) So add 'fsl,sai-amix-mode' property for this feature fsl,sai-amix-mode = "none": is for case 1) fsl,sai-amix-mode = "bypass": is for case 2) fsl,sai-amix-mode = "audmix": is for case 3) Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260123082501.4050296-5-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- include/linux/firmware/imx/sm.h | 2 ++ sound/soc/fsl/fsl_sai.c | 21 +++++++++++++++++++++ sound/soc/fsl/fsl_sai.h | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/include/linux/firmware/imx/sm.h b/include/linux/firmware/imx/sm.h index a33b45027356..ba5d93bd6158 100644 --- a/include/linux/firmware/imx/sm.h +++ b/include/linux/firmware/imx/sm.h @@ -26,6 +26,8 @@ #define SCMI_IMX94_CTRL_SAI3_MCLK 5U /*!< WAKE SAI3 MCLK */ #define SCMI_IMX94_CTRL_SAI4_MCLK 6U /*!< WAKE SAI4 MCLK */ +#define SCMI_IMX952_CTRL_BYPASS_AUDMIX 8U /* WAKE AUDMIX */ + #if IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val); int scmi_imx_misc_ctrl_set(u32 id, u32 val); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 2fa14fbdfe1a..148e09e58dfa 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1425,10 +1426,12 @@ static int fsl_sai_probe(struct platform_device *pdev) struct fsl_sai *sai; struct regmap *gpr; void __iomem *base; + const char *str = NULL; char tmp[8]; int irq, ret, i; int index; u32 dmas[4]; + u32 val; sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -1598,6 +1601,24 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync; + if (of_device_is_compatible(np, "fsl,imx952-sai") && + !of_property_read_string(np, "fsl,sai-amix-mode", &str)) { + if (!strcmp(str, "bypass")) + val = FSL_SAI_AMIX_BYPASS; + else if (!strcmp(str, "audmix")) + val = FSL_SAI_AMIX_AUDMIX; + else + val = FSL_SAI_AMIX_NONE; + + if (val < FSL_SAI_AMIX_NONE) { + ret = scmi_imx_misc_ctrl_set(SCMI_IMX952_CTRL_BYPASS_AUDMIX, val); + if (ret) { + dev_err_probe(dev, ret, "Error setting audmix mode\n"); + goto err_pm_get_sync; + } + } + } + /* * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime(). diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 6c917f79c6b0..7605cbaca3d8 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -230,6 +230,10 @@ #define FSL_SAI_DL_I2S BIT(0) #define FSL_SAI_DL_PDM BIT(1) +#define FSL_SAI_AMIX_BYPASS 0 +#define FSL_SAI_AMIX_AUDMIX 1 +#define FSL_SAI_AMIX_NONE 2 + struct fsl_sai_soc_data { bool use_imx_pcm; bool use_edma; From 3febba217ea73ca86d5e331b8cde98bc6d08fcf5 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Sun, 25 Jan 2026 19:18:37 +0800 Subject: [PATCH 238/341] ASoC: sdw_utils: Add quirk to ignore RT722 DMIC DAI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the device uses the host DMIC as the capture source, add CODEC_MIC quirk to exclude the RT722 codec DMIC DAI link. Signed-off-by: Mac Chiang Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260125111837.2386530-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 2f3c9698a0e8..0e67d9f34cba 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -506,6 +506,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, }, }, .dai_num = 3, From 233ccfe911aa62c0f9211f2e3297d6a7f870b295 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 24 Jan 2026 11:17:03 +0100 Subject: [PATCH 239/341] ASoC: codecs: rt1320-sdw: Refactor to reduce stack frames Compiler is not happy about used stack frames in a couple of functions: sound/soc/codecs/rt1320-sdw.c: In function 'rt1320_rae_load': sound/soc/codecs/rt1320-sdw.c:1570:1: error: the frame size of 1336 bytes is larger than 1280 bytes [-Werror=frame-larger-than=] sound/soc/codecs/rt1320-sdw.c: In function 'rt1320_dspfw_load_code': sound/soc/codecs/rt1320-sdw.c:1786:1: error: the frame size of 1520 bytes is larger than 1280 bytes [-Werror=frame-larger-than=] Refactor the code to fix these. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260124101824.3424793-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 43 +++++++++++------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index e37b4cb87fbe..3ad51db23dea 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1429,8 +1429,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) unsigned int addr, size; unsigned int func, value; const char *dmi_vendor, *dmi_product, *dmi_sku; - char vendor[128], product[128], sku[128]; - char *ptr_vendor, *ptr_product, *ptr_sku; + int len_vendor, len_product, len_sku; char rae_filename[512]; char tag[5]; int ret = 0; @@ -1441,21 +1440,13 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); if (dmi_vendor && dmi_product && dmi_sku) { - strscpy(vendor, dmi_vendor); - strscpy(product, dmi_product); - strscpy(sku, dmi_sku); - ptr_vendor = &vendor[0]; - ptr_product = &product[0]; - ptr_sku = &sku[0]; - ptr_vendor = strsep(&ptr_vendor, " "); - ptr_product = strsep(&ptr_product, " "); - ptr_sku = strsep(&ptr_sku, " "); - - dev_dbg(dev, "%s: DMI vendor=%s, product=%s, sku=%s\n", __func__, - vendor, product, sku); + len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor; + len_product = strchrnul(dmi_product, ' ') - dmi_product; + len_sku = strchrnul(dmi_sku, ' ') - dmi_sku; snprintf(rae_filename, sizeof(rae_filename), - "realtek/rt1320/rt1320_RAE_%s_%s_%s.dat", vendor, product, sku); + "realtek/rt1320/rt1320_RAE_%.*s_%.*s_%.*s.dat", + len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku); dev_dbg(dev, "%s: try to load RAE file %s\n", __func__, rae_filename); } else { dev_warn(dev, "%s: Can't find proper RAE file name\n", __func__); @@ -1595,8 +1586,7 @@ struct rt1320_dspfwheader { static const char hdr_sig[] = "AFX"; unsigned int hdr_size = 0; const char *dmi_vendor, *dmi_product, *dmi_sku; - char vendor[128], product[128], sku[128]; - char *ptr_vendor, *ptr_product, *ptr_sku; + int len_vendor, len_product, len_sku; char filename[512]; switch (rt1320->dev_id) { @@ -1616,21 +1606,14 @@ struct rt1320_dspfwheader { dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); if (dmi_vendor && dmi_product && dmi_sku) { - strscpy(vendor, dmi_vendor); - strscpy(product, dmi_product); - strscpy(sku, dmi_sku); - ptr_vendor = &vendor[0]; - ptr_product = &product[0]; - ptr_sku = &sku[0]; - ptr_vendor = strsep(&ptr_vendor, " "); - ptr_product = strsep(&ptr_product, " "); - ptr_sku = strsep(&ptr_sku, " "); - - dev_dbg(dev, "%s: DMI vendor=%s, product=%s, sku=%s\n", __func__, - vendor, product, sku); + len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor; + len_product = strchrnul(dmi_product, ' ') - dmi_product; + len_sku = strchrnul(dmi_sku, ' ') - dmi_sku; snprintf(filename, sizeof(filename), - "realtek/rt1320/rt1320_%s_%s_%s.dat", vendor, product, sku); + "realtek/rt1320/rt1320_%.*s_%.*s_%.*s.dat", + len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku); + dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename); } else if (rt1320->dspfw_name) { snprintf(filename, sizeof(filename), "rt1320_%s.dat", From f80bee70b1938f904d0b12783044d5eebcc6879b Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Mon, 26 Jan 2026 06:55:36 -0800 Subject: [PATCH 240/341] ASoC: dt-bindings: fsl,mqs: make gpr optional for SM-based SoCs For SM-based SoCs (i.e. MX95, MX943), GPR configuration is performed by the SM coprocessor. Thus, GPR is transparent to the software and does not need to be described in the devicetree. Make it optional. Reviewed-by: Frank Li Reviewed-by: Krzysztof Kozlowski Signed-off-by: Laurentiu Mihalcea Link: https://patch.msgid.link/20260126145537.2301-2-laurentiumihalcea111@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,mqs.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml index 1415247c92c8..bcc265a742c7 100644 --- a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml @@ -63,6 +63,16 @@ required: allOf: - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - fsl,imx6sx-mqs + - fsl,imx93-mqs + then: + required: + - gpr - if: properties: compatible: @@ -91,8 +101,6 @@ allOf: clock-names: items: - const: mclk - required: - - gpr unevaluatedProperties: false From 87ee3f05bfe2f5955c2c77ae26a1be236fd615a5 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Mon, 26 Jan 2026 15:35:28 +0800 Subject: [PATCH 241/341] ASoC: codecs: wm8731: Remove unnecessary NULL check before clk_prepare_enable/clk_disable_unprepare clk_prepare_enable() and clk_disable_unprepare() already checked NULL clock parameter. Remove unneeded NULL check for wm8731->mclk here. Signed-off-by: Chen Ni Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260126073528.1826406-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index a03bbde5d852..a2f0e2f5c407 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -471,11 +471,9 @@ static int wm8731_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: - if (wm8731->mclk) { - ret = clk_prepare_enable(wm8731->mclk); - if (ret) - return ret; - } + ret = clk_prepare_enable(wm8731->mclk); + if (ret) + return ret; break; case SND_SOC_BIAS_PREPARE: break; @@ -494,8 +492,7 @@ static int wm8731_set_bias_level(struct snd_soc_component *component, snd_soc_component_write(component, WM8731_PWR, reg | 0x0040); break; case SND_SOC_BIAS_OFF: - if (wm8731->mclk) - clk_disable_unprepare(wm8731->mclk); + clk_disable_unprepare(wm8731->mclk); snd_soc_component_write(component, WM8731_PWR, 0xffff); regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); From cc051fbd7f40226cc407558bc97c5099513e8657 Mon Sep 17 00:00:00 2001 From: Damien Dagorn Date: Fri, 23 Jan 2026 18:14:52 +0100 Subject: [PATCH 242/341] ALSA: hda/realtek: fix LG Gram Style 14 speakers The LG Gram Style 14 (14Z90RS-G.AD77F, SSID 1854:0490) with Realtek ALC298 shows normal routing and volume changes, but internal speakers stay silent unless a userland HDA-verb workaround is applied. Add a dedicated quirk for the LG Gram Style 14 that programs the codec coefficient sequence used by the known workaround and enables the speaker amps only during playback. Tested-by: Damien Dagorn Signed-off-by: Damien Dagorn Link: https://lore.kernel.org/CAN59QMUhd4kHrkRoJA6VzEr2VKezN2yjHnANaQoZn2-Bnwe3bQ@mail.gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index cf263a164987..3d5a977bb40a 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1854,6 +1854,163 @@ static void alc298_samsung_v2_init_amps(struct hda_codec *codec, spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook; } +/* LG Gram Style 14: program vendor coef sequence used by HDA-verb workaround */ +struct alc298_lg_gram_style_seq { + unsigned short verb; + unsigned short idx; + unsigned short val; +}; + +static void alc298_lg_gram_style_coef_write(struct hda_codec *codec, + unsigned int verb, + unsigned int idx, + unsigned int val) +{ + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x23); + snd_hda_codec_write(codec, 0x20, 0, verb, idx); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0x00); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, val); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb011); +} + +static void alc298_lg_gram_style_run_seq(struct hda_codec *codec, + const struct alc298_lg_gram_style_seq *seq, + int seq_size) +{ + int i; + + for (i = 0; i < seq_size; i++) + alc298_lg_gram_style_coef_write(codec, seq[i].verb, + seq[i].idx, seq[i].val); +} + +/* Coef sequences derived from the HDA-verb workaround for this model. */ +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_preinit_seq[] = { + { 0x420, 0x00, 0x01 }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_disable_seq[] = { + { 0x423, 0xff, 0x00 }, + { 0x420, 0x3a, 0x80 }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_enable_seq[] = { + { 0x420, 0x3a, 0x81 }, + { 0x423, 0xff, 0x01 }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_38[] = { + { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 }, + { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe }, + { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 }, + { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e }, + { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 }, + { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_39[] = { + { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 }, + { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd }, + { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 }, + { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e }, + { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 }, + { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3c[] = { + { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 }, + { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe }, + { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 }, + { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e }, + { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d }, +}; + +static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3d[] = { + { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 }, + { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd }, + { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 }, + { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e }, + { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d }, +}; + +struct alc298_lg_gram_style_amp_desc { + unsigned char nid; + const struct alc298_lg_gram_style_seq *init_seq; + int init_seq_size; +}; + +static const struct alc298_lg_gram_style_amp_desc alc298_lg_gram_style_amps[] = { + { 0x38, alc298_lg_gram_style_init_seq_38, + ARRAY_SIZE(alc298_lg_gram_style_init_seq_38) }, + { 0x39, alc298_lg_gram_style_init_seq_39, + ARRAY_SIZE(alc298_lg_gram_style_init_seq_39) }, + { 0x3c, alc298_lg_gram_style_init_seq_3c, + ARRAY_SIZE(alc298_lg_gram_style_init_seq_3c) }, + { 0x3d, alc298_lg_gram_style_init_seq_3d, + ARRAY_SIZE(alc298_lg_gram_style_init_seq_3d) }, +}; + +static void alc298_lg_gram_style_enable_amps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid); + alc298_lg_gram_style_run_seq(codec, + alc298_lg_gram_style_enable_seq, + ARRAY_SIZE(alc298_lg_gram_style_enable_seq)); + } +} + +static void alc298_lg_gram_style_disable_amps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid); + alc298_lg_gram_style_run_seq(codec, + alc298_lg_gram_style_disable_seq, + ARRAY_SIZE(alc298_lg_gram_style_disable_seq)); + } +} + +static void alc298_lg_gram_style_playback_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + if (action == HDA_GEN_PCM_ACT_OPEN) + alc298_lg_gram_style_enable_amps(codec); + if (action == HDA_GEN_PCM_ACT_CLOSE) + alc298_lg_gram_style_disable_amps(codec); +} + +static void alc298_lg_gram_style_init_amps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + spec->num_speaker_amps = ARRAY_SIZE(alc298_lg_gram_style_amps); + + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid); + alc298_lg_gram_style_run_seq(codec, + alc298_lg_gram_style_preinit_seq, + ARRAY_SIZE(alc298_lg_gram_style_preinit_seq)); + alc298_lg_gram_style_run_seq(codec, + alc298_lg_gram_style_disable_seq, + ARRAY_SIZE(alc298_lg_gram_style_disable_seq)); + alc298_lg_gram_style_run_seq(codec, + alc298_lg_gram_style_amps[i].init_seq, + alc298_lg_gram_style_amps[i].init_seq_size); + alc_write_coef_idx(codec, 0x89, 0x0); + } + + spec->gen.pcm_playback_hook = alc298_lg_gram_style_playback_hook; +} + static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -1868,6 +2025,13 @@ static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, alc298_samsung_v2_init_amps(codec, 4); } +static void alc298_fixup_lg_gram_style_14(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + alc298_lg_gram_style_init_amps(codec); +} + static void gpio2_mic_hotkey_event(struct hda_codec *codec, struct hda_jack_callback *event) { @@ -3753,6 +3917,7 @@ enum { ALC298_FIXUP_SAMSUNG_AMP, ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, + ALC298_FIXUP_LG_GRAM_STYLE_14, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -5430,6 +5595,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_samsung_amp_v2_4_amps }, + [ALC298_FIXUP_LG_GRAM_STYLE_14] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_lg_gram_style_14 + }, [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -7368,6 +7537,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), 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(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 bfa514c4613b39e536d4a9dfbcb8eb8e68776343 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 25 Jan 2026 16:51:56 +0100 Subject: [PATCH 243/341] ALSA: jack: Improve string handling in jack_kctl_name_gen If appending " Jack" is not necessary, replace snprintf("%s", ...) with the faster strscpy(). Additionally, rename 'need_cat' to the clearer 'append_suf', use local variables for the suffix and its length, remove the confusing comment, compare strncmp() to 0, and use 'size_t' for the 'size' function parameter. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260125155159.98720-1-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/core/ctljack.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c index 709b1a9c2caa..6b6ab34fbde8 100644 --- a/sound/core/ctljack.c +++ b/sound/core/ctljack.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -46,17 +47,20 @@ static int get_available_index(struct snd_card *card, const char *name) return sid.index; } -static void jack_kctl_name_gen(char *name, const char *src_name, int size) +static void jack_kctl_name_gen(char *name, const char *src_name, size_t size) { size_t count = strlen(src_name); - bool need_cat = true; + const char *suf = " Jack"; + size_t suf_len = strlen(suf); + bool append_suf = true; - /* remove redundant " Jack" from src_name */ - if (count >= 5) - need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false; - - snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name); + if (count >= suf_len) + append_suf = strncmp(&src_name[count - suf_len], suf, suf_len) != 0; + if (append_suf) + snprintf(name, size, "%s%s", src_name, suf); + else + strscpy(name, src_name, size); } struct snd_kcontrol * From e4808c60b1b1548631041c7db60da06b7d3a3662 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Mon, 26 Jan 2026 11:18:16 +0800 Subject: [PATCH 244/341] ALSA: hda/tas2781: Add tas2781_hda::catlog_id init The default of tas2781_hda::catlog_id is DELL, which cause calibration data is not loaded in HP SPI-basded Laptop, because only HP laptop supports SPI-based TAS2781. Fixes: 9fa6a693ad8d ("ALSA: hda/tas2781: Remove tas2781_spi_fwlib.c and leverage SND_SOC_TAS2781_FMWLIB") Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260126031816.1123-1-shenghao-ding@ti.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 5454b7ac22c6..0c9b57b6ff55 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -727,6 +727,9 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; + /* Only HP Laptop support SPI-based TAS2781 */ + tas_hda->catlog_id = HP; + return ret; } From f2581ea2d9f30844c437e348a462027ea25c12e9 Mon Sep 17 00:00:00 2001 From: gongqi <550230171hxy@gmail.com> Date: Thu, 22 Jan 2026 23:55:01 +0800 Subject: [PATCH 245/341] ALSA: hda/conexant: Add headset mic fix for MECHREVO Wujie 15X Pro The headset microphone on the MECHREVO Wujie 15X Pro requires the CXT_FIXUP_HEADSET_MIC quirk to function properly. Add the PCI SSID (0x1d05:0x3012) to the quirk table. Signed-off-by: gongqi <550230171hxy@gmail.com> Link: https://patch.msgid.link/20260122155501.376199-5-550230171hxy@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 5fcbc1312c69..2384e64eada3 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1123,6 +1123,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), 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), HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER), {} From 7bb0dbf9fc403f65cff33c91ea266683e33b2f04 Mon Sep 17 00:00:00 2001 From: Jegor van Opdorp Date: Tue, 27 Jan 2026 09:15:41 +0100 Subject: [PATCH 246/341] ALSA: usb-audio: add mixer support for Focusrite Forte Add mixer control support for the Focusrite Forte (USB ID 0x1235:0x8010), an older USB audio interface that predates the Scarlett 2nd generation. The Forte uses UAC2_CS_MEM (bRequest=0x03) for its input controls rather than the standard UAC2_CS_CUR (0x01) used by Scarlett devices. This patch adds Forte-specific control handlers that use the correct USB protocol. Features implemented: - Input source selection (Mic/Line/Inst) for both channels - High pass filter switch - 48V phantom power switch - Phase invert switch - Pad switch - Preamp gain control (0-42 range, ~0-75dB) - Matrix mixer controls (6 inputs x 4 outputs) - Output volume and mute controls The device is registered via mixer_quirks.c and uses the existing mixer_scarlett.c infrastructure with Forte-specific additions. Credit: This work builds on prior reverse-engineering by alastair-dm. Link: https://github.com/alastair-dm/forte-mixer/wiki Link: https://github.com/alastair-dm/forte-mixer Link: https://github.com/jopdorp/forte-mixer Signed-off-by: Jegor van Opdorp Link: https://patch.msgid.link/20260127081541.219669-1-jegorvanopdorp@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 3 + sound/usb/mixer_scarlett.c | 476 +++++++++++++++++++++++++++++++++++-- sound/usb/mixer_scarlett.h | 1 + 3 files changed, 461 insertions(+), 19 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f8eccb1c58da..07d5cf0077d8 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4417,6 +4417,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_create_std_mono_table(mixer, ebox44_table); break; + case USB_ID(0x1235, 0x8010): /* Focusrite Forte */ + err = snd_forte_controls_create(mixer); + break; case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */ case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */ case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */ diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 8babfa3f7c45..fa11f25288b7 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -135,7 +135,7 @@ /* some gui mixers can't handle negative ctl values */ #define SND_SCARLETT_LEVEL_BIAS 128 #define SND_SCARLETT_MATRIX_IN_MAX 18 -#define SND_SCARLETT_CONTROLS_MAX 10 +#define SND_SCARLETT_CONTROLS_MAX 14 #define SND_SCARLETT_OFFSETS_MAX 5 enum { @@ -143,6 +143,12 @@ enum { SCARLETT_SWITCH_IMPEDANCE, SCARLETT_SWITCH_PAD, SCARLETT_SWITCH_GAIN, + FORTE_INPUT_SOURCE, /* mic/line/instrument selection */ + FORTE_INPUT_HPF, /* high pass filter */ + FORTE_INPUT_PHANTOM, /* 48V phantom power */ + FORTE_INPUT_PHASE, /* phase invert */ + FORTE_INPUT_PAD, /* pad */ + FORTE_INPUT_GAIN, /* preamp gain 0-42 (~0-75dB for mic) */ }; enum { @@ -172,6 +178,8 @@ struct scarlett_device_info { int input_len; int output_len; + bool has_output_source_routing; + struct scarlett_mixer_elem_enum_info opt_master; struct scarlett_mixer_elem_enum_info opt_matrix; @@ -229,6 +237,239 @@ static const struct scarlett_mixer_elem_enum_info opt_sync = { } }; +/* Forte-specific input control options */ +static const struct scarlett_mixer_elem_enum_info opt_forte_source = { + .start = 0, + .len = 3, + .offsets = {}, + .names = (char const * const []){ + "Mic", "Line", "Inst" + } +}; + +/* + * Forte-specific USB control functions + * Forte input controls use bRequest=0x03 (UAC2_CS_MEM) instead of 0x01 + * wValue = (control_code << 8) | channel + * wIndex = interface | (0x3c << 8) like Scarlett meter/matrix controls + */ +static int forte_set_ctl_value(struct usb_mixer_elem_info *elem, int value) +{ + struct snd_usb_audio *chip = elem->head.mixer->chip; + unsigned char buf[2]; + int wValue = elem->control; /* Just control code, NO shift, NO channel */ + int idx = snd_usb_ctrl_intf(elem->head.mixer->hostif) | (elem->head.id << 8); + int err; + + /* Wiki format: "2 bytes, chan 0,1 and value" + * Data order: [channel, value] - channel FIRST per wiki + */ + buf[0] = elem->idx_off; /* Channel: 0 or 1 */ + buf[1] = value & 0xff; /* Value: 0-2 for source, 0-1 for switches */ + + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return -EIO; + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), + UAC2_CS_MEM, /* bRequest = 0x03 */ + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + wValue, idx, buf, 2); + + snd_usb_unlock_shutdown(chip); + + if (err < 0) { + usb_audio_err(chip, "forte_set FAILED: req=0x03 wVal=0x%04x wIdx=0x%04x buf=[%02x,%02x] err=%d\n", + wValue, idx, buf[0], buf[1], err); + return err; + } + return 0; +} + +static int forte_get_ctl_value(struct usb_mixer_elem_info *elem, int *value) +{ + /* Device may not support reading input controls. + * Return cached value or default to avoid blocking module load. + */ + if (elem->cached) + *value = elem->cache_val[0]; + else + *value = 0; /* Default: first option */ + + return 0; +} + +/* + * Forte Input Gain control functions + * Gain range is 0-42 (0x00-0x2a) which maps to approximately: + * - Mic: 0 to +75dB (~1.8dB per step) + * - Instrument: +14 to +68dB + * - Line: -12 to +42dB + * We use a TLV scale of 0 to 7500 centidB (0 to 75dB) in ~179 cB steps + */ +#define FORTE_INPUT_GAIN_MAX 42 + +static int forte_input_gain_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = FORTE_INPUT_GAIN_MAX; + uinfo->value.integer.step = 1; + return 0; +} + +static int forte_input_gain_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int err, val; + + /* Use Forte-specific USB command with UAC2_CS_MEM */ + err = forte_get_ctl_value(elem, &val); + if (err < 0) + return err; + + ucontrol->value.integer.value[0] = clamp(val, 0, FORTE_INPUT_GAIN_MAX); + return 0; +} + +static int forte_input_gain_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int err, oval, val; + + /* Read current value */ + err = forte_get_ctl_value(elem, &oval); + if (err < 0) + return err; + + val = clamp((int)ucontrol->value.integer.value[0], 0, FORTE_INPUT_GAIN_MAX); + if (oval != val) { + /* Use Forte-specific USB command */ + err = forte_set_ctl_value(elem, val); + if (err < 0) + return err; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; + } + return 0; +} + +static int forte_input_gain_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); + + if (elem->cached) + forte_set_ctl_value(elem, *elem->cache_val); + return 0; +} + +/* + * Forte-specific enum control functions (for Source selection) + * Uses bRequest=0x03 (UAC2_CS_MEM) instead of standard 0x01 + */ +static int forte_ctl_enum_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, val; + + err = forte_get_ctl_value(elem, &val); + if (err < 0) + return err; + + val = clamp(val - opt->start, 0, opt->len - 1); + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int forte_ctl_enum_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, oval, val; + + err = forte_get_ctl_value(elem, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[0] + opt->start; + if (val != oval) { + err = forte_set_ctl_value(elem, val); + if (err < 0) + return err; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; + } + return 0; +} + +static int forte_ctl_enum_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); + + if (elem->cached) + forte_set_ctl_value(elem, *elem->cache_val); + return 0; +} + +/* + * Forte-specific switch control functions (for HPF, 48V, Phase, Pad) + * Uses bRequest=0x03 (UAC2_CS_MEM) instead of standard 0x01 + */ +static int forte_ctl_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int err, val; + + err = forte_get_ctl_value(elem, &val); + if (err < 0) + return err; + + ucontrol->value.integer.value[0] = val ? 1 : 0; + return 0; +} + +static int forte_ctl_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int err, oval, val; + + err = forte_get_ctl_value(elem, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[0] ? 1 : 0; + if (val != oval) { + err = forte_set_ctl_value(elem, val); + if (err < 0) + return err; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; + } + return 0; +} + +static int forte_ctl_switch_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); + + if (elem->cached) + forte_set_ctl_value(elem, *elem->cache_val); + return 0; +} + static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -534,6 +775,37 @@ static const struct snd_kcontrol_new usb_scarlett_ctl_sync = { .get = scarlett_ctl_meter_get, }; +/* Forte-specific control structures - use bRequest=0x03 instead of 0x01 */ +static const struct snd_kcontrol_new usb_forte_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_info, + .get = forte_ctl_enum_get, + .put = forte_ctl_enum_put, +}; + +static const struct snd_kcontrol_new usb_forte_ctl_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = forte_ctl_switch_get, + .put = forte_ctl_switch_put, +}; + +/* Forte input gain: 0-42 maps to approximately 0-75dB (~179 cB per step) */ +static const DECLARE_TLV_DB_SCALE(db_scale_forte_input_gain, 0, 179, 0); + +static const struct snd_kcontrol_new usb_forte_ctl_input_gain = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = forte_input_gain_info, + .get = forte_input_gain_get, + .put = forte_input_gain_put, + .tlv = { .p = db_scale_forte_input_gain } +}; + static int add_new_ctl(struct usb_mixer_interface *mixer, const struct snd_kcontrol_new *ncontrol, usb_mixer_elem_resume_func_t resume, @@ -608,37 +880,83 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, if (err < 0) return err; - /* Add L channel source playback enumeration */ - snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", - index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, - scarlett_ctl_enum_resume, 0x33, 0x00, - 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, - &elem); - if (err < 0) - return err; + /* Add L/R source routing if device supports it */ + if (info->has_output_source_routing) { + /* Add L channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, + 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; - /* Add R channel source playback enumeration */ - snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", - index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, - scarlett_ctl_enum_resume, 0x33, 0x00, - 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, - &elem); - if (err < 0) - return err; + /* Add R channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, + 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + } return 0; } /********************** device-specific config *************************/ +static const struct scarlett_device_info forte_info = { + .matrix_in = 6, + .matrix_out = 4, + .input_len = 2, + .output_len = 4, + .has_output_source_routing = false, + + .opt_master = { + .start = -1, + .len = 13, + .offsets = {0, 4, 6, 6, 6}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 7, + .offsets = {0, 4, 6, 6, 6}, + .names = NULL + }, + + .num_controls = 14, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Line Out" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + /* Input 1 controls */ + { .num = 1, .type = FORTE_INPUT_GAIN, .name = NULL}, + { .num = 1, .type = FORTE_INPUT_SOURCE, .name = NULL}, + { .num = 1, .type = FORTE_INPUT_HPF, .name = NULL}, + { .num = 1, .type = FORTE_INPUT_PHANTOM, .name = NULL}, + { .num = 1, .type = FORTE_INPUT_PHASE, .name = NULL}, + { .num = 1, .type = FORTE_INPUT_PAD, .name = NULL}, + /* Input 2 controls */ + { .num = 2, .type = FORTE_INPUT_GAIN, .name = NULL}, + { .num = 2, .type = FORTE_INPUT_SOURCE, .name = NULL}, + { .num = 2, .type = FORTE_INPUT_HPF, .name = NULL}, + { .num = 2, .type = FORTE_INPUT_PHANTOM, .name = NULL}, + { .num = 2, .type = FORTE_INPUT_PHASE, .name = NULL}, + { .num = 2, .type = FORTE_INPUT_PAD, .name = NULL}, + }, +}; + /* untested... */ static const struct scarlett_device_info s6i6_info = { .matrix_in = 18, .matrix_out = 8, .input_len = 6, .output_len = 6, + .has_output_source_routing = true, .opt_master = { .start = -1, @@ -681,6 +999,7 @@ static const struct scarlett_device_info s8i6_info = { .matrix_out = 6, .input_len = 8, .output_len = 6, + .has_output_source_routing = true, .opt_master = { .start = -1, @@ -720,6 +1039,7 @@ static const struct scarlett_device_info s18i6_info = { .matrix_out = 6, .input_len = 18, .output_len = 6, + .has_output_source_routing = true, .opt_master = { .start = -1, @@ -757,6 +1077,7 @@ static const struct scarlett_device_info s18i8_info = { .matrix_out = 8, .input_len = 18, .output_len = 8, + .has_output_source_routing = true, .opt_master = { .start = -1, @@ -799,6 +1120,7 @@ static const struct scarlett_device_info s18i20_info = { .matrix_out = 8, .input_len = 18, .output_len = 20, + .has_output_source_routing = true, .opt_master = { .start = -1, @@ -906,6 +1228,61 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, if (err < 0) return err; break; + /* Forte input controls - use wIndex 0x3c per wiki docs */ + case FORTE_INPUT_GAIN: + sprintf(mx, "Line In %d Gain Capture Volume", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_input_gain, + forte_input_gain_resume, 0x3c, + 0x06, ctl->num - 1, USB_MIXER_S16, 1, mx, + NULL, &elem); + if (err < 0) + return err; + break; + case FORTE_INPUT_SOURCE: + sprintf(mx, "Input %d Source", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_enum, + forte_ctl_enum_resume, 0x3c, + 0x07, ctl->num - 1, USB_MIXER_S16, 1, mx, + &opt_forte_source, &elem); + if (err < 0) + return err; + break; + case FORTE_INPUT_HPF: + sprintf(mx, "Input %d High Pass Filter Switch", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_switch, + forte_ctl_switch_resume, 0x3c, + 0x08, ctl->num - 1, USB_MIXER_S16, 1, mx, + NULL, &elem); + if (err < 0) + return err; + break; + case FORTE_INPUT_PHANTOM: + sprintf(mx, "Input %d 48V Phantom Power Switch", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_switch, + forte_ctl_switch_resume, 0x3c, + 0x09, ctl->num - 1, USB_MIXER_S16, 1, mx, + NULL, &elem); + if (err < 0) + return err; + break; + case FORTE_INPUT_PHASE: + sprintf(mx, "Input %d Phase Invert Switch", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_switch, + forte_ctl_switch_resume, 0x3c, + 0x0a, ctl->num - 1, USB_MIXER_S16, 1, mx, + NULL, &elem); + if (err < 0) + return err; + break; + case FORTE_INPUT_PAD: + sprintf(mx, "Input %d Pad Switch", ctl->num); + err = add_new_ctl(mixer, &usb_forte_ctl_switch, + forte_ctl_switch_resume, 0x3c, + 0x0b, ctl->num - 1, USB_MIXER_S16, 1, mx, + NULL, &elem); + if (err < 0) + return err; + break; } } @@ -1014,3 +1391,64 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) return err; } + +/* + * Create and initialize a mixer for the Focusrite(R) Forte + */ +int snd_forte_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const struct scarlett_device_info *info; + struct usb_mixer_elem_info *elem; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + switch (mixer->chip->usb_id) { + case USB_ID(0x1235, 0x8010): + info = &forte_info; + break; + default: /* device not (yet) supported */ + return -EINVAL; + } + + /* generic function to create controls */ + err = scarlett_controls_create_generic(mixer, info); + if (err < 0) + return err; + + /* setup matrix controls */ + for (i = 0; i < info->matrix_in; i++) { + snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", + i+1); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x32, + 0x06, i, USB_MIXER_S16, 1, mx, + &info->opt_matrix, &elem); + if (err < 0) + return err; + + for (o = 0; o < info->matrix_out; o++) { + sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, + o+'A'); + err = add_new_ctl(mixer, &usb_scarlett_ctl, + scarlett_ctl_resume, 0x3c, 0x01, + (i << 2) + (o & 0x03), USB_MIXER_S16, + 1, mx, NULL, &elem); + if (err < 0) + return err; + + } + } + + /* val_len == 1 and UAC2_CS_MEM */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2, + USB_MIXER_U8, 1, "Sample Clock Sync Status", + &opt_sync, &elem); + if (err < 0) + return err; + + return err; +} diff --git a/sound/usb/mixer_scarlett.h b/sound/usb/mixer_scarlett.h index bbf063b79370..c44fe7d72ee3 100644 --- a/sound/usb/mixer_scarlett.h +++ b/sound/usb/mixer_scarlett.h @@ -3,5 +3,6 @@ #define __USB_MIXER_SCARLETT_H int snd_scarlett_controls_create(struct usb_mixer_interface *mixer); +int snd_forte_controls_create(struct usb_mixer_interface *mixer); #endif /* __USB_MIXER_SCARLETT_H */ From d7e1f9e84af460c5f1e5352eda8f036000cfcf0a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 22 Jan 2026 20:44:57 +0800 Subject: [PATCH 247/341] ASoC: codec: Remove ak4641 Since commit d6df7df7ae5a0 ("ARM: pxa: remove unused board files"), there has been no in-tree user of the AK4641 codec driver. The last user (HP iPAQ hx4700) was a non-DT PXA board file that instantiated the device via I2C board data; that code was removed as part of the PXA board-file purge. The AK4641 driver was introduced ~2011 and still probes only via the I2C device-ID table ('.id_table'), without an 'of_match_table', so there are no upstream Devicetree users to retain. With no in-tree users left, remove the driver. Signed-off-by: Peng Fan Reviewed-by: Bartosz Golaszewski Acked-by: Andy Shevchenko Link: https://patch.msgid.link/20260122-sound-cleanup-v1-1-0a91901609b8@nxp.com Signed-off-by: Mark Brown --- include/sound/ak4641.h | 23 -- sound/soc/codecs/Kconfig | 6 - sound/soc/codecs/Makefile | 2 - sound/soc/codecs/ak4641.c | 641 -------------------------------------- 4 files changed, 672 deletions(-) delete mode 100644 include/sound/ak4641.h delete mode 100644 sound/soc/codecs/ak4641.c diff --git a/include/sound/ak4641.h b/include/sound/ak4641.h deleted file mode 100644 index 8b1941bbde52..000000000000 --- a/include/sound/ak4641.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * AK4641 ALSA SoC Codec driver - * - * Copyright 2009 Philipp Zabel - */ - -#ifndef __AK4641_H -#define __AK4641_H - -/** - * struct ak4641_platform_data - platform specific AK4641 configuration - * @gpio_power: GPIO to control external power to AK4641 - * @gpio_npdn: GPIO connected to AK4641 nPDN pin - * - * Both GPIO parameters are optional. - */ -struct ak4641_platform_data { - int gpio_power; - int gpio_npdn; -}; - -#endif /* __AK4641_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061791e61907..36eb872dd8fe 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -46,7 +46,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK4554 imply SND_SOC_AK4613 imply SND_SOC_AK4619 - imply SND_SOC_AK4641 imply SND_SOC_AK4642 imply SND_SOC_AK4671 imply SND_SOC_AK5386 @@ -624,11 +623,6 @@ config SND_SOC_AK4619 tristate "AKM AK4619 CODEC" depends on I2C -config SND_SOC_AK4641 - tristate - depends on I2C - depends on GPIOLIB_LEGACY - config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d687d4f74363..013a5fc65266 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -40,7 +40,6 @@ snd-soc-ak4535-y := ak4535.o snd-soc-ak4554-y := ak4554.o snd-soc-ak4613-y := ak4613.o snd-soc-ak4619-y := ak4619.o -snd-soc-ak4641-y := ak4641.o snd-soc-ak4642-y := ak4642.o snd-soc-ak4671-y := ak4671.o snd-soc-ak5386-y := ak5386.o @@ -472,7 +471,6 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o -obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c deleted file mode 100644 index 9db8cdb26d33..000000000000 --- a/sound/soc/codecs/ak4641.c +++ /dev/null @@ -1,641 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ak4641.c -- AK4641 ALSA Soc Audio driver - * - * Copyright (C) 2008 Harald Welte - * Copyright (C) 2011 Dmitry Artamonow - * - * Based on ak4535.c by Richard Purdie - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AK4641 register space */ -#define AK4641_PM1 0x00 -#define AK4641_PM2 0x01 -#define AK4641_SIG1 0x02 -#define AK4641_SIG2 0x03 -#define AK4641_MODE1 0x04 -#define AK4641_MODE2 0x05 -#define AK4641_DAC 0x06 -#define AK4641_MIC 0x07 -#define AK4641_TIMER 0x08 -#define AK4641_ALC1 0x09 -#define AK4641_ALC2 0x0a -#define AK4641_PGA 0x0b -#define AK4641_LATT 0x0c -#define AK4641_RATT 0x0d -#define AK4641_VOL 0x0e -#define AK4641_STATUS 0x0f -#define AK4641_EQLO 0x10 -#define AK4641_EQMID 0x11 -#define AK4641_EQHI 0x12 -#define AK4641_BTIF 0x13 - -/* codec private data */ -struct ak4641_priv { - struct regmap *regmap; - unsigned int sysclk; - int deemph; - int playback_fs; -}; - -/* - * ak4641 register cache - */ -static const struct reg_default ak4641_reg_defaults[] = { - { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 }, - { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 }, - { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 }, - { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 }, - { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 } -}; - -static const int deemph_settings[] = {44100, 0, 48000, 32000}; - -static int ak4641_set_deemph(struct snd_soc_component *component) -{ - struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - int i, best = 0; - - for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) { - /* if deemphasis is on, select the nearest available rate */ - if (ak4641->deemph && deemph_settings[i] != 0 && - abs(deemph_settings[i] - ak4641->playback_fs) < - abs(deemph_settings[best] - ak4641->playback_fs)) - best = i; - - if (!ak4641->deemph && deemph_settings[i] == 0) - best = i; - } - - dev_dbg(component->dev, "Set deemphasis %d\n", best); - - return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best); -} - -static int ak4641_put_deemph(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - int deemph = ucontrol->value.integer.value[0]; - - if (deemph > 1) - return -EINVAL; - - ak4641->deemph = deemph; - - return ak4641_set_deemph(component); -} - -static int ak4641_get_deemph(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - - ucontrol->value.integer.value[0] = ak4641->deemph; - return 0; -}; - -static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"}; -static const char *ak4641_hp_out[] = {"Stereo", "Mono"}; -static const char *ak4641_mic_select[] = {"Internal", "External"}; -static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"}; - - -static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0); -static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0); -static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0); -static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0); -static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0); -static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0); -static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0); -static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0); -static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0); - - -static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum, - AK4641_SIG1, 6, ak4641_mono_out); -static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum, - AK4641_MODE2, 2, ak4641_hp_out); -static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum, - AK4641_MIC, 1, ak4641_mic_select); -static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum, - AK4641_BTIF, 4, ak4641_mic_or_dac); - -static const struct snd_kcontrol_new ak4641_snd_controls[] = { - SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum), - SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1, - mono_gain_tlv), - SOC_ENUM("Headphone Output", ak4641_hp_out_enum), - SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, - ak4641_get_deemph, ak4641_put_deemph), - - SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv), - - SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0), - SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0), - SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0), - - SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0), - - SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv), - SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0), - SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0), - - SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv), - - SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT, - AK4641_RATT, 0, 255, 1, master_tlv), - - SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv), - - SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0), - SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv), - SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv), - SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv), - SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv), - SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv), -}; - -/* Mono 1 Mixer */ -static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = { - SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0, - mic_mono_sidetone_tlv), - SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0), - SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0), -}; - -/* Stereo Mixer */ -static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = { - SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0, - mic_stereo_sidetone_tlv), - SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0), - SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0), - SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0), -}; - -/* Input Mixer */ -static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = { - SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0), - SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0), -}; - -/* Mic mux */ -static const struct snd_kcontrol_new ak4641_mic_mux_control = - SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum); - -/* Input mux */ -static const struct snd_kcontrol_new ak4641_input_mux_control = - SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum); - -/* mono 2 switch */ -static const struct snd_kcontrol_new ak4641_mono2_control = - SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0); - -/* ak4641 dapm widgets */ -static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = { - SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0, - &ak4641_stereo_mixer_controls[0], - ARRAY_SIZE(ak4641_stereo_mixer_controls)), - SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0, - &ak4641_mono1_mixer_controls[0], - ARRAY_SIZE(ak4641_mono1_mixer_controls)), - SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, - &ak4641_input_mixer_controls[0], - ARRAY_SIZE(ak4641_input_mixer_controls)), - SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0, - &ak4641_mic_mux_control), - SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, - &ak4641_input_mux_control), - SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0, - &ak4641_mono2_control), - - SND_SOC_DAPM_OUTPUT("LOUT"), - SND_SOC_DAPM_OUTPUT("ROUT"), - SND_SOC_DAPM_OUTPUT("MOUT1"), - SND_SOC_DAPM_OUTPUT("MOUT2"), - SND_SOC_DAPM_OUTPUT("MICOUT"), - - SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0), - SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0), - - SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0), - SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0), - - SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0), - SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0), - - SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0), - SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0), - - SND_SOC_DAPM_INPUT("MICIN"), - SND_SOC_DAPM_INPUT("MICEXT"), - SND_SOC_DAPM_INPUT("AUX"), - SND_SOC_DAPM_INPUT("AIN"), -}; - -static const struct snd_soc_dapm_route ak4641_audio_map[] = { - /* Stereo Mixer */ - {"Stereo Mixer", "Playback Switch", "DAC"}, - {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"}, - {"Stereo Mixer", "Aux Bypass Switch", "AUX In"}, - - /* Mono 1 Mixer */ - {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"}, - {"Mono1 Mixer", "Mono Playback Switch", "DAC"}, - - /* Mic */ - {"Mic", NULL, "AIN"}, - {"Mic Mux", "Internal", "Mic Int Bias"}, - {"Mic Mux", "External", "Mic Ext Bias"}, - {"Mic Int Bias", NULL, "MICIN"}, - {"Mic Ext Bias", NULL, "MICEXT"}, - {"MICOUT", NULL, "Mic Mux"}, - - /* Input Mux */ - {"Input Mux", "Microphone", "Mic"}, - {"Input Mux", "Voice DAC", "Voice DAC"}, - - /* Line Out */ - {"LOUT", NULL, "Line Out"}, - {"ROUT", NULL, "Line Out"}, - {"Line Out", NULL, "Stereo Mixer"}, - - /* Mono 1 Out */ - {"MOUT1", NULL, "Mono Out"}, - {"Mono Out", NULL, "Mono1 Mixer"}, - - /* Mono 2 Out */ - {"MOUT2", NULL, "Mono 2 Enable"}, - {"Mono 2 Enable", "Switch", "Mono Out 2"}, - {"Mono Out 2", NULL, "Stereo Mixer"}, - - {"Voice ADC", NULL, "Mono 2 Enable"}, - - /* Aux In */ - {"AUX In", NULL, "AUX"}, - - /* ADC */ - {"ADC", NULL, "Input Mixer"}, - {"Input Mixer", "Mic Capture Switch", "Mic"}, - {"Input Mixer", "Aux Capture Switch", "AUX In"}, -}; - -static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_component *component = codec_dai->component; - struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - - ak4641->sysclk = freq; - return 0; -} - -static int ak4641_i2s_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 ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - int rate = params_rate(params), fs = 256; - u8 mode2; - - if (rate) - fs = ak4641->sysclk / rate; - else - return -EINVAL; - - /* set fs */ - switch (fs) { - case 1024: - mode2 = (0x2 << 5); - break; - case 512: - mode2 = (0x1 << 5); - break; - case 256: - mode2 = (0x0 << 5); - break; - default: - dev_err(component->dev, "Error: unsupported fs=%d\n", fs); - return -EINVAL; - } - - snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2); - - /* Update de-emphasis filter for the new rate */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ak4641->playback_fs = rate; - ak4641_set_deemph(component); - } - - return 0; -} - -static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_component *component = codec_dai->component; - u8 btif; - int ret; - - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - btif = (0x3 << 5); - break; - case SND_SOC_DAIFMT_LEFT_J: - btif = (0x2 << 5); - break; - case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */ - btif = (0x0 << 5); - break; - case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */ - btif = (0x1 << 5); - break; - default: - return -EINVAL; - } - - ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif); - if (ret < 0) - return ret; - - return 0; -} - -static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_component *component = codec_dai->component; - u8 mode1 = 0; - - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - mode1 = 0x02; - break; - case SND_SOC_DAIFMT_LEFT_J: - mode1 = 0x01; - break; - default: - return -EINVAL; - } - - return snd_soc_component_write(component, AK4641_MODE1, mode1); -} - -static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction) -{ - struct snd_soc_component *component = dai->component; - - return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0); -} - -static int ak4641_set_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); - struct ak4641_platform_data *pdata = component->dev->platform_data; - int ret; - - switch (level) { - case SND_SOC_BIAS_ON: - /* unmute */ - snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0); - break; - case SND_SOC_BIAS_PREPARE: - /* mute */ - snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20); - break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - if (pdata && gpio_is_valid(pdata->gpio_power)) - gpio_set_value(pdata->gpio_power, 1); - mdelay(1); - if (pdata && gpio_is_valid(pdata->gpio_npdn)) - gpio_set_value(pdata->gpio_npdn, 1); - mdelay(1); - - ret = regcache_sync(ak4641->regmap); - if (ret) { - dev_err(component->dev, - "Failed to sync cache: %d\n", ret); - return ret; - } - } - snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80); - snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0); - break; - case SND_SOC_BIAS_OFF: - snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0); - if (pdata && gpio_is_valid(pdata->gpio_npdn)) - gpio_set_value(pdata->gpio_npdn, 0); - if (pdata && gpio_is_valid(pdata->gpio_power)) - gpio_set_value(pdata->gpio_power, 0); - regcache_mark_dirty(ak4641->regmap); - break; - } - return 0; -} - -#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000) -#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_16000) -#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) - -static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = { - .hw_params = ak4641_i2s_hw_params, - .set_fmt = ak4641_i2s_set_dai_fmt, - .mute_stream = ak4641_mute, - .set_sysclk = ak4641_set_dai_sysclk, - .no_capture_mute = 1, -}; - -static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = { - .hw_params = NULL, /* rates are controlled by BT chip */ - .set_fmt = ak4641_pcm_set_dai_fmt, - .mute_stream = ak4641_mute, - .set_sysclk = ak4641_set_dai_sysclk, - .no_capture_mute = 1, -}; - -static struct snd_soc_dai_driver ak4641_dai[] = { -{ - .name = "ak4641-hifi", - .id = 1, - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = AK4641_RATES, - .formats = AK4641_FORMATS, - }, - .capture = { - .stream_name = "HiFi Capture", - .channels_min = 1, - .channels_max = 2, - .rates = AK4641_RATES, - .formats = AK4641_FORMATS, - }, - .ops = &ak4641_i2s_dai_ops, - .symmetric_rate = 1, -}, -{ - .name = "ak4641-voice", - .id = 1, - .playback = { - .stream_name = "Voice Playback", - .channels_min = 1, - .channels_max = 1, - .rates = AK4641_RATES_BT, - .formats = AK4641_FORMATS, - }, - .capture = { - .stream_name = "Voice Capture", - .channels_min = 1, - .channels_max = 1, - .rates = AK4641_RATES_BT, - .formats = AK4641_FORMATS, - }, - .ops = &ak4641_pcm_dai_ops, - .symmetric_rate = 1, -}, -}; - -static const struct snd_soc_component_driver soc_component_dev_ak4641 = { - .controls = ak4641_snd_controls, - .num_controls = ARRAY_SIZE(ak4641_snd_controls), - .dapm_widgets = ak4641_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets), - .dapm_routes = ak4641_audio_map, - .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), - .set_bias_level = ak4641_set_bias_level, - .suspend_bias_off = 1, - .idle_bias_on = 1, - .use_pmdown_time = 1, - .endianness = 1, -}; - -static const struct regmap_config ak4641_regmap = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = AK4641_BTIF, - .reg_defaults = ak4641_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults), - .cache_type = REGCACHE_RBTREE, -}; - -static int ak4641_i2c_probe(struct i2c_client *i2c) -{ - struct ak4641_platform_data *pdata = i2c->dev.platform_data; - struct ak4641_priv *ak4641; - int ret; - - ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv), - GFP_KERNEL); - if (!ak4641) - return -ENOMEM; - - ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap); - if (IS_ERR(ak4641->regmap)) - return PTR_ERR(ak4641->regmap); - - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) { - ret = gpio_request_one(pdata->gpio_power, - GPIOF_OUT_INIT_LOW, "ak4641 power"); - if (ret) - goto err_out; - } - if (gpio_is_valid(pdata->gpio_npdn)) { - ret = gpio_request_one(pdata->gpio_npdn, - GPIOF_OUT_INIT_LOW, "ak4641 npdn"); - if (ret) - goto err_gpio; - - udelay(1); /* > 150 ns */ - gpio_set_value(pdata->gpio_npdn, 1); - } - } - - i2c_set_clientdata(i2c, ak4641); - - ret = devm_snd_soc_register_component(&i2c->dev, - &soc_component_dev_ak4641, - ak4641_dai, ARRAY_SIZE(ak4641_dai)); - if (ret != 0) - goto err_gpio2; - - return 0; - -err_gpio2: - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) - gpio_set_value(pdata->gpio_power, 0); - if (gpio_is_valid(pdata->gpio_npdn)) - gpio_free(pdata->gpio_npdn); - } -err_gpio: - if (pdata && gpio_is_valid(pdata->gpio_power)) - gpio_free(pdata->gpio_power); -err_out: - return ret; -} - -static void ak4641_i2c_remove(struct i2c_client *i2c) -{ - struct ak4641_platform_data *pdata = i2c->dev.platform_data; - - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) { - gpio_set_value(pdata->gpio_power, 0); - gpio_free(pdata->gpio_power); - } - if (gpio_is_valid(pdata->gpio_npdn)) - gpio_free(pdata->gpio_npdn); - } -} - -static const struct i2c_device_id ak4641_i2c_id[] = { - { "ak4641" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id); - -static struct i2c_driver ak4641_i2c_driver = { - .driver = { - .name = "ak4641", - }, - .probe = ak4641_i2c_probe, - .remove = ak4641_i2c_remove, - .id_table = ak4641_i2c_id, -}; - -module_i2c_driver(ak4641_i2c_driver); - -MODULE_DESCRIPTION("SoC AK4641 driver"); -MODULE_AUTHOR("Harald Welte "); -MODULE_LICENSE("GPL"); From b094de7810f3c0340a3aebaf6b8fc60c81c0cf91 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 22 Jan 2026 20:44:58 +0800 Subject: [PATCH 248/341] ASoC: codec: Remove pxa2xx-ac97.c With commit ce79f3a1ad5f ("ARM: pxa: prune unused device support") and commit 2548e6c76ebf ("ARM: pxa: pxa2xx-ac97-lib: use IRQ resource"), there is no 'pxa2xx-ac97' platform device created by machine level code, so this driver could be removed. Signed-off-by: Peng Fan Reviewed-by: Bartosz Golaszewski Acked-by: Andy Shevchenko Link: https://patch.msgid.link/20260122-sound-cleanup-v1-2-0a91901609b8@nxp.com Signed-off-by: Mark Brown --- sound/arm/Kconfig | 10 -- sound/arm/Makefile | 3 - sound/arm/pxa2xx-ac97.c | 286 ---------------------------------------- 3 files changed, 299 deletions(-) delete mode 100644 sound/arm/pxa2xx-ac97.c diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index dea2c661b353..e4d7288d1e1e 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -18,16 +18,6 @@ config SND_ARMAACI select SND_PCM select SND_AC97_CODEC -config SND_PXA2XX_AC97 - tristate "AC97 driver for the Intel PXA2xx chip" - depends on ARCH_PXA - select SND_AC97_CODEC - select SND_PXA2XX_LIB - select SND_PXA2XX_LIB_AC97 - help - Say Y or M if you want to support any AC97 codec attached to - the PXA2xx AC97 interface. - endif # SND_ARM config SND_PXA2XX_LIB diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 899edb4bb278..99325a66cf77 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -9,6 +9,3 @@ snd-aaci-y := aaci.o obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o - -obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o -snd-pxa2xx-ac97-y := pxa2xx-ac97.o diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c deleted file mode 100644 index 77b11616a7ee..000000000000 --- a/sound/arm/pxa2xx-ac97.c +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. - * - * Author: Nicolas Pitre - * Created: Dec 02, 2004 - * Copyright: MontaVista Software Inc. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97) -{ - if (!pxa2xx_ac97_try_cold_reset()) - pxa2xx_ac97_try_warm_reset(); - - pxa2xx_ac97_finish_reset(); -} - -static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, - unsigned short reg) -{ - int ret; - - ret = pxa2xx_ac97_read(ac97->num, reg); - if (ret < 0) - return 0; - else - return (unsigned short)(ret & 0xffff); -} - -static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, - unsigned short reg, unsigned short val) -{ - pxa2xx_ac97_write(ac97->num, reg, val); -} - -static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_legacy_read, - .write = pxa2xx_ac97_legacy_write, - .reset = pxa2xx_ac97_legacy_reset, -}; - -static struct snd_pcm *pxa2xx_ac97_pcm; -static struct snd_ac97 *pxa2xx_ac97_ac97; - -static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - pxa2xx_audio_ops_t *platform_ops; - int ret, i; - - ret = pxa2xx_pcm_open(substream); - if (ret) - return ret; - - runtime->hw.channels_min = 2; - runtime->hw.channels_max = 2; - - i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - AC97_RATES_FRONT_DAC : AC97_RATES_ADC; - runtime->hw.rates = pxa2xx_ac97_ac97->rates[i]; - snd_pcm_limit_hw_rates(runtime); - - platform_ops = substream->pcm->card->dev->platform_data; - if (platform_ops && platform_ops->startup) { - ret = platform_ops->startup(substream, platform_ops->priv); - if (ret < 0) - pxa2xx_pcm_close(substream); - } - - return ret; -} - -static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream) -{ - pxa2xx_audio_ops_t *platform_ops; - - platform_ops = substream->pcm->card->dev->platform_data; - if (platform_ops && platform_ops->shutdown) - platform_ops->shutdown(substream, platform_ops->priv); - - return 0; -} - -static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; - int ret; - - ret = pxa2xx_pcm_prepare(substream); - if (ret < 0) - return ret; - - return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); -} - -static int pxa2xx_ac97_do_suspend(struct snd_card *card) -{ - pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; - - snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); - snd_ac97_suspend(pxa2xx_ac97_ac97); - if (platform_ops && platform_ops->suspend) - platform_ops->suspend(platform_ops->priv); - - return pxa2xx_ac97_hw_suspend(); -} - -static int pxa2xx_ac97_do_resume(struct snd_card *card) -{ - pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; - int rc; - - rc = pxa2xx_ac97_hw_resume(); - if (rc) - return rc; - - if (platform_ops && platform_ops->resume) - platform_ops->resume(platform_ops->priv); - snd_ac97_resume(pxa2xx_ac97_ac97); - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - - return 0; -} - -static int pxa2xx_ac97_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int ret = 0; - - if (card) - ret = pxa2xx_ac97_do_suspend(card); - - return ret; -} - -static int pxa2xx_ac97_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int ret = 0; - - if (card) - ret = pxa2xx_ac97_do_resume(card); - - return ret; -} - -static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); - -static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = { - .open = pxa2xx_ac97_pcm_open, - .close = pxa2xx_ac97_pcm_close, - .hw_params = pxa2xx_pcm_hw_params, - .prepare = pxa2xx_ac97_pcm_prepare, - .trigger = pxa2xx_pcm_trigger, - .pointer = pxa2xx_pcm_pointer, -}; - - -static int pxa2xx_ac97_pcm_new(struct snd_card *card) -{ - struct snd_pcm *pcm; - int ret; - - ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm); - if (ret) - goto out; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - goto out; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops); - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm); - if (ret) - goto out; - - pxa2xx_ac97_pcm = pcm; - ret = 0; - - out: - return ret; -} - -static int pxa2xx_ac97_probe(struct platform_device *dev) -{ - struct snd_card *card; - struct snd_ac97_bus *ac97_bus; - struct snd_ac97_template ac97_template; - int ret; - pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; - - if (dev->id >= 0) { - dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n"); - ret = -ENXIO; - goto err_dev; - } - - ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (ret < 0) - goto err; - - strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); - - ret = pxa2xx_ac97_pcm_new(card); - if (ret) - goto err; - - ret = pxa2xx_ac97_hw_probe(dev); - if (ret) - goto err; - - ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus); - if (ret) - goto err_remove; - memset(&ac97_template, 0, sizeof(ac97_template)); - ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97); - if (ret) - goto err_remove; - - snprintf(card->shortname, sizeof(card->shortname), - "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97)); - snprintf(card->longname, sizeof(card->longname), - "%s (%s)", dev->dev.driver->name, card->mixername); - - if (pdata && pdata->codec_pdata[0]) - snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]); - ret = snd_card_register(card); - if (ret == 0) { - platform_set_drvdata(dev, card); - return 0; - } - -err_remove: - pxa2xx_ac97_hw_remove(dev); -err: - if (card) - snd_card_free(card); -err_dev: - return ret; -} - -static void pxa2xx_ac97_remove(struct platform_device *dev) -{ - struct snd_card *card = platform_get_drvdata(dev); - - if (card) { - snd_card_free(card); - pxa2xx_ac97_hw_remove(dev); - } -} - -static struct platform_driver pxa2xx_ac97_driver = { - .probe = pxa2xx_ac97_probe, - .remove = pxa2xx_ac97_remove, - .driver = { - .name = "pxa2xx-ac97", - .pm = &pxa2xx_ac97_pm_ops, - }, -}; - -module_platform_driver(pxa2xx_ac97_driver); - -MODULE_AUTHOR("Nicolas Pitre"); -MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa2xx-ac97"); From be829277ef79aa626e952e17030d711bdb6c1eef Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 22 Jan 2026 20:44:59 +0800 Subject: [PATCH 249/341] ASoC: pxa2xx-ac97: Remove platform_data Since commit d6df7df7ae5a0 ("ARM: pxa: remove unused board files"), there has been no in-tree user to create the device with platform data. So remove them. Signed-off-by: Peng Fan Acked-by: Andy Shevchenko Link: https://patch.msgid.link/20260122-sound-cleanup-v1-3-0a91901609b8@nxp.com Signed-off-by: Mark Brown --- sound/arm/pxa2xx-ac97-lib.c | 33 ++++++--------------------------- sound/soc/pxa/pxa2xx-ac97.c | 5 +---- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 64510318091f..0a28e44118c5 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -321,7 +321,6 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) { int ret; int irq; - pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; ac97_reg_base = devm_platform_ioremap_resource(dev, 0); if (IS_ERR(ac97_reg_base)) { @@ -329,32 +328,12 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) return PTR_ERR(ac97_reg_base); } - if (pdata) { - switch (pdata->reset_gpio) { - case 95: - case 113: - reset_gpio = pdata->reset_gpio; - break; - case 0: - reset_gpio = 113; - break; - case -1: - break; - default: - dev_err(&dev->dev, "Invalid reset GPIO %d\n", - pdata->reset_gpio); - } - } else if (!pdata && dev->dev.of_node) { - pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node, - "reset-gpios", 0); - if (pdata->reset_gpio == -ENOENT) - pdata->reset_gpio = -1; - else if (pdata->reset_gpio < 0) - return pdata->reset_gpio; - reset_gpio = pdata->reset_gpio; + if (dev->dev.of_node) { + reset_gpio = of_get_named_gpio(dev->dev.of_node, "reset-gpios", 0); + if (reset_gpio == -ENOENT) + reset_gpio = -1; + else if (reset_gpio < 0) + return reset_gpio; } else { if (cpu_is_pxa27x()) reset_gpio = 113; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 78f50032afc5..109a4958d9c0 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -222,9 +222,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { int ret; struct ac97_controller *ctrl; - pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data; struct resource *regs; - void **codecs_pdata; if (pdev->id != -1) { dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); @@ -247,10 +245,9 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) return ret; } - codecs_pdata = pdata ? pdata->codec_pdata : NULL; ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev, AC97_SLOTS_AVAILABLE_ALL, - codecs_pdata); + NULL); if (IS_ERR(ctrl)) return PTR_ERR(ctrl); From c76d50b71e898694c946993eb3c77f50efa97254 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 22 Jan 2026 20:45:00 +0800 Subject: [PATCH 250/341] ASoC: ac97: Convert to GPIO descriptors of_gpio.h is deprecated, update the driver to use GPIO descriptors. - Use devm_gpiod_get to get GPIO descriptor, and set consumer name. Since the driver still pass the reset_gpio to pxa27x_configure_ac97reset, so use desc_to_gpio() to get it gpio id. Signed-off-by: Peng Fan Link: https://patch.msgid.link/20260122-sound-cleanup-v1-4-0a91901609b8@nxp.com Signed-off-by: Mark Brown --- sound/arm/pxa2xx-ac97-lib.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 0a28e44118c5..1e114dbcf93c 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -13,10 +13,9 @@ #include #include #include +#include #include #include -#include -#include #include #include @@ -31,6 +30,7 @@ static volatile long gsr_bits; static struct clk *ac97_clk; static struct clk *ac97conf_clk; static int reset_gpio; +struct gpio_desc *rst_gpio; static void __iomem *ac97_reg_base; /* @@ -329,11 +329,14 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) } if (dev->dev.of_node) { - reset_gpio = of_get_named_gpio(dev->dev.of_node, "reset-gpios", 0); - if (reset_gpio == -ENOENT) + /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ + rst_gpio = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH); + ret = PTR_ERR(rst_gpio); + if (ret == -ENOENT) reset_gpio = -1; - else if (reset_gpio < 0) - return reset_gpio; + else if (ret) + return ret; + reset_gpio = desc_to_gpio(rst_gpio); } else { if (cpu_is_pxa27x()) reset_gpio = 113; @@ -346,13 +349,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) * here so that it is an output driven high when switching from * AC97_nRESET alt function to generic gpio. */ - ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH, - "pxa27x ac97 reset"); - if (ret < 0) { - pr_err("%s: gpio_request_one() failed: %d\n", - __func__, ret); - goto err_conf; - } + gpiod_set_consumer_name(rst_gpio, "pxa27x ac97 reset"); pxa27x_configure_ac97reset(reset_gpio, false); ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); @@ -403,8 +400,6 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); void pxa2xx_ac97_hw_remove(struct platform_device *dev) { - if (cpu_is_pxa27x()) - gpio_free(reset_gpio); writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); free_irq(platform_get_irq(dev, 0), NULL); if (ac97conf_clk) { From 8ef73c0fbd1e15ead401a74e7114d8d4614a74cf Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 27 Jan 2026 11:40:25 +0800 Subject: [PATCH 251/341] ASoC: sun4i-spdif: Add missing check for devm_regmap_init_mmio Add check for the return value of devm_regmap_init_mmio() and return the error if it fails in order to catch the error. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20260127034025.2044669-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-spdif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 1e755a716c63..65de03ca3ad2 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -684,6 +684,10 @@ 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); + } /* Clocks */ host->apb_clk = devm_clk_get(&pdev->dev, "apb"); From 74823db9ba2e13f3ec007b354759b3d8125e462c Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 27 Jan 2026 11:32:50 +0800 Subject: [PATCH 252/341] ASoC: sunxi: sun50i-dmic: Add missing check for devm_regmap_init_mmio Add check for the return value of devm_regmap_init_mmio() and return the error if it fails in order to catch the error. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20260127033250.2044608-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-dmic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c index bab1e29c9988..eddfebe16616 100644 --- a/sound/soc/sunxi/sun50i-dmic.c +++ b/sound/soc/sunxi/sun50i-dmic.c @@ -358,6 +358,9 @@ static int sun50i_dmic_probe(struct platform_device *pdev) host->regmap = devm_regmap_init_mmio(&pdev->dev, base, &sun50i_dmic_regmap_config); + if (IS_ERR(host->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), + "failed to initialise regmap\n"); /* Clocks */ host->bus_clk = devm_clk_get(&pdev->dev, "bus"); From ad50e1f63873e5d1f2f421bbd11387a0a1d0ca54 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 20 Jan 2026 23:06:03 +0400 Subject: [PATCH 253/341] ASoC: dt-bindings: sophgo,cv1800b: add I2S/TDM controller There are 4 TDM controllers on the SoC. Each controller can receive or transmit data over DMA. The dma it self has 8 channels. Each channel can be connected only to a specific i2s node. But each of dma channel can have multiple purposes so in order to save dma channels the configurations allows to use tx and rx, only rx, only tx or none channels. I2S controller without channels can be useful in configuration where I2S is used as clock source only and doesn't produce any data. Signed-off-by: Anton D. Stavinskii Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260120-cv1800b-i2s-driver-v4-1-6ef787dc6426@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/sophgo,cv1800b-i2s.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml new file mode 100644 index 000000000000..f08362b0ca5e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo CV1800B I2S/TDM controller + +maintainers: + - Anton D. Stavinskii + +description: I2S/TDM controller found in CV1800B / Sophgo SG2002/SG2000 SoCs. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: sophgo,cv1800b-i2s + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: i2s + - const: mclk + + dmas: + minItems: 1 + maxItems: 2 + + dma-names: + minItems: 1 + items: + - enum: [rx, tx] + - const: tx + +required: + - compatible + - reg + - clocks + - clock-names + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include + + i2s@4110000 { + compatible = "sophgo,cv1800b-i2s"; + reg = <0x04110000 0x10000>; + clocks = <&clk CLK_APB_I2S1>, <&clk CLK_SDMA_AUD1>; + clock-names = "i2s", "mclk"; + dmas = <&dmamux 2 1>, <&dmamux 3 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; +... From ea0fb91c02c14748ae525dd547ede7b4a6535d09 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 20 Jan 2026 23:06:04 +0400 Subject: [PATCH 254/341] ASoC: sophgo: add CV1800B I2S/TDM controller driver The actual CPU DAI controller. The driver can be used with simple-audio-card. It respects fixed clock configuration from simple-audio-card. The card driver can request direction out, this will be interpreted as mclk out, the clock which can be used in other CPU or codecs. For example I2S3 generates clock for ADC. I2S was tested in S24_32 and S16 dual channel formats. Signed-off-by: Anton D. Stavinskii Link: https://patch.msgid.link/20260120-cv1800b-i2s-driver-v4-2-6ef787dc6426@gmail.com Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sophgo/Kconfig | 25 ++ sound/soc/sophgo/Makefile | 3 + sound/soc/sophgo/cv1800b-tdm.c | 716 +++++++++++++++++++++++++++++++++ 5 files changed, 746 insertions(+) create mode 100644 sound/soc/sophgo/Kconfig create mode 100644 sound/soc/sophgo/Makefile create mode 100644 sound/soc/sophgo/cv1800b-tdm.c diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 36e0d443ba0e..edfdcbf734fe 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -127,6 +127,7 @@ source "sound/soc/renesas/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sdca/Kconfig" +source "sound/soc/sophgo/Kconfig" source "sound/soc/spacemit/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8c0480e6484e..21d8406767fc 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sdca/ obj-$(CONFIG_SND_SOC) += sof/ +obj-$(CONFIG_SND_SOC) += sophgo/ obj-$(CONFIG_SND_SOC) += spacemit/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sprd/ diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig new file mode 100644 index 000000000000..9495ab49f042 --- /dev/null +++ b/sound/soc/sophgo/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# SoC audio configuration for cv1800b +# + +menu "Sophgo" + depends on COMPILE_TEST || ARCH_SOPHGO + +config SND_SOC_CV1800B_TDM + tristate "Sophgo CV1800B I2S/TDM support" + depends on SND_SOC && OF + select SND_SOC_GENERIC_DMAENGINE_PCM + help + This option enables the I2S/TDM audio controller found in Sophgo + CV1800B / SG2002 SoCs. The controller supports standard I2S + audio modes for playback and capture. + + The driver integrates with the ASoC framework and uses the DMA + engine for audio data transfer. It is intended to be configured + via Device Tree along with simple-audio-card module. + + To compile the driver as a module, choose M here: the module will + be called cv1800b_tdm. + +endmenu diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile new file mode 100644 index 000000000000..3f9f1d07227a --- /dev/null +++ b/sound/soc/sophgo/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# Sophgo Platform Support +obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c new file mode 100644 index 000000000000..4cbac8c1160f --- /dev/null +++ b/sound/soc/sophgo/cv1800b-tdm.c @@ -0,0 +1,716 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TX_FIFO_SIZE (1024) +#define RX_FIFO_SIZE (1024) +#define TX_MAX_BURST (8) +#define RX_MAX_BURST (8) + +#define CV1800B_DEF_FREQ 24576000 +#define CV1800B_DEF_MCLK_FS_RATIO 256 + +/* tdm registers */ +#define CV1800B_BLK_MODE_SETTING 0x000 +#define CV1800B_FRAME_SETTING 0x004 +#define CV1800B_SLOT_SETTING1 0x008 +#define CV1800B_SLOT_SETTING2 0x00C +#define CV1800B_DATA_FORMAT 0x010 +#define CV1800B_BLK_CFG 0x014 +#define CV1800B_I2S_ENABLE 0x018 +#define CV1800B_I2S_RESET 0x01C +#define CV1800B_I2S_INT_EN 0x020 +#define CV1800B_I2S_INT 0x024 +#define CV1800B_FIFO_THRESHOLD 0x028 +#define CV1800B_LRCK_MASTER 0x02C /* special clock only mode */ +#define CV1800B_FIFO_RESET 0x030 +#define CV1800B_RX_STATUS 0x040 +#define CV1800B_TX_STATUS 0x048 +#define CV1800B_CLK_CTRL0 0x060 +#define CV1800B_CLK_CTRL1 0x064 +#define CV1800B_PCM_SYNTH 0x068 +#define CV1800B_RX_RD_PORT 0x080 +#define CV1800B_TX_WR_PORT 0x0C0 + +/* CV1800B_BLK_MODE_SETTING (0x000) */ +#define BLK_TX_MODE_MASK GENMASK(0, 0) +#define BLK_MASTER_MODE_MASK GENMASK(1, 1) +#define BLK_DMA_MODE_MASK GENMASK(7, 7) + +/* CV1800B_CLK_CTRL1 (0x064) */ +#define CLK_MCLK_DIV_MASK GENMASK(15, 0) +#define CLK_BCLK_DIV_MASK GENMASK(31, 16) + +/* CV1800B_CLK_CTRL0 (0x060) */ +#define CLK_AUD_CLK_SEL_MASK GENMASK(0, 0) +#define CLK_BCLK_OUT_CLK_FORCE_EN_MASK GENMASK(6, 6) +#define CLK_MCLK_OUT_EN_MASK GENMASK(7, 7) +#define CLK_AUD_EN_MASK GENMASK(8, 8) + +/* CV1800B_I2S_RESET (0x01C) */ +#define RST_I2S_RESET_RX_MASK GENMASK(0, 0) +#define RST_I2S_RESET_TX_MASK GENMASK(1, 1) + +/* CV1800B_FIFO_RESET (0x030) */ +#define FIFO_RX_RESET_MASK GENMASK(0, 0) +#define FIFO_TX_RESET_MASK GENMASK(16, 16) + +/* CV1800B_I2S_ENABLE (0x018) */ +#define I2S_ENABLE_MASK GENMASK(0, 0) + +/* CV1800B_BLK_CFG (0x014) */ +#define BLK_AUTO_DISABLE_WITH_CH_EN_MASK GENMASK(4, 4) +#define BLK_RX_BLK_CLK_FORCE_EN_MASK GENMASK(8, 8) +#define BLK_RX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(9, 9) +#define BLK_TX_BLK_CLK_FORCE_EN_MASK GENMASK(16, 16) +#define BLK_TX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(17, 17) + +/* CV1800B_FRAME_SETTING (0x004) */ +#define FRAME_LENGTH_MASK GENMASK(8, 0) +#define FS_ACTIVE_LENGTH_MASK GENMASK(23, 16) + +/* CV1800B_I2S_INT_EN (0x020) */ +#define INT_I2S_INT_EN_MASK GENMASK(8, 8) + +/* CV1800B_SLOT_SETTING2 (0x00C) */ +#define SLOT_EN_MASK GENMASK(15, 0) + +/* CV1800B_LRCK_MASTER (0x02C) */ +#define LRCK_MASTER_ENABLE_MASK GENMASK(0, 0) + +/* CV1800B_DATA_FORMAT (0x010) */ +#define DF_WORD_LENGTH_MASK GENMASK(2, 1) +#define DF_TX_SOURCE_LEFT_ALIGN_MASK GENMASK(6, 6) + +/* CV1800B_FIFO_THRESHOLD (0x028) */ +#define FIFO_RX_THRESHOLD_MASK GENMASK(4, 0) +#define FIFO_TX_THRESHOLD_MASK GENMASK(20, 16) +#define FIFO_TX_HIGH_THRESHOLD_MASK GENMASK(28, 24) + +/* CV1800B_SLOT_SETTING1 (0x008) */ +#define SLOT_NUM_MASK GENMASK(3, 0) +#define SLOT_SIZE_MASK GENMASK(13, 8) +#define DATA_SIZE_MASK GENMASK(20, 16) +#define FB_OFFSET_MASK GENMASK(28, 24) + +enum cv1800b_tdm_word_length { + CV1800B_WORD_LENGTH_8_BIT = 0, + CV1800B_WORD_LENGTH_16_BIT = 1, + CV1800B_WORD_LENGTH_32_BIT = 2, +}; + +struct cv1800b_i2s { + void __iomem *base; + struct clk *clk; + struct clk *sysclk; + struct device *dev; + struct snd_dmaengine_dai_dma_data playback_dma; + struct snd_dmaengine_dai_dma_data capture_dma; + u32 mclk_rate; + bool bclk_ratio_fixed; + u32 bclk_ratio; + +}; + +static void cv1800b_setup_dma_struct(struct cv1800b_i2s *i2s, + phys_addr_t phys_base) +{ + i2s->playback_dma.addr = phys_base + CV1800B_TX_WR_PORT; + i2s->playback_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s->playback_dma.fifo_size = TX_FIFO_SIZE; + i2s->playback_dma.maxburst = TX_MAX_BURST; + + i2s->capture_dma.addr = phys_base + CV1800B_RX_RD_PORT; + i2s->capture_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s->capture_dma.fifo_size = RX_FIFO_SIZE; + i2s->capture_dma.maxburst = RX_MAX_BURST; +} + +static const struct snd_dmaengine_pcm_config cv1800b_i2s_pcm_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static void cv1800b_reset_fifo(struct cv1800b_i2s *i2s) +{ + u32 val; + + val = readl(i2s->base + CV1800B_FIFO_RESET); + val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK); + val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK); + writel(val, i2s->base + CV1800B_FIFO_RESET); + + usleep_range(10, 20); + + val = readl(i2s->base + CV1800B_FIFO_RESET); + val = u32_replace_bits(val, 0, FIFO_RX_RESET_MASK); + val = u32_replace_bits(val, 0, FIFO_TX_RESET_MASK); + writel(val, i2s->base + CV1800B_FIFO_RESET); +} + +static void cv1800b_reset_i2s(struct cv1800b_i2s *i2s) +{ + u32 val; + + val = readl(i2s->base + CV1800B_I2S_RESET); + val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK); + val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK); + writel(val, i2s->base + CV1800B_I2S_RESET); + + usleep_range(10, 20); + + val = readl(i2s->base + CV1800B_I2S_RESET); + val = u32_replace_bits(val, 0, RST_I2S_RESET_RX_MASK); + val = u32_replace_bits(val, 0, RST_I2S_RESET_TX_MASK); + writel(val, i2s->base + CV1800B_I2S_RESET); +} + +static void cv1800b_set_mclk_div(struct cv1800b_i2s *i2s, u32 mclk_div) +{ + u32 val; + + val = readl(i2s->base + CV1800B_CLK_CTRL1); + val = u32_replace_bits(val, mclk_div, CLK_MCLK_DIV_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL1); + dev_dbg(i2s->dev, "mclk_div is set to %u\n", mclk_div); +} + +static void cv1800b_set_tx_mode(struct cv1800b_i2s *i2s, bool is_tx) +{ + u32 val; + + val = readl(i2s->base + CV1800B_BLK_MODE_SETTING); + val = u32_replace_bits(val, is_tx, BLK_TX_MODE_MASK); + writel(val, i2s->base + CV1800B_BLK_MODE_SETTING); + dev_dbg(i2s->dev, "tx_mode is set to %u\n", is_tx); +} + +static int cv1800b_set_bclk_div(struct cv1800b_i2s *i2s, u32 bclk_div) +{ + u32 val; + + if (bclk_div == 0 || bclk_div > 0xFFFF) + return -EINVAL; + + val = readl(i2s->base + CV1800B_CLK_CTRL1); + val = u32_replace_bits(val, bclk_div, CLK_BCLK_DIV_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL1); + dev_dbg(i2s->dev, "bclk_div is set to %u\n", bclk_div); + return 0; +} + +/* set memory width of audio data , reg word_length */ +static int cv1800b_set_word_length(struct cv1800b_i2s *i2s, + unsigned int physical_width) +{ + u8 word_length_val; + u32 val; + + switch (physical_width) { + case 8: + word_length_val = CV1800B_WORD_LENGTH_8_BIT; + break; + case 16: + word_length_val = CV1800B_WORD_LENGTH_16_BIT; + break; + case 32: + word_length_val = CV1800B_WORD_LENGTH_32_BIT; + break; + default: + dev_dbg(i2s->dev, "can't set word_length field\n"); + return -EINVAL; + } + + val = readl(i2s->base + CV1800B_DATA_FORMAT); + val = u32_replace_bits(val, word_length_val, DF_WORD_LENGTH_MASK); + writel(val, i2s->base + CV1800B_DATA_FORMAT); + return 0; +} + +static void cv1800b_enable_clocks(struct cv1800b_i2s *i2s, bool enabled) +{ + u32 val; + + val = readl(i2s->base + CV1800B_CLK_CTRL0); + val = u32_replace_bits(val, enabled, CLK_AUD_EN_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL0); +} + +static int cv1800b_set_slot_settings(struct cv1800b_i2s *i2s, u32 slots, + u32 physical_width, u32 data_size) +{ + u32 slot_num; + u32 slot_size; + u32 frame_length; + u32 frame_active_length; + u32 val; + + if (!slots || !physical_width || !data_size) { + dev_err(i2s->dev, "frame or slot settings are not valid\n"); + return -EINVAL; + } + if (slots > 16 || physical_width > 64 || data_size > 32) { + dev_err(i2s->dev, "frame or slot settings are not valid\n"); + return -EINVAL; + } + + slot_num = slots - 1; + slot_size = physical_width - 1; + frame_length = (physical_width * slots) - 1; + frame_active_length = physical_width - 1; + + if (frame_length > 511 || frame_active_length > 255) { + dev_err(i2s->dev, "frame or slot settings are not valid\n"); + return -EINVAL; + } + + val = readl(i2s->base + CV1800B_SLOT_SETTING1); + val = u32_replace_bits(val, slot_size, SLOT_SIZE_MASK); + val = u32_replace_bits(val, data_size - 1, DATA_SIZE_MASK); + val = u32_replace_bits(val, slot_num, SLOT_NUM_MASK); + writel(val, i2s->base + CV1800B_SLOT_SETTING1); + + val = readl(i2s->base + CV1800B_FRAME_SETTING); + val = u32_replace_bits(val, frame_length, FRAME_LENGTH_MASK); + val = u32_replace_bits(val, frame_active_length, FS_ACTIVE_LENGTH_MASK); + writel(val, i2s->base + CV1800B_FRAME_SETTING); + + dev_dbg(i2s->dev, "slot settings num: %u width: %u\n", slots, physical_width); + return 0; +} + +/* + * calculate mclk_div. + * if requested value is bigger than optimal + * leave mclk_div as 1. cff clock is capable + * to handle it + */ +static int cv1800b_calc_mclk_div(unsigned int target_mclk, u32 *mclk_div) +{ + *mclk_div = 1; + + if (target_mclk == 0) + return -EINVAL; + + /* optimal parent frequency is close to CV1800B_DEF_FREQ */ + if (target_mclk < CV1800B_DEF_FREQ) { + *mclk_div = DIV_ROUND_CLOSEST(CV1800B_DEF_FREQ, target_mclk); + if (!*mclk_div || *mclk_div > 0xFFFF) + return -EINVAL; + } + return 0; +} + +/* + * set CCF clock and divider for this clock + * mclk_clock = ccf_clock / mclk_div + */ +static int cv1800b_i2s_set_rate_for_mclk(struct cv1800b_i2s *i2s, + unsigned int target_mclk) +{ + u32 mclk_div = 1; + u64 tmp; + int ret; + unsigned long clk_rate; + unsigned long actual; + + ret = cv1800b_calc_mclk_div(target_mclk, &mclk_div); + if (ret) { + dev_dbg(i2s->dev, "can't calc mclk_div for freq %u\n", + target_mclk); + return ret; + } + + tmp = (u64)target_mclk * mclk_div; + if (tmp > ULONG_MAX) { + dev_err(i2s->dev, "clk_rate overflow: freq=%u div=%u\n", + target_mclk, mclk_div); + return -ERANGE; + } + + clk_rate = (unsigned long)tmp; + + cv1800b_enable_clocks(i2s, false); + + ret = clk_set_rate(i2s->sysclk, clk_rate); + if (ret) + return ret; + + actual = clk_get_rate(i2s->sysclk); + if (clk_rate != actual) { + dev_err_ratelimited(i2s->dev, + "clk_set_rate failed %lu, actual is %lu\n", + clk_rate, actual); + } + + cv1800b_set_mclk_div(i2s, mclk_div); + cv1800b_enable_clocks(i2s, true); + + return 0; +} + +static int cv1800b_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + unsigned int physical_width = params_physical_width(params); + int data_width = params_width(params); + bool tx_mode = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0; + int ret; + u32 bclk_div; + u32 bclk_ratio; + u32 mclk_rate; + u32 tmp; + + if (data_width < 0) + return data_width; + + if (!channels || !rate || !physical_width) + return -EINVAL; + + ret = cv1800b_set_slot_settings(i2s, channels, physical_width, data_width); + if (ret) + return ret; + + if (i2s->mclk_rate) { + mclk_rate = i2s->mclk_rate; + } else { + dev_dbg(i2s->dev, "mclk is not set by machine driver\n"); + ret = cv1800b_i2s_set_rate_for_mclk(i2s, + rate * CV1800B_DEF_MCLK_FS_RATIO); + if (ret) + return ret; + mclk_rate = rate * CV1800B_DEF_MCLK_FS_RATIO; + } + + bclk_ratio = (i2s->bclk_ratio_fixed) ? i2s->bclk_ratio : + (physical_width * channels); + + if (check_mul_overflow(rate, bclk_ratio, &tmp)) + return -EOVERFLOW; + + if (!tmp) + return -EINVAL; + if (mclk_rate % tmp) + dev_warn(i2s->dev, "mclk rate is not aligned to bclk or rate\n"); + + bclk_div = DIV_ROUND_CLOSEST(mclk_rate, tmp); + + ret = cv1800b_set_bclk_div(i2s, bclk_div); + if (ret) + return ret; + + ret = cv1800b_set_word_length(i2s, physical_width); + if (ret) + return ret; + + cv1800b_set_tx_mode(i2s, tx_mode); + + cv1800b_reset_fifo(i2s); + cv1800b_reset_i2s(i2s); + return 0; +} + +static int cv1800b_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 val; + + val = readl(i2s->base + CV1800B_I2S_ENABLE); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = u32_replace_bits(val, 1, I2S_ENABLE_MASK); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = u32_replace_bits(val, 0, I2S_ENABLE_MASK); + break; + default: + return -EINVAL; + } + writel(val, i2s->base + CV1800B_I2S_ENABLE); + return 0; +} + +static int cv1800b_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dev_dbg(i2s->dev, "%s: dai=%s substream=%d\n", __func__, dai->name, + substream->stream); + /** + * Ensure DMA is stopped before DAI + * shutdown (prevents DW AXI DMAC stop/busy on next open). + */ + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + return 0; +} + +static int cv1800b_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (!i2s) { + dev_err(dai->dev, "no drvdata in DAI probe\n"); + return -ENODEV; + } + + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma, &i2s->capture_dma); + return 0; +} + +static int cv1800b_i2s_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 val; + u32 master; + + /* only i2s format is supported */ + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) + return -EINVAL; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + dev_dbg(i2s->dev, "set to master mode\n"); + master = 1; + break; + + case SND_SOC_DAIFMT_CBC_CFC: + dev_dbg(i2s->dev, "set to slave mode\n"); + master = 0; + break; + default: + return -EINVAL; + } + + val = readl(i2s->base + CV1800B_BLK_MODE_SETTING); + val = u32_replace_bits(val, master, BLK_MASTER_MODE_MASK); + writel(val, i2s->base + CV1800B_BLK_MODE_SETTING); + return 0; +} + +static int cv1800b_i2s_dai_set_bclk_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (ratio == 0) + return -EINVAL; + i2s->bclk_ratio = ratio; + i2s->bclk_ratio_fixed = true; + return 0; +} + +static int cv1800b_i2s_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai); + int ret; + u32 val; + bool output_enable = (dir == SND_SOC_CLOCK_OUT) ? true : false; + + dev_dbg(i2s->dev, "%s called with %u\n", __func__, freq); + ret = cv1800b_i2s_set_rate_for_mclk(i2s, freq); + if (ret) + return ret; + + val = readl(i2s->base + CV1800B_CLK_CTRL0); + val = u32_replace_bits(val, output_enable, CLK_MCLK_OUT_EN_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL0); + + i2s->mclk_rate = freq; + return 0; +} + +static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { + .probe = cv1800b_i2s_dai_probe, + .startup = cv1800b_i2s_startup, + .hw_params = cv1800b_i2s_hw_params, + .trigger = cv1800b_i2s_trigger, + .set_fmt = cv1800b_i2s_dai_set_fmt, + .set_bclk_ratio = cv1800b_i2s_dai_set_bclk_ratio, + .set_sysclk = cv1800b_i2s_dai_set_sysclk, +}; + +static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { + .name = "cv1800b-i2s", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &cv1800b_i2s_dai_ops, +}; + +static const struct snd_soc_component_driver cv1800b_i2s_component = { + .name = "cv1800b-i2s", +}; + +static void cv1800b_i2s_hw_disable(struct cv1800b_i2s *i2s) +{ + u32 val; + + val = readl(i2s->base + CV1800B_I2S_ENABLE); + val = u32_replace_bits(val, 0, I2S_ENABLE_MASK); + writel(val, i2s->base + CV1800B_I2S_ENABLE); + + val = readl(i2s->base + CV1800B_CLK_CTRL0); + val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK); + val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL0); + + val = readl(i2s->base + CV1800B_I2S_RESET); + val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK); + val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK); + writel(val, i2s->base + CV1800B_I2S_RESET); + + val = readl(i2s->base + CV1800B_FIFO_RESET); + val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK); + val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK); + writel(val, i2s->base + CV1800B_FIFO_RESET); +} + +static void cv1800b_i2s_setup_tdm(struct cv1800b_i2s *i2s) +{ + u32 val; + + val = readl(i2s->base + CV1800B_BLK_MODE_SETTING); + val = u32_replace_bits(val, 1, BLK_DMA_MODE_MASK); + writel(val, i2s->base + CV1800B_BLK_MODE_SETTING); + + val = readl(i2s->base + CV1800B_CLK_CTRL0); + val = u32_replace_bits(val, 0, CLK_AUD_CLK_SEL_MASK); + val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK); + val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK); + writel(val, i2s->base + CV1800B_CLK_CTRL0); + + val = readl(i2s->base + CV1800B_FIFO_THRESHOLD); + val = u32_replace_bits(val, 4, FIFO_RX_THRESHOLD_MASK); + val = u32_replace_bits(val, 4, FIFO_TX_THRESHOLD_MASK); + val = u32_replace_bits(val, 4, FIFO_TX_HIGH_THRESHOLD_MASK); + writel(val, i2s->base + CV1800B_FIFO_THRESHOLD); + + val = readl(i2s->base + CV1800B_I2S_ENABLE); + val = u32_replace_bits(val, 0, I2S_ENABLE_MASK); + writel(val, i2s->base + CV1800B_I2S_ENABLE); +} + +static int cv1800b_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + 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); + if (!i2s) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + i2s->dev = &pdev->dev; + i2s->base = regs; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + cv1800b_setup_dma_struct(i2s, res->start); + + i2s->clk = devm_clk_get_enabled(dev, "i2s"); + if (IS_ERR(i2s->clk)) + return dev_err_probe(dev, PTR_ERR(i2s->clk), + "failed to get+enable i2s\n"); + i2s->sysclk = devm_clk_get_enabled(dev, "mclk"); + if (IS_ERR(i2s->sysclk)) + return dev_err_probe(dev, PTR_ERR(i2s->sysclk), + "failed to get+enable mclk\n"); + + 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); + if (ret) + 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); + return ret; + } + + return 0; +} + +static void cv1800b_i2s_remove(struct platform_device *pdev) +{ + struct cv1800b_i2s *i2s = platform_get_drvdata(pdev); + + if (!i2s) + return; + cv1800b_i2s_hw_disable(i2s); +} + +static const struct of_device_id cv1800b_i2s_of_match[] = { + { .compatible = "sophgo,cv1800b-i2s" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, cv1800b_i2s_of_match); + +static struct platform_driver cv1800b_i2s_driver = { + .probe = cv1800b_i2s_probe, + .remove = cv1800b_i2s_remove, + .driver = { + .name = "cv1800b-i2s", + .of_match_table = cv1800b_i2s_of_match, + }, +}; +module_platform_driver(cv1800b_i2s_driver); + +MODULE_DESCRIPTION("Sophgo cv1800b I2S/TDM driver"); +MODULE_AUTHOR("Anton D. Stavinsky "); +MODULE_LICENSE("GPL"); From c294aafe474bbbd7a7476773f56f6191742a39e1 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 20 Jan 2026 23:06:05 +0400 Subject: [PATCH 255/341] ASoC: dt-bindings: sophgo,cv1800b: add ADC/DAC codec Document the internal ADC and DAC audio codecs integrated in the Sophgo CV1800B SoC. Signed-off-by: Anton D. Stavinskii Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260120-cv1800b-i2s-driver-v4-3-6ef787dc6426@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/sophgo,cv1800b-codecs.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml new file mode 100644 index 000000000000..7293a98e98c5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-codecs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo CV1800B Internal ADC/DAC Codec + +maintainers: + - Anton D. Stavinskii + +description: + Internal ADC and DAC audio codecs integrated in the Sophgo CV1800B SoC. + Codecs expose a single DAI and are intended to be connected + to an I2S/TDM controller via an ASoC machine driver. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - sophgo,cv1800b-sound-adc + - sophgo,cv1800b-sound-dac + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + audio-codec@300a100 { + compatible = "sophgo,cv1800b-sound-adc"; + reg = <0x0300a100 0x100>; + #sound-dai-cells = <0>; + }; +... From 4cf8752a03e67b2927d137a47c4eca4d516b4838 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 20 Jan 2026 23:06:06 +0400 Subject: [PATCH 256/341] ASoC: sophgo: add CV1800B internal ADC codec driver Codec DAI endpoint for RXADC + basic controls. THe codec have basic volume control. Which is imlemented by lookup table for simplicity. The codec expects set_sysclk callback to adjust internal mclk divider. Signed-off-by: Anton D. Stavinskii Link: https://patch.msgid.link/20260120-cv1800b-i2s-driver-v4-4-6ef787dc6426@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/Kconfig | 12 + sound/soc/sophgo/Makefile | 1 + sound/soc/sophgo/cv1800b-sound-adc.c | 322 +++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 sound/soc/sophgo/cv1800b-sound-adc.c diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig index 9495ab49f042..12d1a57ea308 100644 --- a/sound/soc/sophgo/Kconfig +++ b/sound/soc/sophgo/Kconfig @@ -22,4 +22,16 @@ config SND_SOC_CV1800B_TDM To compile the driver as a module, choose M here: the module will be called cv1800b_tdm. +config SND_SOC_CV1800B_ADC_CODEC + tristate "Sophgo CV1800B/SG2002 internal ADC codec" + depends on SND_SOC + help + This driver provides an ASoC codec DAI for capture and basic + control of the RXADC registers. + + Say Y or M to build support for the Sophgo CV1800B + internal analog ADC codec block (RXADC). + The module will be called cv1800b-sound-adc + + endmenu diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile index 3f9f1d07227a..c654d6059cbd 100644 --- a/sound/soc/sophgo/Makefile +++ b/sound/soc/sophgo/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # Sophgo Platform Support obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o +obj-$(CONFIG_SND_SOC_CV1800B_ADC_CODEC) += cv1800b-sound-adc.o diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c new file mode 100644 index 000000000000..794030b713e9 --- /dev/null +++ b/sound/soc/sophgo/cv1800b-sound-adc.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Internal adc codec for cv1800b compatible SoC + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CV1800B_RXADC_WORD_LEN 16 +#define CV1800B_RXADC_CHANNELS 2 + +#define CV1800B_RXADC_CTRL0 0x00 +#define CV1800B_RXADCC_CTRL1 0x04 +#define CV1800B_RXADC_STATUS 0x08 +#define CV1800B_RXADC_CLK 0x0c +#define CV1800B_RXADC_ANA0 0x10 +#define CV1800B_RXADC_ANA1 0x14 +#define CV1800B_RXADC_ANA2 0x18 +#define CV1800B_RXADC_ANA3 0x1c +#define CV1800B_RXADC_ANA4 0x20 + +/* CV1800B_RXADC_CTRL0 */ +#define REG_RXADC_EN GENMASK(0, 0) +#define REG_I2S_TX_EN GENMASK(1, 1) + +/* CV1800B_RXADCC_CTRL1 */ +#define REG_RXADC_CIC_OPT GENMASK(1, 0) +#define REG_RXADC_IGR_INIT GENMASK(8, 8) + +/* CV1800B_RXADC_ANA0 */ +#define REG_GSTEPL_RXPGA GENMASK(12, 0) +#define REG_G6DBL_RXPGA GENMASK(13, 13) +#define REG_GAINL_RXADC GENMASK(15, 14) +#define REG_GSTEPR_RXPGA GENMASK(28, 16) +#define REG_G6DBR_RXPGA GENMASK(29, 29) +#define REG_GAINR_RXADC GENMASK(31, 30) +#define REG_COMB_LEFT_VOLUME GENMASK(15, 0) +#define REG_COMB_RIGHT_VOLUME GENMASK(31, 16) + +/* CV1800B_RXADC_ANA2 */ +#define REG_MUTEL_RXPGA GENMASK(0, 0) +#define REG_MUTER_RXPGA GENMASK(1, 1) + +/* CV1800B_RXADC_CLK */ +#define REG_RXADC_CLK_INV GENMASK(0, 0) +#define REG_RXADC_SCK_DIV GENMASK(15, 8) +#define REG_RXADC_DLYEN GENMASK(23, 16) + +enum decimation_values { + DECIMATION_64 = 0, + DECIMATION_128, + DECIMATION_256, + DECIMATION_512, +}; + +static const u32 cv1800b_gains[] = { + 0x0001, /* 0dB */ + 0x0002, /* 2dB */ + 0x0004, /* 4dB */ + 0x0008, /* 6dB */ + 0x0010, /* 8dB */ + 0x0020, /* 10dB */ + 0x0040, /* 12dB */ + 0x0080, /* 14dB */ + 0x0100, /* 16dB */ + 0x0200, /* 18dB */ + 0x0400, /* 20dB */ + 0x0800, /* 22dB */ + 0x1000, /* 24dB */ + 0x2400, /* 26dB */ + 0x2800, /* 28dB */ + 0x3000, /* 30dB */ + 0x6400, /* 32dB */ + 0x6800, /* 34dB */ + 0x7000, /* 36dB */ + 0xA400, /* 38dB */ + 0xA800, /* 40dB */ + 0xB000, /* 42dB */ + 0xE400, /* 44dB */ + 0xE800, /* 46dB */ + 0xF000, /* 48dB */ +}; + +struct cv1800b_priv { + void __iomem *regs; + struct device *dev; + unsigned int mclk_rate; +}; + +static int cv1800b_adc_setbclk_div(struct cv1800b_priv *priv, unsigned int rate) +{ + u32 val; + u32 bclk_div; + u64 tmp; + + if (!priv->mclk_rate || !rate) + return -EINVAL; + + tmp = priv->mclk_rate; + tmp /= CV1800B_RXADC_WORD_LEN; + tmp /= CV1800B_RXADC_CHANNELS; + tmp /= rate; + tmp /= 2; + + if (!tmp) { + dev_err(priv->dev, "computed BCLK divider is zero\n"); + return -EINVAL; + } + + if (tmp > 256) { + dev_err(priv->dev, "BCLK divider %llu out of range\n", tmp); + return -EINVAL; + } + + bclk_div = tmp - 1; + val = readl(priv->regs + CV1800B_RXADC_CLK); + val = u32_replace_bits(val, bclk_div, REG_RXADC_SCK_DIV); + /* Vendor value for 48kHz, tested on SG2000/SG2002 */ + val = u32_replace_bits(val, 0x19, REG_RXADC_DLYEN); + writel(val, priv->regs + CV1800B_RXADC_CLK); + + return 0; +} + +static void cv1800b_adc_enable(struct cv1800b_priv *priv, bool enable) +{ + u32 val; + + val = readl(priv->regs + CV1800B_RXADC_CTRL0); + val = u32_replace_bits(val, enable, REG_RXADC_EN); + val = u32_replace_bits(val, enable, REG_I2S_TX_EN); + writel(val, priv->regs + CV1800B_RXADC_CTRL0); +} + +static unsigned int cv1800b_adc_calc_db(u32 ana0, bool right) +{ + u32 step_mask = right ? FIELD_GET(REG_GSTEPR_RXPGA, ana0) : + FIELD_GET(REG_GSTEPL_RXPGA, ana0); + u32 coarse = right ? FIELD_GET(REG_GAINR_RXADC, ana0) : + FIELD_GET(REG_GAINL_RXADC, ana0); + bool g6db = right ? FIELD_GET(REG_G6DBR_RXPGA, ana0) : + FIELD_GET(REG_G6DBL_RXPGA, ana0); + + u32 step = step_mask ? __ffs(step_mask) : 0; + + step = min(step, 12U); + coarse = min(coarse, 3U); + + return 2 * step + 6 * coarse + (g6db ? 6 : 0); +} + +static int cv1800b_adc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + u32 val; + int ret; + + ret = cv1800b_adc_setbclk_div(priv, rate); + if (ret) { + dev_err(priv->dev, + "could not set rate, check DT node for fixed clock\n"); + return ret; + } + + /* init adc */ + val = readl(priv->regs + CV1800B_RXADCC_CTRL1); + val = u32_replace_bits(val, 1, REG_RXADC_IGR_INIT); + val = u32_replace_bits(val, DECIMATION_64, REG_RXADC_CIC_OPT); + writel(val, priv->regs + CV1800B_RXADCC_CTRL1); + return 0; +} + +static int cv1800b_adc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cv1800b_adc_enable(priv, true); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cv1800b_adc_enable(priv, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cv1800b_adc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai); + + priv->mclk_rate = freq; + dev_dbg(priv->dev, "mclk is set to %u\n", freq); + return 0; +} + +static const struct snd_soc_dai_ops cv1800b_adc_dai_ops = { + .hw_params = cv1800b_adc_hw_params, + .set_sysclk = cv1800b_adc_dai_set_sysclk, + .trigger = cv1800b_adc_dai_trigger, +}; + +static struct snd_soc_dai_driver cv1800b_adc_dai = { + .name = "adc-hifi", + .capture = { .stream_name = "ADC Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .ops = &cv1800b_adc_dai_ops, +}; + +static int cv1800b_adc_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component); + u32 ana0 = readl(priv->regs + CV1800B_RXADC_ANA0); + + unsigned int left = cv1800b_adc_calc_db(ana0, false); + unsigned int right = cv1800b_adc_calc_db(ana0, true); + + ucontrol->value.integer.value[0] = min(left / 2, 24U); + ucontrol->value.integer.value[1] = min(right / 2, 24U); + return 0; +} + +static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component); + + u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24); + u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24); + u32 val; + + val = readl(priv->regs + CV1800B_RXADC_ANA0); + val = u32_replace_bits(val, cv1800b_gains[v_left], + REG_COMB_LEFT_VOLUME); + val = u32_replace_bits(val, cv1800b_gains[v_right], + REG_COMB_RIGHT_VOLUME); + writel(val, priv->regs + CV1800B_RXADC_ANA0); + + return 0; +} + +static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0); + +static const struct snd_kcontrol_new cv1800b_adc_controls[] = { + SOC_DOUBLE_EXT_TLV("Internal I2S Capture Volume", SND_SOC_NOPM, 0, 16, 24, false, + cv1800b_adc_volume_get, cv1800b_adc_volume_set, + cv1800b_volume_tlv), +}; + +static const struct snd_soc_component_driver cv1800b_adc_component = { + .name = "cv1800b-adc-codec", + .controls = cv1800b_adc_controls, + .num_controls = ARRAY_SIZE(cv1800b_adc_controls), +}; + +static int cv1800b_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cv1800b_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + platform_set_drvdata(pdev, priv); + return devm_snd_soc_register_component(&pdev->dev, + &cv1800b_adc_component, + &cv1800b_adc_dai, 1); +} + +static const struct of_device_id cv1800b_adc_of_match[] = { + { .compatible = "sophgo,cv1800b-sound-adc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, cv1800b_adc_of_match); + +static struct platform_driver cv1800b_adc_driver = { + .probe = cv1800b_adc_probe, + .driver = { + .name = "cv1800b-sound-adc", + .of_match_table = cv1800b_adc_of_match, + }, +}; + +module_platform_driver(cv1800b_adc_driver); + +MODULE_DESCRIPTION("ADC codec for CV1800B"); +MODULE_AUTHOR("Anton D. Stavinskii "); +MODULE_LICENSE("GPL"); From b3eb755e2db07d85c30e8ff4043ffb9a14b4ece7 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 20 Jan 2026 23:06:07 +0400 Subject: [PATCH 257/341] ASoC: sophgo: add CV1800B internal DAC codec driver Codec DAI endpoint for TXDAC. The codec does only a few things - set up decimation - enable codec and I2S output - ensures the driver doesn't have dac overwrite enabled. (unmute the output) Signed-off-by: Anton D. Stavinskii Link: https://patch.msgid.link/20260120-cv1800b-i2s-driver-v4-5-6ef787dc6426@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/Kconfig | 11 +- sound/soc/sophgo/Makefile | 1 + sound/soc/sophgo/cv1800b-sound-dac.c | 204 +++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sophgo/cv1800b-sound-dac.c diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig index 12d1a57ea308..e4786f087589 100644 --- a/sound/soc/sophgo/Kconfig +++ b/sound/soc/sophgo/Kconfig @@ -28,10 +28,19 @@ config SND_SOC_CV1800B_ADC_CODEC help This driver provides an ASoC codec DAI for capture and basic control of the RXADC registers. - Say Y or M to build support for the Sophgo CV1800B internal analog ADC codec block (RXADC). The module will be called cv1800b-sound-adc +config SND_SOC_CV1800B_DAC_CODEC + tristate "Sophgo CV1800B/SG2002 internal DAC codec" + depends on SND_SOC + help + This driver provides an ASoC codec DAI for playback and basic + control of the TXDAC registers. + + Say Y or M to build support for the Sophgo CV1800B + internal analog DAC codec block (TXDAC). + The module will be called cv1800b-sound-dac endmenu diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile index c654d6059cbd..ec8dd31efddd 100644 --- a/sound/soc/sophgo/Makefile +++ b/sound/soc/sophgo/Makefile @@ -2,3 +2,4 @@ # Sophgo Platform Support obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o obj-$(CONFIG_SND_SOC_CV1800B_ADC_CODEC) += cv1800b-sound-adc.o +obj-$(CONFIG_SND_SOC_CV1800B_DAC_CODEC) += cv1800b-sound-dac.o diff --git a/sound/soc/sophgo/cv1800b-sound-dac.c b/sound/soc/sophgo/cv1800b-sound-dac.c new file mode 100644 index 000000000000..ccf386174639 --- /dev/null +++ b/sound/soc/sophgo/cv1800b-sound-dac.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Internal DAC codec for cv1800b based CPUs + */ + +#include +#include +#include +#include +#include +#include + +#define CV1800B_TXDAC_CTRL0 0x00 +#define CV1800B_TXDAC_CTRL1 0x04 +#define CV1800B_TXDAC_STATUS 0x08 +#define CV1800B_TXDAC_AFE0 0x0c +#define CV1800B_TXDAC_AFE1 0x10 +#define CV1800B_TXDAC_ANA0 0x20 +#define CV1800B_TXDAC_ANA1 0x24 +#define CV1800B_TXDAC_ANA2 0x28 + +/* cv1800b_TXDAC_CTRL0 */ +#define REG_TXDAC_EN GENMASK(0, 0) +#define REG_I2S_RX_EN GENMASK(1, 1) + +/* cv1800b_TXDAC_CTRL1 */ +#define REG_TXDAC_CIC_OPT GENMASK(1, 0) + +/* cv1800b_TXDAC_AFE0 */ +#define REG_TXDAC_INIT_DLY_CNT GENMASK(5, 0) + +/* cv1800b_TXDAC_ANA2 */ +#define TXDAC_OW_VAL_L_MASK GENMASK(7, 0) +#define TXDAC_OW_VAL_R_MASK GENMASK(15, 8) +#define TXDAC_OW_EN_L_MASK GENMASK(16, 16) +#define TXDAC_OW_EN_R_MASK GENMASK(17, 17) + +struct cv1800b_priv { + void __iomem *regs; + struct device *dev; +}; + +enum decimation_values { + DECIMATION_64 = 0, + DECIMATION_128, + DECIMATION_256, + DECIMATION_512, +}; + +static void cv1800b_dac_enable(struct cv1800b_priv *priv, bool enable) +{ + u32 val; + + val = readl(priv->regs + CV1800B_TXDAC_CTRL0); + val = u32_replace_bits(val, enable, REG_TXDAC_EN); + val = u32_replace_bits(val, enable, REG_I2S_RX_EN); + writel(val, priv->regs + CV1800B_TXDAC_CTRL0); +} + +static void cv1800b_dac_mute(struct cv1800b_priv *priv, bool enable) +{ + u32 val; + + val = readl(priv->regs + CV1800B_TXDAC_ANA2); + val = u32_replace_bits(val, enable, TXDAC_OW_EN_L_MASK); + val = u32_replace_bits(val, enable, TXDAC_OW_EN_R_MASK); + writel(val, priv->regs + CV1800B_TXDAC_ANA2); +} + +static int cv1800b_dac_decimation(struct cv1800b_priv *priv, u8 dec) +{ + u32 val; + + if (dec > 3) + return -EINVAL; + + val = readl(priv->regs + CV1800B_TXDAC_CTRL1); + val = u32_replace_bits(val, dec, REG_TXDAC_CIC_OPT); + writel(val, priv->regs + CV1800B_TXDAC_CTRL1); + return 0; +} + +static int cv1800b_dac_dly(struct cv1800b_priv *priv, u32 dly) +{ + u32 val; + + if (dly > 63) + return -EINVAL; + + val = readl(priv->regs + CV1800B_TXDAC_AFE0); + val = u32_replace_bits(val, dly, REG_TXDAC_INIT_DLY_CNT); + writel(val, priv->regs + CV1800B_TXDAC_AFE0); + return 0; +} + +static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai); + int ret; + unsigned int rate = params_rate(params); + + if (rate != 48000) { + dev_err(priv->dev, "rate %u is not supported\n", rate); + return -EINVAL; + } + + cv1800b_dac_mute(priv, false); + /* minimal decimation for 48kHz is 64*/ + ret = cv1800b_dac_decimation(priv, DECIMATION_64); + if (ret) + return ret; + + /* value is taken from vendors driver 48kHz + * tested on sg2000 and sg2002. + */ + ret = cv1800b_dac_dly(priv, 0x19); + if (ret) + return ret; + + return 0; +} + +static int cv1800b_dac_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cv1800b_dac_enable(priv, true); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cv1800b_dac_enable(priv, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops cv1800b_dac_dai_ops = { + .hw_params = cv1800b_dac_hw_params, + .trigger = cv1800b_dac_dai_trigger, +}; + +static struct snd_soc_dai_driver cv1800b_dac_dai = { + .name = "dac-hifi", + .playback = { .stream_name = "DAC Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .ops = &cv1800b_dac_dai_ops, +}; + +static const struct snd_soc_component_driver cv1800b_dac_component = { + .name = "cv1800b-dac-codec", +}; + +static int cv1800b_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cv1800b_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + platform_set_drvdata(pdev, priv); + return devm_snd_soc_register_component(&pdev->dev, + &cv1800b_dac_component, + &cv1800b_dac_dai, 1); +} + +static const struct of_device_id cv1800b_dac_of_match[] = { + { .compatible = "sophgo,cv1800b-sound-dac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cv1800b_dac_of_match); + +static struct platform_driver cv1800b_dac_driver = { + .probe = cv1800b_dac_probe, + .driver = { + .name = "cv1800b-dac-codec", + .of_match_table = cv1800b_dac_of_match, + }, +}; +module_platform_driver(cv1800b_dac_driver); + +MODULE_DESCRIPTION("DAC codec for CV1800B"); +MODULE_AUTHOR("Anton D. Stavinskii "); +MODULE_LICENSE("GPL"); From a8e3e488293118b8fa5d5e7ca786ca25b954ce12 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 27 Jan 2026 23:08:19 +0400 Subject: [PATCH 258/341] ASoC: sophgo: cv1800b: document DAC overwrite handling Add comments to cv1800b_dac_mute() and its caller to explain how the overwrite mechanism works and why we force it off before playback. Signed-off-by: Anton D. Stavinskii Link: https://patch.msgid.link/20260127-incremental-for-i2s-dvier-v2-1-5f66b841f63d@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-sound-dac.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/sophgo/cv1800b-sound-dac.c b/sound/soc/sophgo/cv1800b-sound-dac.c index ccf386174639..135322bcf6ad 100644 --- a/sound/soc/sophgo/cv1800b-sound-dac.c +++ b/sound/soc/sophgo/cv1800b-sound-dac.c @@ -57,6 +57,10 @@ static void cv1800b_dac_enable(struct cv1800b_priv *priv, bool enable) writel(val, priv->regs + CV1800B_TXDAC_CTRL0); } +/* + * Control the DAC overwrite bits. When enabled, the DAC outputs the fixed + * overwrite value instead of samples from the I2S input. + */ static void cv1800b_dac_mute(struct cv1800b_priv *priv, bool enable) { u32 val; @@ -105,7 +109,7 @@ static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream, dev_err(priv->dev, "rate %u is not supported\n", rate); return -EINVAL; } - + /* Clear DAC overwrite so playback uses I2S data. */ cv1800b_dac_mute(priv, false); /* minimal decimation for 48kHz is 64*/ ret = cv1800b_dac_decimation(priv, DECIMATION_64); From 8cf19b19dba8814ccc8b1179dabf28b7f8eefc22 Mon Sep 17 00:00:00 2001 From: "Anton D. Stavinskii" Date: Tue, 27 Jan 2026 23:08:20 +0400 Subject: [PATCH 259/341] ASoC: sophgo: cv1800b: tidy Kconfig spacing Restore the empty line that was accidentally removed earlier Signed-off-by: Anton D. Stavinskii Link: https://patch.msgid.link/20260127-incremental-for-i2s-dvier-v2-2-5f66b841f63d@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig index e4786f087589..9b454261bcfd 100644 --- a/sound/soc/sophgo/Kconfig +++ b/sound/soc/sophgo/Kconfig @@ -28,6 +28,7 @@ config SND_SOC_CV1800B_ADC_CODEC help This driver provides an ASoC codec DAI for capture and basic control of the RXADC registers. + Say Y or M to build support for the Sophgo CV1800B internal analog ADC codec block (RXADC). The module will be called cv1800b-sound-adc From ab3f4f0c7f3aa050cd602cc32830e24ac4aaee97 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 28 Jan 2026 10:59:55 +0800 Subject: [PATCH 260/341] ASoC: wm8962: add .set_tdm_slot callback function The slot_width can be different with the params_width(), for example, DSP_A mode, slot_width = 32, but data format is S16_LE, if the word length is configured to be 16, there is no sound on the right speaker. So add .set_tdm_slot() callback function to configure the slot_width and update the word length according to slot_width in hw_params(). Signed-off-by: Shengjiu Wang Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260128025955.2562331-1-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index bff864467416..8d2435bf44ea 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -85,6 +85,8 @@ struct wm8962_priv { int irq; bool master_flag; + int tdm_width; + int tdm_slots; }; /* We can't use the same notifier block for more than one supply and @@ -2612,6 +2614,19 @@ static int wm8962_set_bias_level(struct snd_soc_component *component, return 0; } +static int wm8962_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 wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + wm8962->tdm_width = slot_width; + /* External is one slot one channel, but internal is one slot two channels */ + wm8962->tdm_slots = slots / 2; + + return 0; +} + static const struct { int rate; int reg; @@ -2639,10 +2654,21 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, int i; int aif0 = 0; int adctl3 = 0; + int width; - wm8962->bclk = snd_soc_params_to_bclk(params); - if (params_channels(params) == 1) - wm8962->bclk *= 2; + if (wm8962->tdm_width && wm8962->tdm_slots) { + wm8962->bclk = snd_soc_calc_bclk(params_rate(params), + wm8962->tdm_width, + params_channels(params), + wm8962->tdm_slots); + width = wm8962->tdm_width; + } else { + wm8962->bclk = snd_soc_params_to_bclk(params); + width = params_width(params); + + if (params_channels(params) == 1) + wm8962->bclk *= 2; + } wm8962->lrclk = params_rate(params); @@ -2660,7 +2686,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, if (wm8962->lrclk % 8000 == 0) adctl3 |= WM8962_SAMPLE_RATE_INT_MODE; - switch (params_width(params)) { + switch (width) { case 16: break; case 20: @@ -3039,6 +3065,7 @@ static const struct snd_soc_dai_ops wm8962_dai_ops = { .hw_params = wm8962_hw_params, .set_sysclk = wm8962_set_dai_sysclk, .set_fmt = wm8962_set_dai_fmt, + .set_tdm_slot = wm8962_set_tdm_slot, .mute_stream = wm8962_mute, .no_capture_mute = 1, }; From 0ba6286a71581aaf8413a55b9bd90ea3463fd23b Mon Sep 17 00:00:00 2001 From: Sheetal Date: Fri, 23 Jan 2026 15:23:43 +0530 Subject: [PATCH 261/341] ASoC: tegra: Add AHUB writeable_reg for RX holes Add writeable_reg callbacks for Tegra210/186 AHUB RX registers so the flat cache only treats valid RX locations as writable, avoiding holes in the register map. Fixes: 16e1bcc2caf4 ("ASoC: tegra: Add Tegra210 based AHUB driver") Signed-off-by: Sheetal Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://patch.msgid.link/20260123095346.1258556-2-sheetal@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_ahub.c | 57 +++++++++++++++++++++++++++++++++ sound/soc/tegra/tegra210_ahub.h | 30 +++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index e795907a3963..fc5892056f83 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -2049,6 +2049,61 @@ static const struct snd_soc_component_driver tegra264_ahub_component = { .num_dapm_routes = ARRAY_SIZE(tegra264_ahub_routes), }; +static bool tegra210_ahub_wr_reg(struct device *dev, unsigned int reg) +{ + int part; + + if (reg % TEGRA210_XBAR_RX_STRIDE) + return false; + + for (part = 0; part < TEGRA210_XBAR_UPDATE_MAX_REG; part++) { + switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) { + case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0: + case TEGRA210_AXBAR_PART_0_I2S1_RX1_0 ... TEGRA210_AXBAR_PART_0_I2S5_RX1_0: + case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0: + case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0: + case TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 ... TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0: + case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0: + case TEGRA210_AXBAR_PART_0_OPE1_RX1_0 ... TEGRA210_AXBAR_PART_0_OPE2_RX1_0: + case TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0: + case TEGRA210_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA210_AXBAR_PART_0_MVC2_RX1_0: + case TEGRA210_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA210_AXBAR_PART_0_ADX2_RX1_0: + return true; + default: + break; + } + } + + return false; +} + +static bool tegra186_ahub_wr_reg(struct device *dev, unsigned int reg) +{ + int part; + + if (reg % TEGRA210_XBAR_RX_STRIDE) + return false; + + for (part = 0; part < TEGRA186_XBAR_UPDATE_MAX_REG; part++) { + switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) { + case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA186_AXBAR_PART_0_I2S6_RX1_0: + case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0: + case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0: + case TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 ... TEGRA186_AXBAR_PART_0_DSPK2_RX1_0: + case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0: + case TEGRA210_AXBAR_PART_0_OPE1_RX1_0: + case TEGRA186_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA186_AXBAR_PART_0_MVC2_RX1_0: + case TEGRA186_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA186_AXBAR_PART_0_AMX3_RX4_0: + case TEGRA210_AXBAR_PART_0_ADX1_RX1_0 ... TEGRA186_AXBAR_PART_0_ASRC1_RX7_0: + return true; + default: + break; + } + } + + return false; +} + static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg) { int part; @@ -2076,6 +2131,7 @@ static const struct regmap_config tegra210_ahub_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, + .writeable_reg = tegra210_ahub_wr_reg, .max_register = TEGRA210_MAX_REGISTER_ADDR, .cache_type = REGCACHE_FLAT, }; @@ -2084,6 +2140,7 @@ static const struct regmap_config tegra186_ahub_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, + .writeable_reg = tegra186_ahub_wr_reg, .max_register = TEGRA186_MAX_REGISTER_ADDR, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h index f355b2cfd19b..acbe640dd3b5 100644 --- a/sound/soc/tegra/tegra210_ahub.h +++ b/sound/soc/tegra/tegra210_ahub.h @@ -68,6 +68,36 @@ #define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX + \ (TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1))) +/* AXBAR register offsets */ +#define TEGRA186_AXBAR_PART_0_AMX1_RX1_0 0x120 +#define TEGRA186_AXBAR_PART_0_AMX3_RX4_0 0x14c +#define TEGRA186_AXBAR_PART_0_ASRC1_RX7_0 0x1a8 +#define TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 0xc0 +#define TEGRA186_AXBAR_PART_0_DSPK2_RX1_0 0xc4 +#define TEGRA186_AXBAR_PART_0_I2S6_RX1_0 0x54 +#define TEGRA186_AXBAR_PART_0_MVC1_RX1_0 0x110 +#define TEGRA186_AXBAR_PART_0_MVC2_RX1_0 0x114 +#define TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0 0x24 +#define TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 0x0 +#define TEGRA210_AXBAR_PART_0_ADX1_RX1_0 0x160 +#define TEGRA210_AXBAR_PART_0_ADX2_RX1_0 0x164 +#define TEGRA210_AXBAR_PART_0_AFC1_RX1_0 0xd0 +#define TEGRA210_AXBAR_PART_0_AFC6_RX1_0 0xe4 +#define TEGRA210_AXBAR_PART_0_AMX1_RX1_0 0x140 +#define TEGRA210_AXBAR_PART_0_I2S1_RX1_0 0x40 +#define TEGRA210_AXBAR_PART_0_I2S5_RX1_0 0x50 +#define TEGRA210_AXBAR_PART_0_MIXER1_RX10_0 0xa4 +#define TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 0x80 +#define TEGRA210_AXBAR_PART_0_MVC1_RX1_0 0x120 +#define TEGRA210_AXBAR_PART_0_MVC2_RX1_0 0x124 +#define TEGRA210_AXBAR_PART_0_OPE1_RX1_0 0x100 +#define TEGRA210_AXBAR_PART_0_OPE2_RX1_0 0x104 +#define TEGRA210_AXBAR_PART_0_SFC1_RX1_0 0x60 +#define TEGRA210_AXBAR_PART_0_SFC4_RX1_0 0x6c +#define TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 0xc0 +#define TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0 0xc4 +#define TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0 0x110 + #define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id)) #define MUX_VALUE(npart, nbit) (1 + (nbit) + (npart) * 32) From 9409d18bf7d58ab716337749e28e2caba0d64cb0 Mon Sep 17 00:00:00 2001 From: Sheetal Date: Fri, 23 Jan 2026 15:23:45 +0530 Subject: [PATCH 262/341] ASoC: tegra: set reg_default_cb callback Set reg_default_cb so REGCACHE_FLAT can supply zero defaults without large reg_defaults tables, simplifying cache initialization for zero-reset registers. Signed-off-by: Sheetal Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://patch.msgid.link/20260123095346.1258556-4-sheetal@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra186_asrc.c | 1 + sound/soc/tegra/tegra186_dspk.c | 1 + sound/soc/tegra/tegra210_admaif.c | 3 +++ sound/soc/tegra/tegra210_adx.c | 2 ++ sound/soc/tegra/tegra210_ahub.c | 3 +++ sound/soc/tegra/tegra210_amx.c | 3 +++ sound/soc/tegra/tegra210_dmic.c | 1 + sound/soc/tegra/tegra210_i2s.c | 2 ++ sound/soc/tegra/tegra210_mbdrc.c | 1 + sound/soc/tegra/tegra210_mixer.c | 1 + sound/soc/tegra/tegra210_mvc.c | 1 + sound/soc/tegra/tegra210_ope.c | 1 + sound/soc/tegra/tegra210_peq.c | 1 + sound/soc/tegra/tegra210_sfc.c | 1 + 14 files changed, 22 insertions(+) diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c index 2c0220e14a57..d2a5ec7c54cc 100644 --- a/sound/soc/tegra/tegra186_asrc.c +++ b/sound/soc/tegra/tegra186_asrc.c @@ -950,6 +950,7 @@ static const struct regmap_config tegra186_asrc_regmap_config = { .volatile_reg = tegra186_asrc_volatile_reg, .reg_defaults = tegra186_asrc_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c index a762150db802..8816e4967331 100644 --- a/sound/soc/tegra/tegra186_dspk.c +++ b/sound/soc/tegra/tegra186_dspk.c @@ -467,6 +467,7 @@ static const struct regmap_config tegra186_dspk_regmap = { .volatile_reg = tegra186_dspk_volatile_reg, .reg_defaults = tegra186_dspk_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index f9f6040c4e34..0976779d29f2 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -241,6 +241,7 @@ static const struct regmap_config tegra210_admaif_regmap_config = { .volatile_reg = tegra_admaif_volatile_reg, .reg_defaults = tegra210_admaif_reg_defaults, .num_reg_defaults = TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -254,6 +255,7 @@ static const struct regmap_config tegra186_admaif_regmap_config = { .volatile_reg = tegra_admaif_volatile_reg, .reg_defaults = tegra186_admaif_reg_defaults, .num_reg_defaults = TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -267,6 +269,7 @@ static const struct regmap_config tegra264_admaif_regmap_config = { .volatile_reg = tegra_admaif_volatile_reg, .reg_defaults = tegra264_admaif_reg_defaults, .num_reg_defaults = TEGRA264_ADMAIF_CHANNEL_COUNT * 6 + 1, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 6c9a410085bc..95875c75ddf8 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -625,6 +625,7 @@ static const struct regmap_config tegra210_adx_regmap_config = { .volatile_reg = tegra210_adx_volatile_reg, .reg_defaults = tegra210_adx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -638,6 +639,7 @@ static const struct regmap_config tegra264_adx_regmap_config = { .volatile_reg = tegra264_adx_volatile_reg, .reg_defaults = tegra264_adx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra264_adx_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index fc5892056f83..43a45f785d5b 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -2133,6 +2133,7 @@ static const struct regmap_config tegra210_ahub_regmap_config = { .reg_stride = 4, .writeable_reg = tegra210_ahub_wr_reg, .max_register = TEGRA210_MAX_REGISTER_ADDR, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -2142,6 +2143,7 @@ static const struct regmap_config tegra186_ahub_regmap_config = { .reg_stride = 4, .writeable_reg = tegra186_ahub_wr_reg, .max_register = TEGRA186_MAX_REGISTER_ADDR, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -2151,6 +2153,7 @@ static const struct regmap_config tegra264_ahub_regmap_config = { .reg_stride = 4, .writeable_reg = tegra264_ahub_wr_reg, .max_register = TEGRA264_MAX_REGISTER_ADDR, + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c index c94f8c84e04f..bfda82505298 100644 --- a/sound/soc/tegra/tegra210_amx.c +++ b/sound/soc/tegra/tegra210_amx.c @@ -654,6 +654,7 @@ static const struct regmap_config tegra210_amx_regmap_config = { .volatile_reg = tegra210_amx_volatile_reg, .reg_defaults = tegra210_amx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -667,6 +668,7 @@ static const struct regmap_config tegra194_amx_regmap_config = { .volatile_reg = tegra210_amx_volatile_reg, .reg_defaults = tegra210_amx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -680,6 +682,7 @@ static const struct regmap_config tegra264_amx_regmap_config = { .volatile_reg = tegra264_amx_volatile_reg, .reg_defaults = tegra264_amx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra264_amx_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c index 66fff53aeaa6..93def7ac4fde 100644 --- a/sound/soc/tegra/tegra210_dmic.c +++ b/sound/soc/tegra/tegra210_dmic.c @@ -483,6 +483,7 @@ static const struct regmap_config tegra210_dmic_regmap_config = { .volatile_reg = tegra210_dmic_volatile_reg, .reg_defaults = tegra210_dmic_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index b91e0e6cd7fe..d8e02f0a3025 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -997,6 +997,7 @@ static const struct regmap_config tegra210_regmap_conf = { .volatile_reg = tegra210_i2s_volatile_reg, .reg_defaults = tegra210_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_i2s_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; @@ -1044,6 +1045,7 @@ static const struct regmap_config tegra264_regmap_conf = { .volatile_reg = tegra264_i2s_volatile_reg, .reg_defaults = tegra264_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra264_i2s_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c index 09fe3c5cf540..6a268dbb7197 100644 --- a/sound/soc/tegra/tegra210_mbdrc.c +++ b/sound/soc/tegra/tegra210_mbdrc.c @@ -763,6 +763,7 @@ static const struct regmap_config tegra210_mbdrc_regmap_cfg = { .precious_reg = tegra210_mbdrc_precious_reg, .reg_defaults = tegra210_mbdrc_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_mbdrc_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index ff8e9f2d7abf..6d3a2b76fd61 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -608,6 +608,7 @@ static const struct regmap_config tegra210_mixer_regmap_config = { .precious_reg = tegra210_mixer_precious_reg, .reg_defaults = tegra210_mixer_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index 779d4c199da9..6cdc5e1f5507 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -699,6 +699,7 @@ static const struct regmap_config tegra210_mvc_regmap_config = { .volatile_reg = tegra210_mvc_volatile_reg, .reg_defaults = tegra210_mvc_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c index 27db70af2746..a440888dcdbd 100644 --- a/sound/soc/tegra/tegra210_ope.c +++ b/sound/soc/tegra/tegra210_ope.c @@ -297,6 +297,7 @@ static const struct regmap_config tegra210_ope_regmap_config = { .volatile_reg = tegra210_ope_volatile_reg, .reg_defaults = tegra210_ope_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_peq.c b/sound/soc/tegra/tegra210_peq.c index 9a05e6913276..2f72e9d541dc 100644 --- a/sound/soc/tegra/tegra210_peq.c +++ b/sound/soc/tegra/tegra210_peq.c @@ -306,6 +306,7 @@ static const struct regmap_config tegra210_peq_regmap_config = { .precious_reg = tegra210_peq_precious_reg, .reg_defaults = tegra210_peq_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c index d6341968bebe..b298bf0421b1 100644 --- a/sound/soc/tegra/tegra210_sfc.c +++ b/sound/soc/tegra/tegra210_sfc.c @@ -3569,6 +3569,7 @@ static const struct regmap_config tegra210_sfc_regmap_config = { .precious_reg = tegra210_sfc_precious_reg, .reg_defaults = tegra210_sfc_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tegra210_sfc_reg_defaults), + .reg_default_cb = regmap_default_zero_cb, .cache_type = REGCACHE_FLAT, }; From 88340fc880cae6a2953a366aa6ad0900fd9830da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 28 Jan 2026 02:19:00 +0000 Subject: [PATCH 263/341] ASoC: soc-core: add lockdep_assert_held() at snd_soc_unregister_dai() snd_soc_register_dai() has lockdep_assert_held() (A), but snd_soc_unregister_dai() doesn't have lockdep_assert_held() (B). void snd_soc_unregister_dai(...) { (B) dev_dbg(...); list_del(...); } struct snd_soc_dai *snd_soc_register_dai(...) { ... (A) lockdep_assert_held(&client_mutex); ... } Both functions should be called with client_mutex lock. Add missing lockdep_assert_held() at snd_soc_unregister_dai(). Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jyx21nu4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 355ccc95f28b..5811d053ce7a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2679,6 +2679,8 @@ static inline char *fmt_multiple_name(struct device *dev, void snd_soc_unregister_dai(struct snd_soc_dai *dai) { + lockdep_assert_held(&client_mutex); + dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); list_del(&dai->list); } From bbb758a6943e19c483ab752cf8220140b46cf22c Mon Sep 17 00:00:00 2001 From: Boris Faure Date: Thu, 29 Jan 2026 14:14:54 +0000 Subject: [PATCH 264/341] ASoC: sdca: Fix missing regmap dependencies in Kconfig The SDCA modules failed to build with modpost errors: ERROR: modpost: "__devm_regmap_init_sdw" [sound/soc/sdca/snd-soc-sdca-class.ko] undefined! ERROR: modpost: "__devm_regmap_init_sdw_mbq" [sound/soc/sdca/snd-soc-sdca-class-function.ko] undefined! The issue occurs because: - sdca_class.c calls devm_regmap_init_sdw() which requires REGMAP_SOUNDWIRE - sdca_class_function.c calls devm_regmap_init_sdw_mbq_cfg() which requires REGMAP_SOUNDWIRE_MBQ However, the Kconfig didn't select these dependencies, causing the symbols to be unavailable when the SDCA modules are built. Fix this by adding: - select REGMAP_SOUNDWIRE to SND_SOC_SDCA_CLASS - select REGMAP_SOUNDWIRE_MBQ to SND_SOC_SDCA_CLASS_FUNCTION This ensures the required regmap drivers are enabled when building SDCA support. Configuration after fix: CONFIG_SND_SOC_SDCA_CLASS=m CONFIG_SND_SOC_SDCA_CLASS_FUNCTION=m CONFIG_REGMAP_SOUNDWIRE=m CONFIG_REGMAP_SOUNDWIRE_MBQ=m Signed-off-by: Boris Faure Link: https://patch.msgid.link/20260129141419.13843-1-boris@fau.re Signed-off-by: Mark Brown --- sound/soc/sdca/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index fabb69a3450d..87ab2895096c 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -46,12 +46,14 @@ config SND_SOC_SDCA_CLASS select SND_SOC_SDCA_FDL select SND_SOC_SDCA_HID select SND_SOC_SDCA_IRQ + select REGMAP_SOUNDWIRE help This option enables support for the SDCA Class driver which should support any class compliant SDCA part. config SND_SOC_SDCA_CLASS_FUNCTION tristate + select REGMAP_SOUNDWIRE_MBQ help This option enables support for the SDCA Class Function drivers, these implement the individual functions of the SDCA Class driver. From a1d14d8364eac2611fe1391c73ff0e5b26064f0e Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Fri, 30 Jan 2026 17:19:04 +0800 Subject: [PATCH 265/341] ASoC: codecs: max98390: Check return value of devm_gpiod_get_optional() in max98390_i2c_probe() The devm_gpiod_get_optional() function may return an error pointer (ERR_PTR) in case of a genuine failure during GPIO acquisition, not just NULL which indicates the legitimate absence of an optional GPIO. Add an IS_ERR() check after the function call to catch such errors and propagate them to the probe function, ensuring the driver fails to load safely rather than proceeding with an invalid pointer. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20260130091904.3426149-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98390.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 3dd4dd94bc37..ff58805e97d1 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1067,6 +1067,9 @@ static int max98390_i2c_probe(struct i2c_client *i2c) reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(reset_gpio), + "Failed to get reset gpio\n"); /* Power on device */ if (reset_gpio) { From 1db63f6af17945aed7497ce34a5648add0c1b6d9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 2 Feb 2026 10:54:12 +0100 Subject: [PATCH 266/341] ASoC: rt5575: fix SPI dependency The rt5575 driver fails to link when SPI support is in a loadable module but the codec is built-in: x86_64-linux-ld: vmlinux.o: in function `rt5575_i2c_probe': rt5575.c:(.text+0x9792ce): undefined reference to `rt5575_spi_get_device' rt5575.c:(.text+0x979332): undefined reference to `rt5575_spi_fw_load' Change the symbol in to a 'bool' and add a dependency that rules out the broken configuration. Fixes: 420739112e95 ("ASoC: rt5575: Add the codec driver for the ALC5575") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260202095432.1234133-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f34d7b510c91..e78ac302da15 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1801,9 +1801,10 @@ config SND_SOC_RT5575 depends on I2C config SND_SOC_RT5575_SPI - tristate "Realtek ALC5575 Codec - SPI" + bool "Realtek ALC5575 Codec - SPI" depends on SPI_MASTER && I2C depends on SND_SOC_RT5575 + depends on SPI_MASTER=y || SND_SOC_RT5575=m config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" From cad9720dd7e4dcdaec8e854fb2c1d1d45fd6dbad Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 2 Feb 2026 10:53:14 +0100 Subject: [PATCH 267/341] ASoC: sophgo: fix 64-bit division build failure cv1800b_adc_setbclk_div() does four 64-bit divisions in a row, which is rather inefficient on 32-bit systems, and using the plain division causes a build failure as a result: ERROR: modpost: "__aeabi_uldivmod" [sound/soc/sophgo/cv1800b-sound-adc.ko] undefined! Consolidate those into a single division using the div_u64() macro. Fixes: 4cf8752a03e6 ("ASoC: sophgo: add CV1800B internal ADC codec driver") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260202095323.1233553-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-sound-adc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c index 794030b713e9..b66761156b99 100644 --- a/sound/soc/sophgo/cv1800b-sound-adc.c +++ b/sound/soc/sophgo/cv1800b-sound-adc.c @@ -105,11 +105,8 @@ static int cv1800b_adc_setbclk_div(struct cv1800b_priv *priv, unsigned int rate) if (!priv->mclk_rate || !rate) return -EINVAL; - tmp = priv->mclk_rate; - tmp /= CV1800B_RXADC_WORD_LEN; - tmp /= CV1800B_RXADC_CHANNELS; - tmp /= rate; - tmp /= 2; + tmp = div_u64(priv->mclk_rate, CV1800B_RXADC_WORD_LEN * + CV1800B_RXADC_CHANNELS * rate * 2); if (!tmp) { dev_err(priv->dev, "computed BCLK divider is zero\n"); From 742048f2e128c06d0ef89ffc334519cb8e991f66 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 2 Feb 2026 10:53:50 +0100 Subject: [PATCH 268/341] ASoC: fsl_sai: add IMX_SCMI_MISC_DRV dependency The sai driver now links against the SCMI code directly, causing a link failure when that is in a loadable module: aarch64-linux-ld: sound/soc/fsl/fsl_sai.o: in function `fsl_sai_probe': fsl_sai.c:(.text+0x1fe4): undefined reference to `scmi_imx_misc_ctrl_set' Move the dependency from SND_SOC_FSL_MQS to SND_SOC_FSL_SAI. The MQS driver depends on the SAI one, so it still gets the same dependency indirectly. All other drivers that select the SAI symbol need the same dependency in turn, though that could probably get replaced with a 'depends on SND_SOC_FSL_SAI' to keep it simpler. Fixes: 19b08fd23b20 ("ASoC: fsl_sai: Add AUDMIX mode support on i.MX952") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260202095353.1233963-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index c4a00b22bc2a..828524c90f17 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -19,6 +19,7 @@ config SND_SOC_FSL_ASRC config SND_SOC_FSL_SAI tristate "Synchronous Audio Interface (SAI) module support" + depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_GENERIC_DMAENGINE_PCM @@ -32,7 +33,6 @@ config SND_SOC_FSL_SAI config SND_SOC_FSL_MQS tristate "Medium Quality Sound (MQS) module support" depends on SND_SOC_FSL_SAI - depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV select REGMAP_MMIO help Say Y if you want to add Medium Quality Sound (MQS) @@ -309,6 +309,7 @@ config SND_SOC_IMX_SGTL5000 config SND_SOC_FSL_ASOC_CARD tristate "Generic ASoC Sound Card with ASRC support" + depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV depends on OF && I2C # enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m: depends on SND_AC97_CODEC || SND_AC97_CODEC=n @@ -330,6 +331,7 @@ config SND_SOC_FSL_ASOC_CARD config SND_SOC_IMX_AUDMIX tristate "SoC Audio support for i.MX boards with AUDMIX" + depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV select SND_SOC_FSL_AUDMIX select SND_SOC_FSL_SAI help @@ -339,6 +341,7 @@ config SND_SOC_IMX_AUDMIX config SND_SOC_IMX_HDMI tristate "SoC Audio support for i.MX boards with HDMI port" + depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV select SND_SOC_FSL_SAI select SND_SOC_FSL_AUD2HTX select SND_SOC_HDMI_CODEC @@ -364,6 +367,7 @@ config SND_SOC_IMX_RPMSG config SND_SOC_IMX_CARD tristate "SoC Audio Graph Sound Card support for i.MX boards" depends on OF && I2C + depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV select SND_SOC_AK4458 select SND_SOC_AK5558 select SND_SOC_IMX_PCM_DMA From c26d6cdade6c2a96049f24fac64a8e3734188703 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 28 Jan 2026 11:46:06 -0600 Subject: [PATCH 269/341] ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier Add a binding for the Anbernic RG-DS Amplifier, which is an Awinic aw87391 audio amplifier. This manufacturer did not provide firmware so we have to use a list of init commands instead, requiring device specific functionality rather than generic aw87391 functionality. Signed-off-by: Chris Morgan Acked-by: Conor Dooley Link: https://patch.msgid.link/20260128174608.1498-2-macroalpha82@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/awinic,aw87390.yaml | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml index ba9d8767c5d5..9c1baae767c4 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml @@ -15,12 +15,15 @@ description: sound quallity, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. -allOf: - - $ref: dai-common.yaml# - properties: compatible: - const: awinic,aw87390 + oneOf: + - enum: + - awinic,aw87390 + - items: + - enum: + - anbernic,rgds-amp + - const: awinic,aw87391 reg: maxItems: 1 @@ -40,10 +43,31 @@ required: - compatible - reg - "#sound-dai-cells" - - awinic,audio-channel unevaluatedProperties: false +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - awinic,aw87390 + then: + required: + - awinic,audio-channel + + - if: + properties: + compatible: + contains: + enum: + - anbernic,rgds-amp + then: + properties: + vdd-supply: true + examples: - | i2c { From a145cfd0ffe7bd7d61ce25839cec737c449b0d2c Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 28 Jan 2026 11:46:07 -0600 Subject: [PATCH 270/341] ASoC: codecs: aw87390: Add Anbernic RG-DS amp driver Add support for Anbernic's RG-DS audio amplifiers, powered by Awinic AW87391 amplifier ICs. These chips typically require an init sequence provided by firmware, but the manufacturer did not provide firmware in this case. As a result we had to hard-code the init sequence and use a device specific binding (rather than a binding just for the aw87391). Signed-off-by: Chris Morgan Link: https://patch.msgid.link/20260128174608.1498-3-macroalpha82@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw87390.c | 175 +++++++++++++++++++++++++++++++++++-- sound/soc/codecs/aw87390.h | 86 ++++++++++++++++++ 2 files changed, 253 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index d7fd865c349f..613daccca3af 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -314,6 +314,45 @@ static int aw87390_drv_event(struct snd_soc_dapm_widget *w, return ret; } +static int aw87391_rgds_drv_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 aw87390 *aw87390 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw87390->aw_pa; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!IS_ERR(aw87390->vdd_reg)) { + if (regulator_enable(aw87390->vdd_reg)) + dev_warn(aw_dev->dev, "Failed to enable vdd\n"); + } + break; + case SND_SOC_DAPM_POST_PMU: + regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG, + AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP | + AW87391_REG_EN_2X | AW87391_EN_SPK | + AW87391_EN_PA | AW87391_REG_EN_CP | + AW87391_EN_SW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, + AW87390_POWER_DOWN_VALUE); + break; + case SND_SOC_DAPM_POST_PMD: + if (!IS_ERR(aw87390->vdd_reg)) { + if (regulator_disable(aw87390->vdd_reg)) + dev_warn(aw_dev->dev, "Failed to disable vdd\n"); + } + break; + default: + dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event); + return -EINVAL; + } + + return 0; +} + static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN"), SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event, @@ -321,6 +360,14 @@ static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUT"), }; +static const struct snd_soc_dapm_widget aw87391_rgds_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87391_rgds_drv_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + static const struct snd_soc_dapm_route aw87390_dapm_routes[] = { { "SPK PA", NULL, "IN" }, { "OUT", NULL, "SPK PA" }, @@ -339,6 +386,80 @@ static int aw87390_codec_probe(struct snd_soc_component *component) return 0; } +/* + * Firmware typically is used to load the sequence of init commands, + * however for the Anbernic RG-DS we don't have a firmware file just + * a list of registers and values. Most of these values are undocumented + * in the AW87391 datasheet. + */ +static void aw87391_rgds_codec_init(struct aw87390 *aw87390) +{ + struct aw_device *aw_dev = aw87390->aw_pa; + + /* Undocumented command per datasheet. */ + regmap_write(aw_dev->regmap, 0x64, 0x3a); + + /* Bits 7:4 are undocumented but provided by manufacturer. */ + regmap_write(aw_dev->regmap, AW87391_CP_REG, + (5 << 4) | AW87391_REG_CP_OVP_8_50V); + + regmap_write(aw_dev->regmap, AW87391_AGCPO_REG, + AW87391_AK1_S_016 | AW87391_AGC2PO_MW(500)); + + regmap_write(aw_dev->regmap, AW87391_AGC2PA_REG, + AW87391_RK_S_20_48 | AW87391_AK2_S_41 | AW87391_AK2F_S_41); + + /* Undocumented commands per datasheet. */ + regmap_write(aw_dev->regmap, 0x5d, 0x00); + regmap_write(aw_dev->regmap, 0x5e, 0xb4); + regmap_write(aw_dev->regmap, 0x5f, 0x30); + regmap_write(aw_dev->regmap, 0x60, 0x39); + regmap_write(aw_dev->regmap, 0x61, 0x10); + regmap_write(aw_dev->regmap, 0x62, 0x03); + regmap_write(aw_dev->regmap, 0x63, 0x7d); + regmap_write(aw_dev->regmap, 0x65, 0xa0); + regmap_write(aw_dev->regmap, 0x66, 0x21); + regmap_write(aw_dev->regmap, 0x67, 0x41); + regmap_write(aw_dev->regmap, 0x68, 0x3b); + regmap_write(aw_dev->regmap, 0x6e, 0x00); + regmap_write(aw_dev->regmap, 0x6f, 0x00); + regmap_write(aw_dev->regmap, 0x70, 0x00); + regmap_write(aw_dev->regmap, 0x71, 0x00); + regmap_write(aw_dev->regmap, 0x72, 0x34); + regmap_write(aw_dev->regmap, 0x73, 0x06); + regmap_write(aw_dev->regmap, 0x74, 0x10); + regmap_write(aw_dev->regmap, 0x75, 0x00); + regmap_write(aw_dev->regmap, 0x7a, 0x00); + regmap_write(aw_dev->regmap, 0x7b, 0x00); + regmap_write(aw_dev->regmap, 0x7c, 0x00); + regmap_write(aw_dev->regmap, 0x7d, 0x00); + + regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_12DB); + regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG, + AW87391_EN_PA | AW87391_REG_EN_CP | AW87391_EN_SW); + regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG, + AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP | + AW87391_REG_EN_2X | AW87391_EN_SPK | AW87391_EN_PA | + AW87391_REG_EN_CP | AW87391_EN_SW); + regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_15DB); +} + +static int aw87391_rgds_codec_probe(struct snd_soc_component *component) +{ + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component); + + aw87390->vdd_reg = devm_regulator_get_optional(aw87390->aw_pa->dev, + "vdd"); + if (IS_ERR(aw87390->vdd_reg) && PTR_ERR(aw87390->vdd_reg) != -ENODEV) + return dev_err_probe(aw87390->aw_pa->dev, + PTR_ERR(aw87390->vdd_reg), + "Could not get vdd regulator\n"); + + aw87391_rgds_codec_init(aw87390); + + return 0; +} + static const struct snd_soc_component_driver soc_codec_dev_aw87390 = { .probe = aw87390_codec_probe, .dapm_widgets = aw87390_dapm_widgets, @@ -349,6 +470,14 @@ static const struct snd_soc_component_driver soc_codec_dev_aw87390 = { .num_controls = ARRAY_SIZE(aw87390_controls), }; +static const struct snd_soc_component_driver soc_codec_dev_anbernic_rgds = { + .probe = aw87391_rgds_codec_probe, + .dapm_widgets = aw87391_rgds_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw87391_rgds_dapm_widgets), + .dapm_routes = aw87390_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes), +}; + static void aw87390_parse_channel_dt(struct aw87390 *aw87390) { struct aw_device *aw_dev = aw87390->aw_pa; @@ -366,6 +495,10 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct unsigned int chip_id; int ret; + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + /* read chip id */ ret = regmap_read(regmap, AW87390_ID_REG, &chip_id); if (ret) { @@ -373,22 +506,24 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct return ret; } - if (chip_id != AW87390_CHIP_ID) { + switch (chip_id) { + case AW87390_CHIP_ID: + aw_dev->chip_id = AW87390_CHIP_ID; + break; + case AW87391_CHIP_ID: + aw_dev->chip_id = AW87391_CHIP_ID; + break; + default: dev_err(&i2c->dev, "unsupported device\n"); return -ENXIO; } dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id); - aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); - if (!aw_dev) - return -ENOMEM; - aw87390->aw_pa = aw_dev; aw_dev->i2c = i2c; aw_dev->regmap = regmap; aw_dev->dev = &i2c->dev; - aw_dev->chip_id = AW87390_CHIP_ID; aw_dev->acf = NULL; aw_dev->prof_info.prof_desc = NULL; aw_dev->prof_info.count = 0; @@ -406,6 +541,7 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct static int aw87390_i2c_probe(struct i2c_client *i2c) { struct aw87390 *aw87390; + const struct snd_soc_component_driver *priv; int ret; ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); @@ -434,16 +570,38 @@ static int aw87390_i2c_probe(struct i2c_client *i2c) if (ret) return ret; - ret = devm_snd_soc_register_component(&i2c->dev, - &soc_codec_dev_aw87390, NULL, 0); + switch (aw87390->aw_pa->chip_id) { + case AW87390_CHIP_ID: + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw87390, NULL, 0); + break; + case AW87391_CHIP_ID: + priv = of_device_get_match_data(&i2c->dev); + if (!priv) + return dev_err_probe(&i2c->dev, -EINVAL, + "aw87391 not currently supported\n"); + ret = devm_snd_soc_register_component(&i2c->dev, priv, NULL, 0); + break; + default: + return -ENXIO; + } + if (ret) dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret); return ret; } +static const struct of_device_id aw87390_of_match[] = { + { .compatible = "awinic,aw87390" }, + { .compatible = "anbernic,rgds-amp", .data = &soc_codec_dev_anbernic_rgds }, + {}, +}; +MODULE_DEVICE_TABLE(of, aw87390_of_match); + static const struct i2c_device_id aw87390_i2c_id[] = { { AW87390_I2C_NAME }, + { AW87391_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); @@ -451,6 +609,7 @@ MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); static struct i2c_driver aw87390_i2c_driver = { .driver = { .name = AW87390_I2C_NAME, + .of_match_table = of_match_ptr(aw87390_of_match), }, .probe = aw87390_i2c_probe, .id_table = aw87390_i2c_id, diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h index d0d049e65991..f48b207e4bb4 100644 --- a/sound/soc/codecs/aw87390.h +++ b/sound/soc/codecs/aw87390.h @@ -52,6 +52,90 @@ #define AW87390_I2C_NAME "aw87390" #define AW87390_ACF_FILE "aw87390_acf.bin" +#define AW87391_SYSCTRL_REG (0x01) +#define AW87391_REG_VER_SEL_LOW (0 << 6) +#define AW87391_REG_VER_SEL_NORMAL (1 << 6) +#define AW87391_REG_VER_SEL_SUPER (2 << 6) +#define AW87391_REG_EN_ADAP BIT(5) +#define AW87391_REG_EN_2X BIT(4) +#define AW87391_EN_SPK BIT(3) +#define AW87391_EN_PA BIT(2) +#define AW87391_REG_EN_CP BIT(1) +#define AW87391_EN_SW BIT(0) + +#define AW87391_CP_REG (0x02) +#define AW87391_REG_CP_OVP_6_50V 0 +#define AW87391_REG_CP_OVP_6_75V 1 +#define AW87391_REG_CP_OVP_7_00V 2 +#define AW87391_REG_CP_OVP_7_25V 3 +#define AW87391_REG_CP_OVP_7_50V 4 +#define AW87391_REG_CP_OVP_7_75V 5 +#define AW87391_REG_CP_OVP_8_00V 6 +#define AW87391_REG_CP_OVP_8_25V 7 +#define AW87391_REG_CP_OVP_8_50V 8 + +#define AW87391_PAG_REG (0x03) +#define AW87391_GAIN_12DB 0 +#define AW87391_GAIN_15DB 1 +#define AW87391_GAIN_18DB 2 +#define AW87391_GAIN_21DB 3 +#define AW87391_GAIN_24DB 4 + +#define AW87391_AGCPO_REG (0x04) +#define AW87391_AK1_S_016 (2 << 5) +#define AW87391_AK1_S_032 (3 << 5) +#define AW87391_PD_AGC1_PWRDN BIT(4) +/* AGC2PO supports values between 500mW (0000) to 1600mW (1011) */ +#define AW87391_AGC2PO_MW(n) ((n / 100) - 5) + +#define AW87391_AGC2PA_REG (0x05) +#define AW87391_RK_S_5_12 (0 << 5) +#define AW87391_RK_S_10_24 (1 << 5) +#define AW87391_RK_S_20_48 (2 << 5) +#define AW87391_RK_S_41 (3 << 5) +#define AW87391_RK_S_82 (4 << 5) +#define AW87391_RK_S_164 (5 << 5) +#define AW87391_RK_S_328 (6 << 5) +#define AW87391_RK_S_656 (7 << 5) +#define AW87391_AK2_S_1_28 (0 << 2) +#define AW87391_AK2_S_2_56 (1 << 2) +#define AW87391_AK2_S_10_24 (2 << 2) +#define AW87391_AK2_S_41 (3 << 2) +#define AW87391_AK2_S_82 (4 << 2) +#define AW87391_AK2_S_164 (5 << 2) +#define AW87391_AK2_S_328 (6 << 2) +#define AW87391_AK2_S_656 (7 << 2) +#define AW87391_AK2F_S_10_24 0 +#define AW87391_AK2F_S_20_48 1 +#define AW87391_AK2F_S_41 2 +#define AW87391_AK2F_S_82 3 + +#define AW87391_SYSST_REG (0x06) +#define AW87391_UVLO BIT(7) +#define AW87391_OTN BIT(6) +#define AW87391_OC_FLAG BIT(5) +#define AW87391_ADAP_CP BIT(4) +#define AW87391_STARTOK BIT(3) +#define AW87391_CP_OVP BIT(2) +#define AW87391_PORN BIT(1) + +#define AW87391_SYSINT_REG (0x07) +#define AW87391_UVLOI BIT(7) +#define AW87391_ONTI BIT(6) +#define AW87391_OC_FLAGI BIT(5) +#define AW87391_ADAP_CPI BIT(4) +#define AW87391_STARTOKI BIT(3) +#define AW87391_CP_OVPI BIT(2) +#define AW87391_PORNI BIT(1) + +#define AW87391_DFT_THGEN0_REG (0x63) +#define AW87391_ADAPVTH_01W (0 << 2) +#define AW87391_ADAPVTH_02W (1 << 2) +#define AW87391_ADAPVTH_03W (2 << 2) +#define AW87391_ADAPVTH_04W (3 << 2) + +#define AW87391_I2C_NAME "aw87391" + #define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -63,6 +147,7 @@ enum aw87390_id { AW87390_CHIP_ID = 0x76, + AW87391_CHIP_ID = 0xc1, }; enum { @@ -80,6 +165,7 @@ struct aw87390 { struct mutex lock; struct regmap *regmap; struct aw_container *aw_cfg; + struct regulator *vdd_reg; }; #endif From 4c6b74d58766ce7cd66ae8e14babf877039adef6 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Sat, 31 Jan 2026 00:00:13 +0800 Subject: [PATCH 271/341] ASoC: es8328: Fix DAC deemphasis control handling The DAC deemphasis control updated the hardware before updating the cached state, causing the previous setting to be applied. Update the cached deemphasis state first and then apply the setting. Also check and propagate errors from es8328_set_deemph() in hw_params(). Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260130160017.2630-2-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 38340f292282..46868b7924a0 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -163,12 +163,11 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, if (es8328->deemph == deemph) return 0; + es8328->deemph = deemph; ret = es8328_set_deemph(component); if (ret < 0) return ret; - es8328->deemph = deemph; - return 1; } @@ -530,7 +529,9 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, return ret; es8328->playback_fs = params_rate(params); - es8328_set_deemph(component); + ret = es8328_set_deemph(component); + if (ret < 0) + return ret; } else { ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, ES8328_ADCCONTROL4_ADCWL_MASK, From 0801a03a317b4848ca8c10a98f9b84028b7c8fc6 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Sat, 31 Jan 2026 00:00:14 +0800 Subject: [PATCH 272/341] ASoC: es8328: Propagate errors in set_bias_level() Register writes and updates in set_bias_level() ignored return values, potentially masking I/O failures during bias level transitions. Check and propagate errors from component register writes and updates. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260130160017.2630-3-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 60 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 46868b7924a0..98fc798ff565 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -647,6 +647,7 @@ static int es8328_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -654,43 +655,56 @@ static int es8328_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_PREPARE: /* VREF, VMID=2x50k, digital enabled */ - snd_soc_component_write(component, ES8328_CHIPPOWER, 0); - snd_soc_component_update_bits(component, ES8328_CONTROL1, - ES8328_CONTROL1_VMIDSEL_MASK | - ES8328_CONTROL1_ENREF, - ES8328_CONTROL1_VMIDSEL_50k | - ES8328_CONTROL1_ENREF); + ret = snd_soc_component_write(component, ES8328_CHIPPOWER, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_50k | + ES8328_CONTROL1_ENREF); + if (ret < 0) + return ret; break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - snd_soc_component_update_bits(component, ES8328_CONTROL1, - ES8328_CONTROL1_VMIDSEL_MASK | - ES8328_CONTROL1_ENREF, - ES8328_CONTROL1_VMIDSEL_5k | - ES8328_CONTROL1_ENREF); + ret = snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_5k | + ES8328_CONTROL1_ENREF); + if (ret < 0) + return ret; /* Charge caps */ msleep(100); } - snd_soc_component_write(component, ES8328_CONTROL2, - ES8328_CONTROL2_OVERCURRENT_ON | - ES8328_CONTROL2_THERMAL_SHUTDOWN_ON); + ret = snd_soc_component_write(component, ES8328_CONTROL2, + ES8328_CONTROL2_OVERCURRENT_ON | + ES8328_CONTROL2_THERMAL_SHUTDOWN_ON); + if (ret < 0) + return ret; /* VREF, VMID=2*500k, digital stopped */ - snd_soc_component_update_bits(component, ES8328_CONTROL1, - ES8328_CONTROL1_VMIDSEL_MASK | - ES8328_CONTROL1_ENREF, - ES8328_CONTROL1_VMIDSEL_500k | - ES8328_CONTROL1_ENREF); + ret = snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_500k | + ES8328_CONTROL1_ENREF); + if (ret < 0) + return ret; break; case SND_SOC_BIAS_OFF: - snd_soc_component_update_bits(component, ES8328_CONTROL1, - ES8328_CONTROL1_VMIDSEL_MASK | - ES8328_CONTROL1_ENREF, - 0); + ret = snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + 0); + if (ret < 0) + return ret; break; } return 0; From 3570e8eef217e60178d23eb490a34d64249bee45 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Sat, 31 Jan 2026 00:00:15 +0800 Subject: [PATCH 273/341] ASoC: es8328: Check errors in set_dai_fmt() Check and propagate return values from snd_soc_component_update_bits() in es8328_set_dai_fmt(). This avoids silent failures when register updates fail and ensures the DAI format is not left in an inconsistent state. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260130160017.2630-4-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 98fc798ff565..a18c3fe22da5 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -592,21 +592,26 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + int ret; u8 dac_mode = 0; u8 adc_mode = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBP_CFP: /* Master serial port mode, with BCLK generated automatically */ - snd_soc_component_update_bits(component, ES8328_MASTERMODE, - ES8328_MASTERMODE_MSC, - ES8328_MASTERMODE_MSC); + ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, + ES8328_MASTERMODE_MSC); + if (ret < 0) + return ret; es8328->provider = true; break; case SND_SOC_DAIFMT_CBC_CFC: /* Slave serial port mode */ - snd_soc_component_update_bits(component, ES8328_MASTERMODE, - ES8328_MASTERMODE_MSC, 0); + ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, 0); + if (ret < 0) + return ret; es8328->provider = false; break; default: @@ -635,10 +640,17 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) return -EINVAL; - snd_soc_component_update_bits(component, ES8328_DACCONTROL1, - ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode); - snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, - ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); + ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACFORMAT_MASK, + dac_mode); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCFORMAT_MASK, + adc_mode); + if (ret < 0) + return ret; return 0; } From 9917d99f44231b531a1c704bbbd58059e78c2f59 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Sat, 31 Jan 2026 00:00:16 +0800 Subject: [PATCH 274/341] ASoC: es8328: Use cached regmap on resume Use the regmap stored in the driver private data when restoring the register cache on resume, instead of looking it up from the device. This keeps the resume path consistent with the regmap instance used by the driver and avoids relying on a separate dev_get_regmap() lookup. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260130160017.2630-5-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index a18c3fe22da5..7afc97c62587 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -771,12 +771,9 @@ static int es8328_suspend(struct snd_soc_component *component) static int es8328_resume(struct snd_soc_component *component) { - struct regmap *regmap = dev_get_regmap(component->dev, NULL); - struct es8328_priv *es8328; + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); int ret; - es8328 = snd_soc_component_get_drvdata(component); - ret = clk_prepare_enable(es8328->clk); if (ret) { dev_err(component->dev, "unable to enable clock\n"); @@ -790,8 +787,8 @@ static int es8328_resume(struct snd_soc_component *component) return ret; } - regcache_mark_dirty(regmap); - ret = regcache_sync(regmap); + regcache_mark_dirty(es8328->regmap); + ret = regcache_sync(es8328->regmap); if (ret) { dev_err(component->dev, "unable to sync regcache\n"); return ret; From 8232e6079ae6f8d3a61d87973cb427385aa469b9 Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Sat, 31 Jan 2026 00:00:17 +0800 Subject: [PATCH 275/341] ASoC: es8328: Add error unwind in resume Handle failures in the resume path by unwinding previously enabled resources. If enabling regulators or syncing the regcache fails, disable regulators and unprepare the clock to avoid leaking resources and leaving the device in a partially resumed state. Signed-off-by: Hsieh Hung-En Link: https://patch.msgid.link/20260130160017.2630-6-hungen3108@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 7afc97c62587..9838fe42cb6f 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -784,17 +784,23 @@ static int es8328_resume(struct snd_soc_component *component) es8328->supplies); if (ret) { dev_err(component->dev, "unable to enable regulators\n"); - return ret; + goto err_clk; } regcache_mark_dirty(es8328->regmap); ret = regcache_sync(es8328->regmap); if (ret) { dev_err(component->dev, "unable to sync regcache\n"); - return ret; + goto err_regulators; } return 0; + +err_regulators: + regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), es8328->supplies); +err_clk: + clk_disable_unprepare(es8328->clk); + return ret; } static int es8328_component_probe(struct snd_soc_component *component) From 9ba734901cfd67d1b2101cce803a62d422e81b67 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 27 Jan 2026 17:34:46 +0000 Subject: [PATCH 276/341] ASoC: amd: acp: Sort match table into most specific first Match tables should be sorted so that more complex combinations of device matches are before simpler combinations, with the single device matches at the end. Signed-off-by: Simon Trimmer Link: https://patch.msgid.link/50c385a7f64ccd75cabf49eddbc0ec6fe13f3252.1769534442.git.simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index c5f42bd79548..93eb01a00a0c 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -326,11 +326,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_rt722_l0_rt1320_l1, .drv_name = "amd_sdw", }, - { - .link_mask = BIT(0), - .links = acp70_rt722_only, - .drv_name = "amd_sdw", - }, { .link_mask = BIT(0) | BIT(1), .links = acp70_4_in_1_sdca, @@ -351,6 +346,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_cs35l56x4_l1, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(0), + .links = acp70_rt722_only, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(1), .links = acp70_alc712_vb_l1, From 5eb3fc48d37157fd83315bcbb5693269fc497fb7 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 27 Jan 2026 17:34:47 +0000 Subject: [PATCH 277/341] ASoC: amd: acp: Rename Cirrus Logic component match entries to include link and uid In preparation for having similar matches with multiple UIDs on the same bus rename the match entries to include the bus link and device uid numbers in their name using the lNuN convention. Signed-off-by: Simon Trimmer Link: https://patch.msgid.link/f8f7fab5131ea6080421812dcce3e9ffc5b936e0.1769534442.git.simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 93eb01a00a0c..44fab145aa59 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -187,7 +187,7 @@ static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { }, }; -static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { +static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = { { .adr = 0x00003001FA424301ull, .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), @@ -196,7 +196,7 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { } }; -static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = { +static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = { { .adr = 0x00013001FA424301ull, .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), @@ -229,7 +229,7 @@ static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = { }, }; -static const struct snd_soc_acpi_adr_device cs35l56x4_1_adr[] = { +static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = { { .adr = 0x00013301FA355601ull, .num_endpoints = 1, @@ -256,34 +256,34 @@ static const struct snd_soc_acpi_adr_device cs35l56x4_1_adr[] = { }, }; -static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1_cs35l56x4_l1[] = { +static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = { { .mask = BIT(1), - .num_adr = ARRAY_SIZE(cs42l43_1_cs35l56x4_1_adr), - .adr_d = cs42l43_1_cs35l56x4_1_adr, + .num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr), + .adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr, }, {} }; -static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0_cs35l56x4_l1[] = { +static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] = { { .mask = BIT(0), - .num_adr = ARRAY_SIZE(cs42l43_0_adr), - .adr_d = cs42l43_0_adr, + .num_adr = ARRAY_SIZE(cs42l43_l0u0_adr), + .adr_d = cs42l43_l0u0_adr, }, { .mask = BIT(1), - .num_adr = ARRAY_SIZE(cs35l56x4_1_adr), - .adr_d = cs35l56x4_1_adr, + .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr), + .adr_d = cs35l56x4_l1u3210_adr, }, {} }; -static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1[] = { +static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = { { .mask = BIT(1), - .num_adr = ARRAY_SIZE(cs35l56x4_1_adr), - .adr_d = cs35l56x4_1_adr, + .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr), + .adr_d = cs35l56x4_l1u3210_adr, }, {} }; @@ -333,17 +333,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { }, { .link_mask = BIT(0) | BIT(1), - .links = acp70_cs42l43_l0_cs35l56x4_l1, + .links = acp70_cs42l43_l0u0_cs35l56x4_l1u3210, .drv_name = "amd_sdw", }, { .link_mask = BIT(1), - .links = acp70_cs42l43_l1_cs35l56x4_l1, + .links = acp70_cs42l43_l1u0_cs35l56x4_l1u0123, .drv_name = "amd_sdw", }, { .link_mask = BIT(1), - .links = acp70_cs35l56x4_l1, + .links = acp70_cs35l56x4_l1u3210, .drv_name = "amd_sdw", }, { From 3de9cf4782492b19c9d8bdac309462027e18ab43 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 27 Jan 2026 17:34:48 +0000 Subject: [PATCH 278/341] ASoC: amd: acp: Sort Cirrus Logic match entries In preparation for adding more match entries sort the ones that are present. Signed-off-by: Simon Trimmer Link: https://patch.msgid.link/0a756390dfb7e928246d0f35c611f175e1311e55.1769534442.git.simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 66 ++++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 44fab145aa59..de8f5a2a8e9d 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -187,6 +187,33 @@ static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { }, }; +static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = { + { + .adr = 0x00013301FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00013201FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, + { + .adr = 0x00013101FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP3" + }, + { + .adr = 0x00013001FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP4" + }, +}; + static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = { { .adr = 0x00003001FA424301ull, @@ -229,38 +256,11 @@ static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] }, }; -static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = { - { - .adr = 0x00013301FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_l_endpoint, - .name_prefix = "AMP1" - }, - { - .adr = 0x00013201FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_r_endpoint, - .name_prefix = "AMP2" - }, - { - .adr = 0x00013101FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_2_endpoint, - .name_prefix = "AMP3" - }, - { - .adr = 0x00013001FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_3_endpoint, - .name_prefix = "AMP4" - }, -}; - -static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = { +static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = { { .mask = BIT(1), - .num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr), - .adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr, + .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr), + .adr_d = cs35l56x4_l1u3210_adr, }, {} }; @@ -279,11 +279,11 @@ static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] {} }; -static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = { +static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = { { .mask = BIT(1), - .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr), - .adr_d = cs35l56x4_l1u3210_adr, + .num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr), + .adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr, }, {} }; From ddd9bf2212ab80bb90fa986cd987c671de53bd44 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 27 Jan 2026 17:34:49 +0000 Subject: [PATCH 279/341] ASoC: amd: acp: Add ACP7.0 match entries for Cirrus Logic parts This adds some match entries for a few system configurations: cs42l45 link 1 UID 0 cs35l63 link 0 UID 0 cs35l63 link 0 UID 2 cs35l63 link 0 UID 4 cs35l63 link 0 UID 6 cs42l45 link 0 UID 0 cs35l63 link 1 UID 0 cs35l63 link 1 UID 1 cs42l45 link 0 UID 0 cs35l63 link 1 UID 1 cs35l63 link 1 UID 3 cs42l45 link 1 UID 0 cs35l63 link 0 UID 0 cs35l63 link 0 UID 1 cs35l63 link 0 UID 0 cs35l63 link 0 UID 2 cs35l63 link 0 UID 4 cs35l63 link 0 UID 6 cs42l43 link 0 UID 1 cs42l43b link 0 UID 1 cs42l45 link 0 UID 0 cs42l45 link 1 UID 0 Signed-off-by: Simon Trimmer Reviewed-by: Maciej Strozek Link: https://patch.msgid.link/6cac5670fd5bc14201d925584251d75e59307431.1769534442.git.simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 256 +++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index de8f5a2a8e9d..dd2b010efdaa 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -214,6 +214,78 @@ static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = { }, }; +static const struct snd_soc_acpi_adr_device cs35l63x2_l0u01_adr[] = { + { + .adr = 0x00003001FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003101FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l63x2_l1u01_adr[] = { + { + .adr = 0x00013001FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00013101FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l63x2_l1u13_adr[] = { + { + .adr = 0x00013101FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00013301FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l63x4_l0u0246_adr[] = { + { + .adr = 0x00003001FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003201FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, + { + .adr = 0x00003401FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP3" + }, + { + .adr = 0x00003601FA356301ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP4" + }, +}; + static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = { { .adr = 0x00003001FA424301ull, @@ -223,6 +295,24 @@ static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs42l43_l0u1_adr[] = { + { + .adr = 0x00003101FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + +static const struct snd_soc_acpi_adr_device cs42l43b_l0u1_adr[] = { + { + .adr = 0x00003101FA2A3B01ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = { { .adr = 0x00013001FA424301ull, @@ -256,6 +346,26 @@ static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] }, }; +static const struct snd_soc_acpi_adr_device cs42l45_l0u0_adr[] = { + { + .adr = 0x00003001FA424501ull, + /* Re-use endpoints, but cs42l45 has no speaker */ + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1, + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l45" + } +}; + +static const struct snd_soc_acpi_adr_device cs42l45_l1u0_adr[] = { + { + .adr = 0x00013001FA424501ull, + /* Re-use endpoints, but cs42l45 has no speaker */ + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1, + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l45" + } +}; + static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = { { .mask = BIT(1), @@ -265,6 +375,33 @@ static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = { {} }; +static const struct snd_soc_acpi_link_adr acp70_cs35l63x4_l0u0246[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr), + .adr_d = cs35l63x4_l0u0246_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_l0u1_adr), + .adr_d = cs42l43_l0u1_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l43b_l0u1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43b_l0u1_adr), + .adr_d = cs42l43b_l0u1_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] = { { .mask = BIT(0), @@ -288,6 +425,80 @@ static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] {} }; +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr), + .adr_d = cs42l45_l0u0_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u01[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr), + .adr_d = cs42l45_l0u0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l63x2_l1u01_adr), + .adr_d = cs35l63x2_l1u01_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u13[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr), + .adr_d = cs42l45_l0u0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l63x2_l1u13_adr), + .adr_d = cs35l63x2_l1u13_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr), + .adr_d = cs42l45_l1u0_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x2_l0u01[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr), + .adr_d = cs42l45_l1u0_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l63x2_l0u01_adr), + .adr_d = cs35l63x2_l0u01_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x4_l0u0246[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr), + .adr_d = cs42l45_l1u0_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr), + .adr_d = cs35l63x4_l0u0246_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr acp70_alc712_vb_l1[] = { { .mask = BIT(1), @@ -336,6 +547,26 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_cs42l43_l0u0_cs35l56x4_l1u3210, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_cs42l45_l1u0_cs35l63x4_l0u0246, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_cs42l45_l0u0_cs35l63x2_l1u01, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_cs42l45_l0u0_cs35l63x2_l1u13, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_cs42l45_l1u0_cs35l63x2_l0u01, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(1), .links = acp70_cs42l43_l1u0_cs35l56x4_l1u0123, @@ -346,11 +577,36 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_cs35l56x4_l1u3210, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(0), + .links = acp70_cs35l63x4_l0u0246, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(0), .links = acp70_rt722_only, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(0), + .links = acp70_cs42l43_l0u1, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0), + .links = acp70_cs42l43b_l0u1, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0), + .links = acp70_cs42l45_l0u0, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(1), + .links = acp70_cs42l45_l1u0, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(1), .links = acp70_alc712_vb_l1, From 090c8844e5d454a898183ec4a7be8a75681ed262 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 18:01:10 -0800 Subject: [PATCH 280/341] ASoC: wcd-mbhc-v2: remove unnecessary module_init/exit functions The wcd-mbhc-v2 driver has unnecessary empty module_init and module_exit functions. Remove them. Note that if a module_init function exists, a module_exit function must also exist; otherwise, the module cannot be unloaded. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260131020111.46108-1-enelsonmoore@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 26ebcdadeb7d..0c842aaa7eec 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1631,17 +1631,5 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) } EXPORT_SYMBOL(wcd_mbhc_deinit); -static int __init mbhc_init(void) -{ - return 0; -} - -static void __exit mbhc_exit(void) -{ -} - -module_init(mbhc_init); -module_exit(mbhc_exit); - MODULE_DESCRIPTION("wcd MBHC v2 module"); MODULE_LICENSE("GPL"); From 8a066a81ee0c1b6cdbd81393536c3b2d19ccef25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Fri, 2 Jan 2026 22:52:25 +0100 Subject: [PATCH 281/341] ASoC: qcom: q6asm: drop DSP responses for closed data streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'Commit a354f030dbce ("ASoC: qcom: q6asm: handle the responses after closing")' attempted to ignore DSP responses arriving after a stream had been closed. However, those responses were still handled, causing lockups. Fix this by unconditionally dropping all DSP responses associated with closed data streams. Signed-off-by: Cédric Bellegarde Link: https://patch.msgid.link/20260102215225.609166-1-cedric.bellegarde@adishatz.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index d76d29fa0099..1bb295407c60 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -636,7 +636,6 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE; break; case ASM_STREAM_CMD_OPEN_WRITE_V3: - case ASM_DATA_CMD_WRITE_V2: case ASM_STREAM_CMD_OPEN_READ_V3: case ASM_STREAM_CMD_OPEN_READWRITE_V2: case ASM_STREAM_CMD_SET_ENCDEC_PARAM: @@ -655,8 +654,9 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, break; case ASM_DATA_CMD_EOS: case ASM_DATA_CMD_READ_V2: + case ASM_DATA_CMD_WRITE_V2: /* response as result of close stream */ - break; + goto done; default: dev_err(ac->dev, "command[0x%x] not expecting rsp\n", result->opcode); From 89e1d632bb29e0915d801629d4a842ff609514e2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 3 Feb 2026 19:40:25 +0800 Subject: [PATCH 282/341] ASoC: SOF: Intel: add hda_dsp_stream_pair_get/put helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, hda_dsp_stream_get/put are used to get/put the host dma. However, we may want to use a hda stream that both host and link dma are available. Add helper to find the hda stream and reserve/release it. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260203114027.3742558-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 41 +++++++++++++++++++++++++++++--- sound/soc/sof/intel/hda.h | 3 +++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 8fdaf1fdc338..36b647d987fc 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -210,8 +210,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, } /* get next unused stream */ -struct hdac_ext_stream * -hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) +static struct hdac_ext_stream * +_hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair) { const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -233,7 +233,14 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) if (hda_stream->host_reserved) continue; + if (pair && hext_stream->link_locked) + continue; + s->opened = true; + + if (pair) + hext_stream->link_locked = true; + break; } } @@ -264,14 +271,27 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) return hext_stream; } +struct hdac_ext_stream * +hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) +{ + return _hda_dsp_stream_get(sdev, direction, flags, false); +} + +struct hdac_ext_stream * +hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags) +{ + return _hda_dsp_stream_get(sdev, direction, flags, true); +} + /* free a stream */ -int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair) { const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *hda_stream; struct hdac_ext_stream *hext_stream; + struct hdac_ext_stream *link_stream; struct hdac_stream *s; bool dmi_l1_enable = true; bool found = false; @@ -292,6 +312,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) if (s->direction == direction && s->stream_tag == stream_tag) { s->opened = false; found = true; + if (pair) + link_stream = hext_stream; } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { dmi_l1_enable = false; } @@ -312,9 +334,22 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) return -ENODEV; } + if (pair) + snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); + return 0; } +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +{ + return _hda_dsp_stream_put(sdev, direction, stream_tag, false); +} + +int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +{ + return _hda_dsp_stream_put(sdev, direction, stream_tag, true); +} + static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) { int sd_offset = SOF_STREAM_SD_OFFSET(hstream); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 3be39c229c9f..9234e73f30fb 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -694,7 +694,10 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); +struct hdac_ext_stream * + hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags); int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); +int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, int enable, u32 size); From 8834ae896bfe10f239d49adb9cc76bb6a57c431c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 3 Feb 2026 19:40:26 +0800 Subject: [PATCH 283/341] ASoC: SOF: Intel: add hda_dma_prepare/cleanup helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SoundWire BPT stream needs to use link and host DMAs. Thus we need helpers to prepare and cleanup the link and host DMAs. Currently the SoundWire BPT stream uses hda_cl_prepare/cleanup helpers. It works fine because we assume the SwoundWire BPT will not run with audio streams simultaneously. The new helpers are copied from hda_cl_prepare/cleanup and add a flag to reserve the paired host and link DMAs. The new helpers will be used by both code loader and SoundWire BPT. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260203114027.3742558-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 94 +------------------------ sound/soc/sof/intel/hda-stream.c | 116 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 8 +++ 3 files changed, 127 insertions(+), 91 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 2cc11d8b0f70..2b3abcf75d55 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -53,65 +53,8 @@ hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, bool is_iccmax) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_ext_stream *hext_stream; - struct hdac_stream *hstream; - int ret; - - hext_stream = hda_dsp_stream_get(sdev, direction, 0); - - if (!hext_stream) { - dev_err(sdev->dev, "error: no stream available\n"); - return ERR_PTR(-ENODEV); - } - hstream = &hext_stream->hstream; - hstream->substream = NULL; - - /* - * Allocate DMA buffer if it is temporary or if the buffer is intended - * to be persistent but not yet allocated. - * We cannot rely solely on !dmab->area as caller might use a struct on - * stack (when it is temporary) without clearing it to 0. - */ - if (!persistent_buffer || !dmab->area) { - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); - if (ret < 0) { - dev_err(sdev->dev, "%s: memory alloc failed: %d\n", - __func__, ret); - goto out_put; - } - } - - hstream->period_bytes = 0;/* initialize period_bytes */ - hstream->format_val = format; - hstream->bufsize = size; - - if (is_iccmax) { - ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); - goto out_free; - } - } else { - ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); - goto out_free; - } - hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); - } - - return hext_stream; - -out_free: - snd_dma_free_pages(dmab); - dmab->area = NULL; - dmab->bytes = 0; - hstream->bufsize = 0; - hstream->format_val = 0; -out_put: - hda_dsp_stream_put(sdev, direction, hstream->stream_tag); - return ERR_PTR(ret); + return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer, + direction, is_iccmax, false); } EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -275,38 +218,7 @@ 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) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_stream *hstream = &hext_stream->hstream; - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret = 0; - - if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - else - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, - SOF_HDA_SD_CTL_DMA_START, 0); - - hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); - hstream->running = 0; - hstream->substream = NULL; - - /* reset BDL address */ - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); - - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); - - if (!persistent_buffer) { - snd_dma_free_pages(dmab); - dmab->area = NULL; - dmab->bytes = 0; - hstream->bufsize = 0; - hstream->format_val = 0; - } - - return ret; + return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); } EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 36b647d987fc..1c04b5d9c0d8 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1243,3 +1243,119 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, return ((u64)ldp_u << 32) | ldp_l; } EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +struct hdac_ext_stream * +hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, + bool is_iccmax, bool pair) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_ext_stream *hext_stream; + struct hdac_stream *hstream; + int ret; + + if (pair) + hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0); + else + hext_stream = hda_dsp_stream_get(sdev, direction, 0); + + if (!hext_stream) { + dev_err(sdev->dev, "%s: no stream available\n", __func__); + return ERR_PTR(-ENODEV); + } + hstream = &hext_stream->hstream; + hstream->substream = NULL; + + /* + * Allocate DMA buffer if it is temporary or if the buffer is intended + * to be persistent but not yet allocated. + * We cannot rely solely on !dmab->area as caller might use a struct on + * stack (when it is temporary) without clearing it to 0. + */ + if (!persistent_buffer || !dmab->area) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); + if (ret < 0) { + dev_err(sdev->dev, "%s: memory alloc failed: %d\n", + __func__, ret); + goto out_put; + } + } + + hstream->period_bytes = 0; /* initialize period_bytes */ + hstream->format_val = format; + hstream->bufsize = size; + + if (is_iccmax) { + ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n", + __func__, ret); + goto out_free; + } + } else { + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret); + goto out_free; + } + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); + } + + return hext_stream; + +out_free: + snd_dma_free_pages(dmab); + dmab->area = NULL; + dmab->bytes = 0; + hstream->bufsize = 0; + hstream->format_val = 0; +out_put: + if (pair) + hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag); + else + hda_dsp_stream_put(sdev, direction, hstream->stream_tag); + return ERR_PTR(ret); +} +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) +{ + 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) + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); + else + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); + + if (pair) + hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag); + else + hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); + + hstream->running = 0; + hstream->substream = NULL; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); + + if (!persistent_buffer) { + snd_dma_free_pages(dmab); + dmab->area = NULL; + dmab->bytes = 0; + hstream->bufsize = 0; + hstream->format_val = 0; + } + + return ret; +} +EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 9234e73f30fb..3fe00c269114 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -905,6 +905,14 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai); +struct hdac_ext_stream * +hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, + 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); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); From 330d1deb410fe75f7f79bd1e5025f2827365eb83 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 3 Feb 2026 19:40:27 +0800 Subject: [PATCH 284/341] ASoC: SOF: Intel: hda-sdw-bpt: support simultaneous audio and BPT streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the SoundWire BPT stream uses the paired link DMA but not reserve it. It works without any issue because we assume the SoundWire BPT will not run with audio streams simultaneously. To support simultaneous audio and BPT streams, we need to use the hda_dma_prepare/cleanup helpers to reserve the pair link host DMA. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260203114027.3742558-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-sdw-bpt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index ae2f8d55dbd0..728ffe7ae54d 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -118,7 +118,8 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream ** dev_dbg(dev, "direction %d format_val %#x\n", direction, format); - bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false); + bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl, + false, direction, false, true); if (IS_ERR(bpt_stream)) { dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n", __func__, direction); @@ -162,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream u32 mask; int ret; - ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream); + ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); if (ret < 0) { dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", __func__); From 023dfedec1478d506ba42d45f7993578fe38fa11 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 3 Feb 2026 19:15:45 +0800 Subject: [PATCH 285/341] ASoC: SOF: Intel: allow module parameter override BT link to 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code test if (bt_link_mask_override) to overwrite the BT link mask. This doesn't allow user to disable the BT link mask. User may want to disable the BT link when it is detected by the NHLT. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260203111545.3742255-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index d88ec66728fd..762100f7547f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -482,7 +482,7 @@ static int mclk_id_override = -1; module_param_named(mclk_id, mclk_id_override, int, 0444); MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id"); -static int bt_link_mask_override; +static int bt_link_mask_override = -1; module_param_named(bt_link_mask, bt_link_mask_override, int, 0444); MODULE_PARM_DESC(bt_link_mask, "SOF BT offload link mask"); @@ -1532,7 +1532,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) mach->mach_params.bt_link_mask); /* allow for module parameter override */ - if (bt_link_mask_override) { + if (bt_link_mask_override != -1) { dev_dbg(sdev->dev, "overriding BT link detected in NHLT tables %#x by kernel param %#x\n", mach->mach_params.bt_link_mask, bt_link_mask_override); mach->mach_params.bt_link_mask = bt_link_mask_override; From 754b3dade5ddbfd849e6ca9864cef45ce34cd7f6 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Tue, 3 Feb 2026 18:00:27 +0800 Subject: [PATCH 286/341] ASoC: Intel: soc-acpi-intel-ptl-match: drop rt721 related match tables Use functional topologies to support all RT721-related topology and amplifier combinations, e.g. sof-ptl-rt721.tplg, sof-ptl-rt721-l3-rt1320-l3.tplg. If these entries are not removed, they will all use the sof-ptl-rt721.tplg. Signed-off-by: Mac Chiang Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260203100027.3741754-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-ptl-match.c | 50 ------------------- 1 file changed, 50 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 1055fb4838f6..ddd919847c1f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -356,33 +356,6 @@ static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = { } }; -static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = { - { - .adr = 0x000030025d072101ull, - .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), - .endpoints = rt_mf_endpoints, - .name_prefix = "rt721" - } -}; - -static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = { - { - .adr = 0x000330025d072101ull, - .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), - .endpoints = rt_mf_endpoints, - .name_prefix = "rt721" - } -}; - -static const struct snd_soc_acpi_link_adr ptl_rt721_l3[] = { - { - .mask = BIT(3), - .num_adr = ARRAY_SIZE(rt721_3_single_adr), - .adr_d = rt721_3_single_adr, - }, - {}, -}; - static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = { { .adr = 0x000030025d072201ull, @@ -506,15 +479,6 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = { {} }; -static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = { - { - .mask = BIT(0), - .num_adr = ARRAY_SIZE(rt721_0_single_adr), - .adr_d = rt721_0_single_adr, - }, - {} -}; - static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = { { .mask = BIT(0), @@ -734,13 +698,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt711.tplg", }, - { - .link_mask = BIT(0), - .links = ptl_rt721_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt721.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, - }, { .link_mask = BIT(0), .links = ptl_rt722_only, @@ -763,13 +720,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", .get_function_tplg_files = sof_sdw_get_tplg_files, }, - { - .link_mask = BIT(3), - .links = ptl_rt721_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt721.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, - }, { .link_mask = BIT(3), .links = ptl_rt722_l3, From bb6a3c2db281c7d5aaa79b2a6fa00bcd10c0bb8f Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Tue, 3 Feb 2026 17:59:23 +0800 Subject: [PATCH 287/341] ASoC: SOF: Intel: hda: add SDCA property check If SDCA property is not present in the DisCo table, assume it is present. This allows DAI links to be created from codec_info_list instead of being skipped. Signed-off-by: Mac Chiang Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260203095923.3741674-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 762100f7547f..9189fc4e6dfa 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1138,6 +1138,12 @@ static bool is_endpoint_present(struct sdw_slave *sdw_device, { int i; + /* If SDCA is not present, assume the endpoint is present */ + if (!sdw_device->sdca_data.interface_revision) { + dev_warn(&sdw_device->dev, "SDCA properties not found in BIOS\n"); + return true; + } + for (i = 0; i < sdw_device->sdca_data.num_functions; i++) { if (dai_type == dai_info->dais[i].dai_type) return true; From e3474301824926ecce1d45f2ede7ecdda9a35840 Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Mon, 2 Feb 2026 20:01:33 -0500 Subject: [PATCH 288/341] ALSA: hda/realtek: Add quirk for Minisforum V3 SE First, adding a generic quirk for Bass speaker DAC avoidance. This pattern (re-routing the bass speakers off of a DAC without volume control) seems common enough that having a "model" to match against and quickly use to verify may be worthwhile. The alc285_fixup_thinkpad_x1_gen7 routing was selected, amongst the different options, as it should allow tuning the ratio between both speaker set. The routing was verified using `hda-verb`, and picking either 0x00 or 0x01. Either routing made the volume of the bass speakers controllable. hda-verb /dev/snd/hwC1D0 0x17 SET_CONNECT_SEL 0x01 This likely will apply for the Minisforum V3, though there isn't a lot of information to confirm whether or not the identifiers are the same. This was verified on the Minisforum V3 SE, and the root cause (the bass speakers routing) was found out by using pink noise, and playing with the mixers. Signed-off-by: Samuel Dionne-Riel Link: https://patch.msgid.link/20260203010132.1981419-2-samuel@dionne-riel.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 3d5a977bb40a..249586ea78ca 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4034,6 +4034,7 @@ enum { ALC288_FIXUP_SURFACE_SWAP_DACS, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO, ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY, + ALC245_FIXUP_BASS_HP_DAC, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6515,6 +6516,11 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_lenovo_gpio2_mic_hotkey, }, + [ALC245_FIXUP_BASS_HP_DAC] = { + .type = HDA_FIXUP_FUNC, + /* Borrow the DAC routing selected for those Thinkpads */ + .v.func = alc285_fixup_thinkpad_x1_gen7, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7570,6 +7576,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(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), + 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), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7784,6 +7791,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"}, {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, + {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, {} }; #define ALC225_STANDARD_PINS \ From 236d5e66b54b8052f7f4ae2ef2aa174e85c04a2f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:24 +0200 Subject: [PATCH 289/341] ASoC: SOF: sof-audio: Add a new op in struct sof_ipc_tplg_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new host_config op in struct sof_ipc_tplg_ops and define it for IPC4. This will be used to configure the host widget during prepare after a suspend/resume or after an xrun. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 20 ++++++++++++++++++++ sound/soc/sof/sof-audio.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 1bc95e3e584c..d28aad71c7ed 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2004,6 +2004,25 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, return ret; } +static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_platform_stream_params *platform_params) +{ + struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + u32 host_dma_id = platform_params->stream_tag - 1; + + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); + return; + } + + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id); +} + static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -3929,4 +3948,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .dai_get_param = sof_ipc4_dai_get_param, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .link_setup = sof_ipc4_link_setup, + .host_config = sof_ipc4_host_config, }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 03c8dd29e071..8596de1e8b95 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -209,6 +209,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @host_config: Function pointer for setting the DMA ID for host widgets * @dai_get_param: Function pointer for getting the DAI parameter * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines @@ -230,6 +231,8 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_platform_stream_params *platform_params); int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); From f462697680aa15c55f642168d6694d996f8ae940 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:25 +0200 Subject: [PATCH 290/341] ASoC: SOF: pcm: Split up widget prepare and setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Widgets are set up in 2 steps, first ipc_prepare followed by the actual IPC sent to the DSP to set up the widget. Split these 2 steps to do the ipc_prepare during hw_params and the setting up in the prepare callback. This will allow for future modifications to pipeline set up to be split up between the FE and BE DAI prepare ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 81 +++++++++++++++++++++++++++++++-------- sound/soc/sof/sof-audio.c | 48 ++++++++++++++--------- sound/soc/sof/sof-audio.h | 7 ++++ 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 31879a11c33e..5b598d0940eb 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -88,9 +88,9 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run spcm->stream[dir].list = list; - ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir); if (ret < 0) { - spcm_err(spcm, dir, "Widget list set up failed\n"); + spcm_err(spcm, dir, "widget list prepare failed\n"); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); return ret; @@ -100,15 +100,30 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run return 0; } +static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, + int comp_id) +{ + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (comp_id == swidget->comp_id) + return swidget; + } + + return NULL; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - struct snd_sof_platform_stream_params platform_params = { 0 }; + struct snd_sof_platform_stream_params *platform_params; struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_widget *host_widget; struct snd_sof_pcm *spcm; int ret; @@ -144,7 +159,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, spcm->prepared[substream->stream] = false; } - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); + platform_params = &spcm->platform_params[substream->stream]; + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; @@ -152,12 +168,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, /* if this is a repeated hw_params without hw_free, skip setting up widgets */ if (!spcm->stream[substream->stream].list) { - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params, substream->stream); if (ret < 0) return ret; } + if (!sdev->dspless_mode_selected) { + int host_comp_id = spcm->stream[substream->stream].comp_id; + + host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id); + if (!host_widget) { + spcm_err(spcm, substream->stream, + "failed to find host widget with comp_id %d\n", host_comp_id); + return -EINVAL; + } + + /* set the host DMA ID */ + if (tplg_ops && tplg_ops->host_config) + tplg_ops->host_config(sdev, host_widget, platform_params); + } + /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); @@ -169,14 +200,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - if (pcm_ops && pcm_ops->hw_params) { - ret = pcm_ops->hw_params(component, substream, params, &platform_params); - if (ret < 0) - return ret; - } - - spcm->prepared[substream->stream] = true; - /* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params)); @@ -281,6 +304,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + /* unprepare and free the list of DAPM widgets */ + sof_widget_list_unprepare(sdev, spcm, substream->stream); + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); return ret; @@ -291,7 +317,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_platform_stream_params *platform_params; + struct snd_soc_dapm_widget_list *list; + struct snd_pcm_hw_params *params; struct snd_sof_pcm *spcm; + int dir = substream->stream; int ret; /* nothing to do for BE */ @@ -317,15 +348,33 @@ static int sof_pcm_prepare(struct snd_soc_component *component, return ret; } - /* set hw_params */ - ret = sof_pcm_hw_params(component, - substream, &spcm->params[substream->stream]); + ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); if (ret < 0) { spcm_err(spcm, substream->stream, "failed to set hw_params after resume\n"); return ret; } + list = spcm->stream[dir].list; + params = &spcm->params[substream->stream]; + platform_params = &spcm->platform_params[substream->stream]; + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n", + spcm->pcm.pcm_id, dir); + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + return ret; + } + + if (pcm_ops && pcm_ops->hw_params) { + ret = pcm_ops->hw_params(component, substream, params, platform_params); + if (ret < 0) + return ret; + } + + spcm->prepared[substream->stream] = true; + return 0; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index d55ee7343f8e..ac2d6660d2fa 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -660,6 +660,30 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; } +int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + int dir) +{ + /* + * Prepare widgets for set up. The prepare step is used to allocate memory, assign + * instance ID and pick the widget configuration based on the runtime PCM params. + */ + return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + dir, SOF_WIDGET_PREPARE); +} + +void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + + /* unprepare the widget */ + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + + snd_soc_dapm_dai_free_widgets(&list); + spcm->stream[dir].list = NULL; +} + int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, @@ -670,19 +694,10 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_soc_dapm_widget *widget; int i, ret; - /* nothing to set up */ - if (!list) + /* nothing to set up or setup has been already done */ + if (!list || spcm->setup_done[dir]) return 0; - /* - * Prepare widgets for set up. The prepare step is used to allocate memory, assign - * instance ID and pick the widget configuration based on the runtime PCM params. - */ - ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, - dir, SOF_WIDGET_PREPARE); - if (ret < 0) - return ret; - /* Set up is used to send the IPC to the DSP to create the widget */ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, SOF_WIDGET_SETUP); @@ -737,6 +752,8 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, } } + spcm->setup_done[dir] = true; + return 0; widget_free: @@ -754,18 +771,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int int ret; /* nothing to free */ - if (!list) + if (!list || !spcm->setup_done[dir]) return 0; /* send IPC to free widget in the DSP */ ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); - /* unprepare the widget */ - sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); - - snd_soc_dapm_dai_free_widgets(&list); - spcm->stream[dir].list = NULL; - + spcm->setup_done[dir] = false; pipeline_list->count = 0; return ret; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 8596de1e8b95..5f62a34582da 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -354,7 +354,9 @@ struct snd_sof_pcm { struct snd_sof_pcm_stream stream[2]; struct list_head list; /* list in sdev pcm list */ struct snd_pcm_hw_params params[2]; + struct snd_sof_platform_stream_params platform_params[2]; bool prepared[2]; /* PCM_PARAMS set successfully */ + bool setup_done[2]; /* the setup of the SOF PCM device is done */ bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */ /* Must be last - ends in a flex-array member. */ @@ -676,6 +678,11 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir); +int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + int dir); +void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); From af0bc3ac9a9e830cb52b718ecb237c4e76a466be Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:26 +0200 Subject: [PATCH 291/341] uapi: sound: sof: tokens: Add missing token for KCPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align with the firmware and add the missing token for pipeline kcps. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 5fa8ab5088e0..a68381a263eb 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -56,6 +56,7 @@ #define SOF_TKN_SCHED_LP_MODE 207 #define SOF_TKN_SCHED_MEM_USAGE 208 #define SOF_TKN_SCHED_USE_CHAIN_DMA 209 +#define SOF_TKN_SCHED_KCPS 210 /* volume */ #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 From e614fce9fea62dddf298550a61cb11abd5d84a02 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:27 +0200 Subject: [PATCH 292/341] ASoC: Intel: sof_sdw: Add a DAI link for loopback capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a DAI link for loopback capture as the last link to make sure the other DAI link ID's remain unaffected. It serves as a dummy DAI link to enable echo reference capture in the SDW topologies which do not have an actual backend capture DAI. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 50b838be24e9..ee34282828e4 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1186,6 +1186,34 @@ static int create_bt_dailinks(struct snd_soc_card *card, return 0; } +static int create_echoref_dailink(struct snd_soc_card *card, + struct snd_soc_dai_link **dai_links, int *be_id) +{ + struct device *dev = card->dev; + int ret; + char *name = devm_kasprintf(dev, GFP_KERNEL, "Loopback_Virtual"); + + if (!name) + return -ENOMEM; + + /* + * use dummy DAI names as this won't be connected to an actual DAI but just to establish a + * fe <-> be connection for loopback capture for echo reference + */ + ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name, + 0, 1, "Loopback Virtual Pin", "dummy", + snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name, + 1, NULL, NULL); + if (ret) + return ret; + + (*dai_links)++; + + dev_dbg(dev, "Added echo reference DAI link\n"); + + return 0; +} + static int sof_card_dai_links_create(struct snd_soc_card *card) { struct device *dev = card->dev; @@ -1294,8 +1322,12 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_end; } - /* allocate BE dailinks */ - num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; + /* + * allocate BE dailinks, add an extra DAI link for echo reference capture. + * This should be the last DAI link and it is expected both for monolithic + * and functional SOF topologies to support echo reference. + */ + num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1; dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); if (!dai_links) { ret = -ENOMEM; @@ -1344,6 +1376,13 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_end; } + /* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */ + ret = create_echoref_dailink(card, &dai_links, &be_id); + if (ret) { + dev_err(dev, "failed to create echo ref dai link: %d\n", ret); + goto err_end; + } + WARN_ON(codec_conf != card->codec_conf + card->num_configs); WARN_ON(dai_links != card->dai_link + card->num_links); From 15a55ec2f8b956d6aa0dd948c907e13db7978c6e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:28 +0200 Subject: [PATCH 293/341] ASoC: SOF: ipc4-topology: Add new tokens for pipeline direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse the pipeline direction from topology. The direction_valid token is required for backward-compatibility with older topologies that may not have the direction set for pipelines. This will be used when setting up pipelines to check if a pipeline is in the same direction as the requested params and skip those in the opposite direction like in the case of echo reference capture pipelines during playback. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 2 ++ sound/soc/sof/ipc4-topology.c | 12 ++++++++++-- sound/soc/sof/ipc4-topology.h | 4 ++++ sound/soc/sof/sof-audio.h | 5 +++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index a68381a263eb..f4a7baadb44d 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -57,6 +57,8 @@ #define SOF_TKN_SCHED_MEM_USAGE 208 #define SOF_TKN_SCHED_USE_CHAIN_DMA 209 #define SOF_TKN_SCHED_KCPS 210 +#define SOF_TKN_SCHED_DIRECTION 211 +#define SOF_TKN_SCHED_DIRECTION_VALID 212 /* volume */ #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index d28aad71c7ed..09aca52e020a 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -76,6 +76,10 @@ static const struct sof_topology_token ipc4_sched_tokens[] = { offsetof(struct sof_ipc4_pipeline, core_id)}, {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, priority)}, + {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_pipeline, direction)}, + {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct sof_ipc4_pipeline, direction_valid)}, }; static const struct sof_topology_token pipeline_tokens[] = { @@ -939,6 +943,10 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) swidget->core = pipeline->core_id; spipe->core_mask |= BIT(pipeline->core_id); + if (pipeline->direction_valid) { + spipe->direction = pipeline->direction; + spipe->direction_valid = true; + } if (pipeline->use_chain_dma) { dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); @@ -954,9 +962,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) goto err; } - dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n", + dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d direction %d\n", swidget->widget->name, swidget->pipeline_id, - pipeline->priority, pipeline->core_id, pipeline->lp_mode); + pipeline->priority, pipeline->core_id, pipeline->lp_mode, pipeline->direction); swidget->private = pipeline; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 9a028a59c553..a289c1d8f3ff 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -150,6 +150,8 @@ struct sof_ipc4_copier_config_set_sink_format { * @use_chain_dma: flag to indicate if the firmware shall use chained DMA * @msg: message structure for pipeline * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger + * @direction_valid: flag indicating if valid direction is set in topology + * @direction: pipeline direction set in topology if direction_valid is true */ struct sof_ipc4_pipeline { uint32_t priority; @@ -160,6 +162,8 @@ struct sof_ipc4_pipeline { bool use_chain_dma; struct sof_ipc4_msg msg; bool skip_during_fe_trigger; + bool direction_valid; + u32 direction; }; /** diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 5f62a34582da..36082e764bf9 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -512,6 +512,9 @@ struct snd_sof_widget { * @complete: flag used to indicate that pipeline set up is complete. * @core_mask: Mask containing target cores for all modules in the pipeline * @list: List item in sdev pipeline_list + * @direction_valid: flag indicating if the direction is set in topology + * @direction: pipeline direction set in topology, valid is direction_valid is true + * */ struct snd_sof_pipeline { struct snd_sof_widget *pipe_widget; @@ -520,6 +523,8 @@ struct snd_sof_pipeline { int complete; unsigned long core_mask; struct list_head list; + bool direction_valid; + u32 direction; }; /* ASoC SOF DAPM route */ From 42d0f8695db59b74e7e7db0c851e480362a8fdca Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:29 +0200 Subject: [PATCH 294/341] ASoC: SOF: ipc4-topology: Add support for process modules with no input pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A tone generator module can be a type of processing module with no input pins. Adjust the logic to set the reference params for selecting output format and the basecfg format based on the output format. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 50 +++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 09aca52e020a..622bffb50a1c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2753,12 +2753,14 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, int input_fmt_index = 0; int ret; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &process->base_config, - pipeline_params, - available_fmt); - if (input_fmt_index < 0) - return input_fmt_index; + if (available_fmt->num_input_formats) { + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &process->base_config, + pipeline_params, + available_fmt); + if (input_fmt_index < 0) + return input_fmt_index; + } /* Configure output audio format only if the module supports output */ if (available_fmt->num_output_formats) { @@ -2767,12 +2769,28 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels; int out_ref_valid_bits, out_ref_type; - in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; + if (available_fmt->num_input_formats) { + in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_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); + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = + SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_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); + } 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); + ret = sof_ipc4_get_sample_type(sdev, fe_params); + if (ret < 0) + return ret; + out_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; + } output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &process->base_config, @@ -2800,6 +2818,16 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, if (ret) return ret; } + + /* set base cfg to match the first output format if there are no input formats */ + if (!available_fmt->num_input_formats) { + struct sof_ipc4_audio_format *out_fmt; + + out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; + + /* copy output format */ + memcpy(&process->base_config.audio_fmt, out_fmt, sizeof(*out_fmt)); + } } sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, From 74b11b22b5942a1a9b65c39b8ba6642a6e1ff5d0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:30 +0200 Subject: [PATCH 295/341] ASoC: SOF: sof-audio: Traverse paths with aggregated DAI widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aggregated DAI widgets exist in topology for representation and are not actually initialized in the firmware. But in preparation for using this as a virtual DAI for loopback capture, make sure that we can traverse the path from an aggregated DAI widget to the host widget. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index ac2d6660d2fa..efb5ed7d2b13 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -13,6 +13,21 @@ #include "sof-audio.h" #include "ops.h" +/* + * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers + * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains + * 2 DAI's names alh-copier.SDW1.Playback.0 and alh-copier-SDW1.Playback.1. In this case, only the + * DAI alh-copier.SDW1.Playback.0 is set up in the firmware. The other DAI, + * alh-copier.SDW1.Playback.1 in topology is for the sake of completeness to show aggregation for + * the speaker amp and does not need any firmware configuration. + */ +static bool is_aggregated_dai(struct snd_sof_widget *swidget) +{ + return (WIDGET_IS_DAI(swidget->id) && + isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) && + swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); +} + static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, const char *func) { @@ -402,8 +417,9 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg if (is_virtual_widget(sdev, widget, __func__)) return; - /* skip if the widget is in use or if it is already unprepared */ - if (!swidget || !swidget->prepared || swidget->use_count > 0) + /* skip if the widget in use or already unprepared or is an aggregated DAI */ + if (!swidget || !swidget->prepared || swidget->use_count > 0 || + is_aggregated_dai(swidget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -449,6 +465,10 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) goto sink_prepare; + /* skip aggregated DAIs */ + if (is_aggregated_dai(swidget)) + goto sink_prepare; + /* prepare the source widget */ ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params, pipeline_params, dir); @@ -493,6 +513,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int dir, struct snd_sof_pcm *spcm) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_sof_widget *swidget; struct snd_soc_dapm_path *p; int err; int ret = 0; @@ -500,8 +521,11 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap if (is_virtual_widget(sdev, widget, __func__)) return 0; - if (widget->dobj.private) { - err = sof_widget_free(sdev, widget->dobj.private); + swidget = widget->dobj.private; + + /* no need to free aggregated DAI widgets */ + if (swidget && !is_aggregated_dai(swidget)) { + err = sof_widget_free(sdev, swidget); if (err < 0) ret = err; } @@ -545,7 +569,10 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d if (swidget) { int i; - ret = sof_widget_setup(sdev, widget->dobj.private); + if (is_aggregated_dai(swidget)) + goto sink_setup; + + ret = sof_widget_setup(sdev, swidget); if (ret < 0) return ret; From c4b37c21c75de7b37a3d839fa3dff63464caad1e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:31 +0200 Subject: [PATCH 296/341] ASoC: SOF: sof-audio: Add support for loopback capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An example of a DAI-less loopback pipeline would be the echo reference capture in the speaker playback path. This pipeline is set up as follows: Host(Playback) -> mixin -> mixout -> gain -> module-copier -> DAI | V Host(Capture) <- Process module <- virtual DAI In the above example, the virtual DAI exploits the concept of an aggregated DAI (one with a non-zero DAI ID) in topology to enable this pipeline to work with DPCM. A virtual DAI is a DAI widget with a non-zero DAI ID and hence is skipped when traversing the list of DAPM widgets during widget prepare/set/up/free/unprepare. The process module in the above pipeline generates 0's that are captured by the echo reference PCM. When the playback path is active, the process module acts as a passthrough module to allow the playback samples to be passthrough to the capture host. In order for these pipelines to work properly, the logic for setting/preparing/freeing/unpreparing the widgets needs to be amended to make sure that only the widgets that are in the pipeline in the same direction as the PCM being started are set up. For example, when the playback PCM is started, the capture pipeline widgets also show up in the list of connected DAPM widgets but they shouldn't be set up yet because the echo reference capture PCM hasn't been started yet. Alternatively, when the echo reference capture PCM is started, the playback pipeline widgets should not be setup. Finally, the last step needed to put this all together is the set the routes for widgets connecting the playback and the capture pipelines when both are active. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 152 +++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 52 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index efb5ed7d2b13..acf56607bc9c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -269,6 +269,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc is_virtual_widget(sdev, sink_widget->widget, __func__)) return 0; + /* skip route if source/sink widget is not set up */ + if (!src_widget->use_count || !sink_widget->use_count) + return 0; + /* find route matching source and sink widgets */ list_for_each_entry(sroute, &sdev->route_list, list) if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { @@ -297,10 +301,34 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; } +static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir) +{ + return swidget->spipe->direction == dir; +} + +static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) +{ + struct snd_sof_widget *src_widget = wsource->dobj.private; + struct snd_sof_widget *sink_widget = wsink->dobj.private; + + /* + * skip setting up route if source and sink are in different directions (ex. playback and + * echo ref) if the direction is set in topology. These will be set up later. It is enough + * to check if the direction_valid is set for one of the widgets as all widgets will have + * the direction set in topology if one is set. + */ + if (sink_widget->spipe && sink_widget->spipe->direction_valid && + !sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) + return 0; + + return sof_route_setup(sdev, wsource, wsink); +} + static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_dapm_widget *widget; struct snd_sof_route *sroute; struct snd_soc_dapm_path *p; @@ -323,7 +351,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->sink->dobj.private) { - ret = sof_route_setup(sdev, widget, p->sink); + ret = sof_set_up_same_dir_widget_routes(sdev, widget, + p->sink); if (ret < 0) return ret; } @@ -339,7 +368,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->source->dobj.private) { - ret = sof_route_setup(sdev, p->source, widget); + ret = sof_set_up_same_dir_widget_routes(sdev, p->source, + widget); if (ret < 0) return ret; } @@ -355,7 +385,6 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, */ list_for_each_entry(sroute, &sdev->route_list, list) { bool src_widget_in_dapm_list, sink_widget_in_dapm_list; - struct snd_sof_widget *swidget; if (sroute->setup) continue; @@ -364,40 +393,37 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget); /* - * if both source and sink are in the DAPM list, the route must already have been - * set up above. And if neither are in the DAPM list, the route shouldn't be - * handled now. + * no need to set up the route if both the source and sink widgets are not in the + * DAPM list */ - if (src_widget_in_dapm_list == sink_widget_in_dapm_list) + if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list) continue; /* - * At this point either the source widget or the sink widget is in the DAPM list - * with a route that might need to be set up. Check the use_count of the widget - * that is not in the DAPM list to confirm if it is in use currently before setting - * up the route. + * set up the route only if both the source and sink widgets are in the DAPM list + * but are in different directions. The ones in the same direction would already + * have been set up in the previous loop. */ - if (src_widget_in_dapm_list) - swidget = sroute->sink_widget; - else - swidget = sroute->src_widget; + if (src_widget_in_dapm_list && sink_widget_in_dapm_list) { + struct snd_sof_widget *src_widget, *sink_widget; - scoped_guard(mutex, &swidget->setup_mutex) { - if (!swidget->use_count) + src_widget = sroute->src_widget->widget->dobj.private; + sink_widget = sroute->sink_widget->widget->dobj.private; + + /* + * it is enough to check if the direction_valid is set for one of the + * widgets as all widgets will have the direction set in topology if one + * is set. + */ + if (src_widget && sink_widget && + src_widget->spipe && src_widget->spipe->direction_valid && + sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) continue; - - if (tplg_ops && tplg_ops->route_setup) { - /* - * this route will get freed when either the - * source widget or the sink widget is freed - * during hw_free - */ - ret = tplg_ops->route_setup(sdev, sroute); - if (!ret) - sroute->setup = true; - } } + ret = sof_route_setup(sdev, sroute->src_widget->widget, + sroute->sink_widget->widget); + if (ret < 0) return ret; } @@ -407,7 +433,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, static void sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list *list) + struct snd_soc_dapm_widget_list *list, int dir) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; @@ -417,9 +443,15 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg if (is_virtual_widget(sdev, widget, __func__)) return; - /* skip if the widget in use or already unprepared or is an aggregated DAI */ - if (!swidget || !swidget->prepared || swidget->use_count > 0 || - is_aggregated_dai(swidget)) + if (!swidget) + goto sink_unprepare; + + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return; + + /* skip widgets in use, those already unprepared or aggregated DAIs */ + if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -434,9 +466,10 @@ sink_unprepare: snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!widget_in_list(list, p->sink)) continue; + if (!p->walking && p->sink->dobj.private) { p->walking = true; - sof_unprepare_widgets_in_path(sdev, p->sink, list); + sof_unprepare_widgets_in_path(sdev, p->sink, list, dir); p->walking = false; } } @@ -458,15 +491,20 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget if (is_virtual_widget(sdev, widget, __func__)) return 0; + if (!swidget) + goto sink_prepare; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; if (!widget_ops) return 0; - if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) - goto sink_prepare; + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; - /* skip aggregated DAIs */ - if (is_aggregated_dai(swidget)) + /* skip widgets already prepared or aggregated DAI widgets*/ + if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || + is_aggregated_dai(swidget)) goto sink_prepare; /* prepare the source widget */ @@ -484,6 +522,7 @@ sink_prepare: snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!widget_in_list(list, p->sink)) continue; + if (!p->walking && p->sink->dobj.private) { p->walking = true; ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, @@ -513,7 +552,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int dir, struct snd_sof_pcm *spcm) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; - struct snd_sof_widget *swidget; + struct snd_sof_widget *swidget = widget->dobj.private; struct snd_soc_dapm_path *p; int err; int ret = 0; @@ -521,15 +560,21 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap if (is_virtual_widget(sdev, widget, __func__)) return 0; - swidget = widget->dobj.private; + if (!swidget) + goto sink_free; - /* no need to free aggregated DAI widgets */ - if (swidget && !is_aggregated_dai(swidget)) { - err = sof_widget_free(sdev, swidget); - if (err < 0) - ret = err; - } + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; + /* skip aggregated DAIs */ + if (is_aggregated_dai(swidget)) + goto sink_free; + + err = sof_widget_free(sdev, widget->dobj.private); + if (err < 0) + ret = err; +sink_free: /* free all widgets in the sink paths even in case of error to keep use counts balanced */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!p->walking) { @@ -569,6 +614,11 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d if (swidget) { int i; + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; + + /* skip aggregated DAIs */ if (is_aggregated_dai(swidget)) goto sink_setup; @@ -634,15 +684,13 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; for_each_dapm_widgets(list, i, widget) { - if (is_virtual_widget(sdev, widget, __func__)) - continue; - - /* starting widget for playback is AIF type */ + /* starting widget for playback is of AIF type */ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) continue; /* starting widget for capture is DAI type */ - if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out) + if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out && + widget->id != snd_soc_dapm_output) continue; switch (op) { @@ -672,7 +720,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, break; } case SOF_WIDGET_UNPREPARE: - sof_unprepare_widgets_in_path(sdev, widget, list); + sof_unprepare_widgets_in_path(sdev, widget, list, dir); break; default: dev_err(sdev->dev, "Invalid widget op %d\n", op); From 16c589567a956d46a7c1363af3f64de3d420af20 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:32 +0200 Subject: [PATCH 297/341] ASoC: SOF: Intel: hda: Fix NULL pointer dereference If there's a mismatch between the DAI links in the machine driver and the topology, it is possible that the playback/capture widget is not set, especially in the case of loopback capture for echo reference where we use the dummy DAI link. Return the error when the widget is not set to avoid a null pointer dereference like below when the topology is broken. RIP: 0010:hda_dai_get_ops.isra.0+0x14/0xa0 [snd_sof_intel_hda_common] Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Mateusz Redzynia Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 883d0d3bae9e..3c742d535133 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -70,12 +70,22 @@ static const struct hda_dai_widget_dma_ops * hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); - struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_widget *swidget; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; - sdev = widget_to_sdev(w); + /* + * this is unlikely if the topology and the machine driver DAI links match. + * But if there's a missing DAI link in topology, this will prevent a NULL pointer + * dereference later on. + */ + if (!w) { + dev_err(cpu_dai->dev, "%s: widget is NULL\n", __func__); + return NULL; + } + sdev = widget_to_sdev(w); + swidget = w->dobj.private; if (!swidget) { dev_err(sdev->dev, "%s: swidget is NULL\n", __func__); return NULL; From 6c52fda42066a87b76fd140e027280907071dd8a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Feb 2026 10:18:33 +0200 Subject: [PATCH 298/341] ASoC: SOF: Intel: hda: Add a virtual CPU DAI Add a virtual CPU DAI for loopback capture for echo reference implementation. We can't use the snd-soc-dummy-dai because it is already used for the bluetooth DAI link. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Mateusz Redzynia Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260204081833.16630-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 8 ++++++++ sound/soc/sof/intel/hda.h | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 3c742d535133..15faedeec16d 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -866,6 +866,14 @@ struct snd_soc_dai_driver skl_dai[] = { .channels_max = 4, }, }, +{ + /* Virtual CPU DAI for Echo reference */ + .name = "Loopback Virtual Pin", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) { .name = "iDisp1 Pin", diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 3fe00c269114..3f0966477ace 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -418,10 +418,10 @@ (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) /* Number of DAIs */ -#define SOF_SKL_NUM_DAIS_NOCODEC 8 +#define SOF_SKL_NUM_DAIS_NOCODEC 9 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) -#define SOF_SKL_NUM_DAIS 15 +#define SOF_SKL_NUM_DAIS 16 #else #define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC #endif From 19e45247288574d7875c2bfd3c9d3f4f61fd9d92 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 2 Feb 2026 19:56:21 +0900 Subject: [PATCH 299/341] ASoC: dt-bindings: fsl_rpmsg: Add compatible string for i.MX94 Add compatible string "fsl,imx94-rpmsg-audio" for i.MX94 platform, which is backward compatible with i.MX95. Set it to fall back to "fsl,imx95-rpmsg-audio". Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260202105622.39772-2-chancel.liu@nxp.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,rpmsg.yaml | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index 3d5d435c765b..48cd5fbeb8af 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -22,14 +22,19 @@ allOf: properties: compatible: - enum: - - fsl,imx7ulp-rpmsg-audio - - fsl,imx8mn-rpmsg-audio - - fsl,imx8mm-rpmsg-audio - - fsl,imx8mp-rpmsg-audio - - fsl,imx8ulp-rpmsg-audio - - fsl,imx93-rpmsg-audio - - fsl,imx95-rpmsg-audio + oneOf: + - enum: + - fsl,imx7ulp-rpmsg-audio + - fsl,imx8mn-rpmsg-audio + - fsl,imx8mm-rpmsg-audio + - fsl,imx8mp-rpmsg-audio + - fsl,imx8ulp-rpmsg-audio + - fsl,imx93-rpmsg-audio + - fsl,imx95-rpmsg-audio + - items: + - enum: + - fsl,imx94-rpmsg-audio + - const: fsl,imx95-rpmsg-audio clocks: items: From e50de21fd7ce54d34a461d5f5ba6331271be57fb Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 2 Feb 2026 19:56:22 +0900 Subject: [PATCH 300/341] ASoC: dt-bindings: fsl_rpmsg: Add compatible string for i.MX952 Add compatible string "fsl,imx952-rpmsg-audio" for i.MX952 platform, which is backward compatible with i.MX95. Set it to fall back to "fsl,imx95-rpmsg-audio". Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260202105622.39772-3-chancel.liu@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index 48cd5fbeb8af..3a32f7517d0c 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -34,6 +34,7 @@ properties: - items: - enum: - fsl,imx94-rpmsg-audio + - fsl,imx952-rpmsg-audio - const: fsl,imx95-rpmsg-audio clocks: From 55137f5a68b5e888504ad36d07221cd749bb8956 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Mon, 2 Feb 2026 18:27:56 +0800 Subject: [PATCH 301/341] ASoC: tas2781: Put three different calibrated data solution into the same data structure TAS2781 driver supports three solutions of calibrated data. The first is from the driver itself: driver reads the calibrated files directly during probe; The second is from user space: during init of audio hal, the audio hal will pass the calibrated data via kcontrol interface. Driver will store this data in "struct calidata" for use. The third is from UEFI, mainly used in hda device. These three solutions save the calibrated data into different data structures. It is time to put them together into "struct calidata" for use. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260202102757.532-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781.h | 3 +- sound/hda/codecs/side-codecs/tas2781_hda.c | 9 +- .../hda/codecs/side-codecs/tas2781_hda_i2c.c | 13 -- sound/soc/codecs/tas2781-fmwlib.c | 138 ++++++++++++++---- sound/soc/codecs/tas2781-i2c.c | 11 +- 5 files changed, 121 insertions(+), 53 deletions(-) diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 9d3c54cb8223..7c03bdc951bb 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2025 Texas Instruments Incorporated +// Copyright (C) 2022 - 2026 Texas Instruments Incorporated // https://www.ti.com // // The TAS2563/TAS2781 driver implements a flexible and configurable @@ -233,7 +233,6 @@ struct tasdevice_priv { bool playback_started; bool isacpi; bool isspi; - bool is_user_space_calidata; unsigned int global_addr; int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv, diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c index 96e6d82dc69e..b22f93424c62 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda.c @@ -2,7 +2,7 @@ // // TAS2781 HDA Shared Lib for I2C&SPI driver // -// Copyright 2025 Texas Instruments, Inc. +// Copyright 2025 - 2026 Texas Instruments, Inc. // // Author: Shenghao Ding @@ -159,7 +159,6 @@ static void tas2781_apply_calib(struct tasdevice_priv *p) r->tlimit_reg = cali_reg[4]; } - p->is_user_space_calidata = true; cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); } @@ -216,6 +215,12 @@ int tas2781_save_calibration(struct tas2781_hda *hda) status = -ENOMEM; continue; } + /* + * Set to an invalid value before the calibrated data + * is stored into it, for the default value is 0, which + * means the first device. + */ + data[0] = 0xff; /* Get variable contents into buffer */ status = efi.get_variable(efi_name[i], &efi_guid, &attr, &cali_data->total_sz, data); diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 624a822341bb..62ddaceaa8a5 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -393,19 +393,6 @@ static int tas2563_save_calibration(struct tas2781_hda *h) r->pow_reg = TAS2563_CAL_POWER; r->tlimit_reg = TAS2563_CAL_TLIM; - /* - * TAS2781_FMWLIB supports two solutions of calibrated data. One is - * from the driver itself: driver reads the calibrated files directly - * during probe; The other from user space: during init of audio hal, - * the audio hal will pass the calibrated data via kcontrol interface. - * Driver will store this data in "struct calidata" for use. For hda - * device, calibrated data are usunally saved into UEFI. So Hda side - * codec driver use the mixture of these two solutions, driver reads - * the data from UEFI, then store this data in "struct calidata" for - * use. - */ - p->is_user_space_calidata = true; - return 0; } diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 78fd0a5dc6f2..0e084c3a162d 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2,7 +2,7 @@ // // tas2781-fmwlib.c -- TASDEVICE firmware support // -// Copyright 2023 - 2025 Texas Instruments, Inc. +// Copyright 2023 - 2026 Texas Instruments, Inc. // // Author: Shenghao Ding // Author: Baojun Xu @@ -80,6 +80,14 @@ #define POST_SOFTWARE_RESET_DEVICE_C 0x47 #define POST_SOFTWARE_RESET_DEVICE_D 0x48 +#define COPY_CAL_DATA(i) \ + do { \ + calbin_data[i + 1] = data[7]; \ + calbin_data[i + 2] = data[8]; \ + calbin_data[i + 3] = data[9]; \ + calbin_data[i + 4] = data[10]; \ + } while (0) + struct tas_crc { unsigned char offset; unsigned char len; @@ -1952,23 +1960,6 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv, return rc; } -static int load_calib_data(struct tasdevice_priv *tas_priv, - struct tasdevice_data *dev_data) -{ - struct tasdev_blk *block; - unsigned int i; - int ret = 0; - - for (i = 0; i < dev_data->nr_blk; i++) { - block = &(dev_data->dev_blks[i]); - ret = tasdevice_load_block(tas_priv, block); - if (ret < 0) - break; - } - - return ret; -} - static int fw_parse_header(struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) { @@ -2029,6 +2020,103 @@ out: return offset; } +static inline int check_cal_bin_data(struct device *dev, + const unsigned char *data, const char *name) +{ + if (data[2] != 0x85 || data[1] != 4) { + dev_err(dev, "Invalid cal bin file in %s\n", name); + return -1; + } + return 0; +} + +static void calbin_conversion(struct tasdevice_priv *priv, + struct tasdevice_fw *tas_fmw) +{ + struct calidata *cali_data = &priv->cali_data; + unsigned char *calbin_data = cali_data->data; + struct cali_reg *p = &cali_data->cali_reg_array; + struct tasdevice_calibration *calibration; + struct tasdevice_data *img_data; + struct tasdev_blk *blk; + unsigned char *data; + int chn, k; + + if (cali_data->total_sz != priv->ndev * + (cali_data->cali_dat_sz_per_dev + 1)) { + dev_err(priv->dev, "%s: cali_data size err\n", + __func__); + return; + } + calibration = &(tas_fmw->calibrations[0]); + img_data = &(calibration->dev_data); + + if (img_data->nr_blk != 1) { + dev_err(priv->dev, "%s: Invalid nr_blk, wrong cal bin\n", + __func__); + return; + } + + blk = &(img_data->dev_blks[0]); + if (blk->nr_cmds != 15) { + dev_err(priv->dev, "%s: Invalid nr_cmds, wrong cal bin\n", + __func__); + return; + } + + switch (blk->type) { + case COEFF_DEVICE_A: + chn = 0; + break; + case COEFF_DEVICE_B: + chn = 1; + break; + case COEFF_DEVICE_C: + chn = 2; + break; + case COEFF_DEVICE_D: + chn = 3; + break; + default: + dev_err(priv->dev, "%s: Other Type = 0x%02x\n", + __func__, blk->type); + return; + } + k = chn * (cali_data->cali_dat_sz_per_dev + 1); + + data = blk->data; + if (check_cal_bin_data(priv->dev, data, "r0_reg") < 0) + return; + p->r0_reg = TASDEVICE_REG(data[4], data[5], data[6]); + COPY_CAL_DATA(k); + + data = blk->data + 12; + if (check_cal_bin_data(priv->dev, data, "r0_low_reg") < 0) + return; + p->r0_low_reg = TASDEVICE_REG(data[4], data[5], data[6]); + COPY_CAL_DATA(k + 4); + + data = blk->data + 24; + if (check_cal_bin_data(priv->dev, data, "invr0_reg") < 0) + return; + p->invr0_reg = TASDEVICE_REG(data[4], data[5], data[6]); + COPY_CAL_DATA(k + 8); + + data = blk->data + 36; + if (check_cal_bin_data(priv->dev, data, "pow_reg") < 0) + return; + p->pow_reg = TASDEVICE_REG(data[4], data[5], data[6]); + COPY_CAL_DATA(k + 12); + + data = blk->data + 48; + if (check_cal_bin_data(priv->dev, data, "tlimit_reg") < 0) + return; + p->tlimit_reg = TASDEVICE_REG(data[4], data[5], data[6]); + COPY_CAL_DATA(k + 16); + + calbin_data[k] = chn; +} + /* When calibrated data parsing error occurs, DSP can still work with default * calibrated data, memory resource related to calibrated data will be * released in the tasdevice_codec_remove. @@ -2086,6 +2174,7 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, goto out; } + calbin_conversion(tas_priv, tas_fmw); out: return offset; } @@ -2371,25 +2460,12 @@ static int tasdevice_load_data(struct tasdevice_priv *tas_priv, static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i) { - struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw; struct calidata *cali_data = &priv->cali_data; struct cali_reg *p = &cali_data->cali_reg_array; unsigned char *data = cali_data->data; - struct tasdevice_calibration *cal; int k = i * (cali_data->cali_dat_sz_per_dev + 1); int rc; - /* Load the calibrated data from cal bin file */ - if (!priv->is_user_space_calidata && cal_fmw) { - cal = cal_fmw->calibrations; - - if (cal) - load_calib_data(priv, &cal->dev_data); - return; - } - if (!priv->is_user_space_calidata) - return; - /* load calibrated data from user space */ if (data[k] != i) { dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n", __func__, i); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index d1c76ab0144d..41b89fcc69c3 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2025 Texas Instruments Incorporated +// Copyright (C) 2022 - 2026 Texas Instruments Incorporated // https://www.ti.com // // The TAS2563/TAS2781 driver implements a flexible and configurable @@ -255,8 +255,6 @@ static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol, int rc; guard(mutex)(&priv->codec_lock); - if (!priv->is_user_space_calidata) - return -1; if (!p->r0_reg) return -1; @@ -654,7 +652,6 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol, } } i += 2; - priv->is_user_space_calidata = true; if (priv->dspbin_typ == TASDEV_BASIC) { p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); @@ -1444,7 +1441,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) GFP_KERNEL); if (!cali_data->data) return -ENOMEM; - + /* + * Set to an invalid value before the calibrated data is stored into + * it, for the default value is 0, which means the first device. + */ + cali_data->data[0] = 0xff; if (priv->chip_id == TAS2781) { struct soc_bytes_ext *ext_cali_start; char *cali_start_name; From b27b57f85fe3f0eca479556ac55bc9cbd1a5685a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:38 +0000 Subject: [PATCH 302/341] ASoC: SDCA: Remove outdated todo comment Support for -cn- properties has already been added, however the TODO comment noting this feature was required was not removed. Remove the now redundant comment. Fixes: 50a479527ef01 ("ASoC: SDCA: Add support for -cn- value properties") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-2-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 80c71116e6d4..f97dde912d59 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -911,10 +911,6 @@ static int find_sdca_control_value(struct device *dev, struct sdca_entity *entit return 0; } -/* - * TODO: Add support for -cn- properties, allowing different channels to have - * different defaults etc. - */ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity, struct fwnode_handle *control_node, struct sdca_control *control) From 9fad74b79e5ff353fe156c4b685cceffa5afdb1d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:39 +0000 Subject: [PATCH 303/341] ASoC: SDCA: Handle volatile controls correctly There are very few volatile controls in SDCA that are exported as ALSA controls, typically Detected Mode is the only common one. However, the current code does not resume the device when these ALSA controls are accessed, which will result in the read/write failing. Add a new wrapper specifically for volatile controls that will do the required pm_runtime operations before accessing the register. Fixes: c3ca24e3fcb6 ("ASoC: SDCA: Create ALSA controls from DisCo") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-3-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 52 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 498aba9df5d9..9685281529e9 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -792,6 +793,48 @@ static int control_limit_kctl(struct device *dev, return 0; } +static int volatile_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct device *dev = component->dev; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to resume reading %s: %d\n", + kcontrol->id.name, ret); + return ret; + } + + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + pm_runtime_put(dev); + + return ret; +} + +static int volatile_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct device *dev = component->dev; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to resume writing %s: %d\n", + kcontrol->id.name, ret); + return ret; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + pm_runtime_put(dev); + + return ret; +} + static int populate_control(struct device *dev, struct sdca_function_data *function, struct sdca_entity *entity, @@ -849,8 +892,13 @@ static int populate_control(struct device *dev, (*kctl)->private_value = (unsigned long)mc; (*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER; (*kctl)->info = snd_soc_info_volsw; - (*kctl)->get = snd_soc_get_volsw; - (*kctl)->put = snd_soc_put_volsw; + if (control->is_volatile) { + (*kctl)->get = volatile_get_volsw; + (*kctl)->put = volatile_put_volsw; + } else { + (*kctl)->get = snd_soc_get_volsw; + (*kctl)->put = snd_soc_put_volsw; + } if (readonly_control(control)) (*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ; From d7730c44b7dddbc5063505ce9e0c21d8bf298368 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:40 +0000 Subject: [PATCH 304/341] ASoC: SDCA: Still process most of the jack detect if control is missing DAPM creates its controls very late in the card creation, so there is no call into the driver after the controls are created. This means the jack IRQs can't be guaranteed to be registered after the ALSA controls are available. If a jack IRQ is received before the controls are available, currently the driver does not update the Selected Mode as it is required by the specification to do. If the ALSA controls are not available update the Selected Mode directly rather than going through the ALSA control. The ALSA control should pick up the state once it is created. Fixes: b9ab3b618241 ("ASoC: SDCA: Add some initial IRQ handlers") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-4-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_jack.c | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index 5b9cf69cbcd6..bfa621b744e1 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -41,10 +41,11 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) struct jack_state *state = interrupt->priv; struct snd_kcontrol *kctl = state->kctl; struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; - struct soc_enum *soc_enum; unsigned int reg, val; int ret; + guard(rwsem_write)(rwsem); + if (!kctl) { const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", interrupt->entity->label, @@ -54,16 +55,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) return -ENOMEM; kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) { + if (!kctl) dev_dbg(dev, "control not found: %s\n", name); - return -ENOENT; - } - - state->kctl = kctl; + else + state->kctl = kctl; } - soc_enum = (struct soc_enum *)kctl->private_value; - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, interrupt->control->sel, 0); @@ -73,13 +70,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) return ret; } + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + SDCA_CTL_GE_SELECTED_MODE, 0); + switch (val) { case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS: case SDCA_DETECTED_MODE_JACK_UNKNOWN: - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, - interrupt->entity->id, - SDCA_CTL_GE_SELECTED_MODE, 0); - /* * Selected mode is not normally marked as volatile register * (RW), but here force a read from the hardware. If the @@ -100,22 +96,30 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) dev_dbg(dev, "%s: %#x\n", interrupt->name, val); - ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); - if (!ucontrol) - return -ENOMEM; + if (kctl) { + struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value; - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return -ENOMEM; - down_write(rwsem); - ret = kctl->put(kctl, ucontrol); - up_write(rwsem); - if (ret < 0) { - dev_err(dev, "failed to update selected mode: %d\n", ret); - return ret; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + + ret = kctl->put(kctl, ucontrol); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return ret; + } + + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + } else { + ret = regmap_write(interrupt->function_regmap, reg, val); + if (ret) { + dev_err(dev, "failed to write selected mode: %d\n", ret); + return ret; + } } - snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - return sdca_jack_report(interrupt); } EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); From cc2f22a61ad66fda7e50339f9ec33720694e0b20 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:41 +0000 Subject: [PATCH 305/341] ASoC: SDCA: Rearrange FDL file messages It is helpful to have something in the log showing which firmware file was loaded by the driver. Update the existing FDL disk file debug statement to just note that a disk file rather than ACPI file was used, and add a new info printk that prints out the details of the loaded file regardless of where that file came from. Likewise, sometimes it is useful to get a message if the file-sets list is missing, although this isn't technically an error so make it a debug. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-5-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_fdl.c | 6 ++++-- sound/soc/sdca/sdca_functions.c | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 8bee9f23c473..07892bc3a44e 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -256,8 +256,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, tmp->file_length != firmware->size) { dev_err(dev, "bad disk SWF size\n"); } else if (!swf || swf->file_version <= tmp->file_version) { - dev_dbg(dev, "using SWF from disk: %x-%x-%x\n", - tmp->vendor_id, tmp->file_id, tmp->file_version); + dev_dbg(dev, "using SWF from disk\n"); swf = tmp; } } @@ -267,6 +266,9 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, return -ENOENT; } + dev_info(dev, "loading SWF: %x-%x-%x\n", + swf->vendor_id, swf->file_id, swf->file_version); + ret = sdca_ump_write_message(dev, interrupt->device_regmap, interrupt->function_regmap, interrupt->function, interrupt->entity, diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index f97dde912d59..f38791eab4f1 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -2029,6 +2029,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, num_sets = fwnode_property_count_u32(function_node, "mipi-sdca-file-set-id-list"); if (num_sets == 0 || num_sets == -EINVAL) { + dev_dbg(dev, "%pfwP: file set id list missing\n", function_node); return 0; } else if (num_sets < 0) { dev_err(dev, "%pfwP: failed to read file set list: %d\n", From 02d851b46b366b69dfe0842ab45313d8a4d6c960 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:42 +0000 Subject: [PATCH 306/341] ASoC: SDCA: Add regmap defaults for specification defined values Some of the SDCA Controls have a defined reset value in the specification. Update the parsing to add these specification defined values into the regmap defaults array. This will reduce the number of registers that are synchronised on a cache sync. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-6-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 4 ++++ sound/soc/sdca/sdca_functions.c | 36 +++++++++++++++++++++++++++++++++ sound/soc/sdca/sdca_regmap.c | 14 ++++++++++--- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 6e9391b3816c..79bd5a7a0f88 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -798,6 +798,7 @@ struct sdca_control_range { * @sel: Identifier used for addressing. * @nbits: Number of bits used in the Control. * @values: Holds the Control value for constants and defaults. + * @reset: Defined reset value for the Control. * @cn_list: A bitmask showing the valid Control Numbers within this Control, * Control Numbers typically represent channels. * @interrupt_position: SCDA interrupt line that will alert to changes on this @@ -808,6 +809,7 @@ struct sdca_control_range { * @layers: Bitmask of access layers of the Control. * @deferrable: Indicates if the access to the Control can be deferred. * @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. */ struct sdca_control { @@ -816,6 +818,7 @@ struct sdca_control { int nbits; int *values; + int reset; u64 cn_list; int interrupt_position; @@ -827,6 +830,7 @@ struct sdca_control { bool deferrable; bool is_volatile; bool has_default; + bool has_reset; bool has_fixed; }; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index f38791eab4f1..95b67bb904c3 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -911,6 +911,38 @@ static int find_sdca_control_value(struct device *dev, struct sdca_entity *entit return 0; } +static int find_sdca_control_reset(const struct sdca_entity *entity, + struct sdca_control *control) +{ + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(FU, AGC): + case SDCA_CTL_TYPE_S(FU, BASS_BOOST): + case SDCA_CTL_TYPE_S(FU, LOUDNESS): + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE): + case SDCA_CTL_TYPE_S(GE, SELECTED_MODE): + case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER): + case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK): + control->has_reset = true; + control->reset = 0; + break; + case SDCA_CTL_TYPE_S(XU, BYPASS): + case SDCA_CTL_TYPE_S(MFPU, BYPASS): + case SDCA_CTL_TYPE_S(FU, MUTE): + case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT): + control->has_reset = true; + control->reset = 1; + break; + case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS): + control->has_reset = true; + control->reset = 3; + break; + default: + break; + } + + return 0; +} + static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity, struct fwnode_handle *control_node, struct sdca_control *control) @@ -986,6 +1018,10 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti control->is_volatile = find_sdca_control_volatile(entity, control); + ret = find_sdca_control_reset(entity, control); + if (ret) + return ret; + ret = find_sdca_control_range(dev, control_node, &control->range); if (ret) { dev_err(dev, "%s: control %#x: range missing: %d\n", diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c index 2cca9a9c71ea..4f8a685dc43d 100644 --- a/sound/soc/sdca/sdca_regmap.c +++ b/sound/soc/sdca/sdca_regmap.c @@ -218,7 +218,8 @@ int sdca_regmap_count_constants(struct device *dev, struct sdca_entity *entity = &function->entities[i]; for (j = 0; j < entity->num_controls; j++) { - if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC) + if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC || + entity->controls[j].has_reset) nconsts += hweight64(entity->controls[j].cn_list); } } @@ -255,7 +256,8 @@ int sdca_regmap_populate_constants(struct device *dev, struct sdca_control *control = &entity->controls[j]; int cn; - if (control->mode != SDCA_ACCESS_MODE_DC) + if (control->mode != SDCA_ACCESS_MODE_DC && + !control->has_reset) continue; l = 0; @@ -264,7 +266,10 @@ int sdca_regmap_populate_constants(struct device *dev, consts[k].reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn); - consts[k].def = control->values[l]; + if (control->mode == SDCA_ACCESS_MODE_DC) + consts[k].def = control->values[l]; + else + consts[k].def = control->reset; k++; l++; } @@ -306,6 +311,9 @@ static int populate_control_defaults(struct device *dev, struct regmap *regmap, i++; } else if (!control->is_volatile) { + if (control->has_reset) + regcache_drop_region(regmap, reg, reg); + ret = regmap_read(regmap, reg, &val); if (ret) { dev_err(dev, "Failed to read initial %#x: %d\n", From 812ff1baa764080ba37bb0729e0c23c0e869b542 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Feb 2026 12:59:43 +0000 Subject: [PATCH 307/341] ASoC: SDCA: Limit values user can write to Selected Mode Prevent the user from both updating the Selected Mode control whilst the jack is not present, and from writing values that don't correspond to a valid jack type (Unknown, in progress, etc.). Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260204125944.1134011-7-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 37 ++++++++++++++++++++++++++++++++++++- sound/soc/sdca/sdca_jack.c | 2 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 9685281529e9..bb6e74e80a3e 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -116,6 +116,41 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun } EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA"); +static int ge_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct device *dev = component->dev; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int reg = e->reg; + int ret; + + reg &= ~SDW_SDCA_CTL_CSEL(0x3F); + reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to resume writing %s: %d\n", + kcontrol->id.name, ret); + return ret; + } + + ret = snd_soc_component_read(component, reg); + pm_runtime_put(dev); + if (ret < 0) + return ret; + else if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS) + return -EBUSY; + + ret = snd_soc_enum_item_to_val(e, item[0]); + if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS) + return -EINVAL; + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + static int entity_early_parse_ge(struct device *dev, struct sdca_function_data *function, struct sdca_entity *entity) @@ -192,7 +227,7 @@ static int entity_early_parse_ge(struct device *dev, kctl->name = control_name; kctl->info = snd_soc_info_enum_double; kctl->get = snd_soc_dapm_get_enum_double; - kctl->put = snd_soc_dapm_put_enum_double; + kctl->put = ge_put_enum_double; kctl->private_value = (unsigned long)soc_enum; entity->ge.kctl = kctl; diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index bfa621b744e1..605514f02045 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -105,7 +105,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); - ret = kctl->put(kctl, ucontrol); + ret = snd_soc_dapm_put_enum_double(kctl, ucontrol); if (ret < 0) { dev_err(dev, "failed to update selected mode: %d\n", ret); return ret; From f1ef70a4a32042984d29b8d02bdf6167474373af Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Mon, 2 Feb 2026 18:37:00 -0600 Subject: [PATCH 308/341] ASoC: dt-bindings: davinci-mcasp: Add properties for asynchronous mode McASP supports the independent configuration of TX & RX clk and frame sync registers. By default, the driver is configured in synchronous mode where RX clock generator is disabled and it uses transmit clock signals as bit clock and frame sync. Therefore add optional properties needed for asynchronous mode. Add ti,async-mode boolean binding to provide a way to decouple the default behavior and allows for independent TX & RX clocking. Add tdm-slots-rx uint32 binding to provide an alternative hardware specifier stating the number of RX serializers. The existing property tdm-slots will still dictate number of TX serializers, and RX if tdm-slots-rx isn't given for backwards compatibility. Add auxclk-fs-ratio-rx which allows to specify the ratio just for RX. The driver can be supplied with two different ratios (auxclk-fs-ratio and auxclk-fs-ratio-rx in tandem) and achieve two different sampling rates for tx & rx. Signed-off-by: Sen Wang Acked-by: Peter Ujfalusi Tested-by: Paresh Bhagat Link: https://patch.msgid.link/20260203003703.2334443-2-sen@ti.com Signed-off-by: Mark Brown --- .../bindings/sound/davinci-mcasp-audio.yaml | 71 +++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml index beef193aaaeb..87559d0d079a 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml @@ -40,11 +40,33 @@ properties: tdm-slots: $ref: /schemas/types.yaml#/definitions/uint32 description: - number of channels over one serializer - the property is ignored in DIT mode + Number of channels over one serializer. This property + specifies the TX playback TDM slot count, along with default RX slot count + if tdm-slots-rx is not specified. + The property is ignored in DIT mode. minimum: 2 maximum: 32 + tdm-slots-rx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Number of RX capture channels over one serializer. If specified, + allows independent RX TDM slot count separate from TX. Requires + ti,async-mode to be enabled for independent TX/RX clock rates. + The property is ignored in DIT mode. + minimum: 2 + maximum: 32 + + ti,async-mode: + description: + Specify to allow independent TX & RX clocking, + to enable audio playback & record with different sampling rate, + and different number of bits per frame. + if property is omitted, TX and RX will share same bit clock and frame clock signals, + thus RX need to use same bits per frame and sampling rate as TX in synchronous mode. + the property is ignored in DIT mode (as DIT is TX-only) + type: boolean + serial-dir: description: A list of serializer configuration @@ -125,7 +147,21 @@ properties: auxclk-fs-ratio: $ref: /schemas/types.yaml#/definitions/uint32 - description: ratio of AUCLK and FS rate if applicable + description: + Ratio of AUCLK and FS rate if applicable. This property specifies + the TX ratio, along with default RX ratio if auxclk-fs-ratio-rx + is not specified. + When not specified, the inputted system clock frequency via set_sysclk + callback by the machine driver is used for divider calculation. + + auxclk-fs-ratio-rx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Ratio of AUCLK and FS rate for RX. If specified, allows + for a different RX ratio. Requires ti,async-mode to be + enabled when the ratio differs from auxclk-fs-ratio. + When not specified, it defaults to the value of auxclk-fs-ratio. + The property is ignored in DIT mode. gpio-controller: true @@ -170,14 +206,38 @@ allOf: - $ref: dai-common.yaml# - if: properties: - opmode: + op-mode: enum: - 0 - then: required: - tdm-slots + - if: + properties: + op-mode: + const: 1 + then: + properties: + tdm-slots: false + tdm-slots-rx: false + ti,async-mode: false + auxclk-fs-ratio-rx: false + + - if: + required: + - tdm-slots-rx + then: + required: + - ti,async-mode + + - if: + required: + - auxclk-fs-ratio-rx + then: + required: + - ti,async-mode + unevaluatedProperties: false examples: @@ -190,6 +250,7 @@ examples: interrupt-names = "tx", "rx"; op-mode = <0>; /* MCASP_IIS_MODE */ tdm-slots = <2>; + ti,async-mode; dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>; dma-names = "tx", "rx"; serial-dir = < From e683cb088fdcbdc86fc30008319312cc0bb80226 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Mon, 2 Feb 2026 18:37:01 -0600 Subject: [PATCH 309/341] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function The current mcasp_is_synchronous() function does more than what it proclaims, it also checks if McASP is a frame producer. Therefore split the original function into two separate ones and replace all occurrences with the new equivalent logic. Signed-off-by: Sen Wang Acked-by: Peter Ujfalusi Tested-by: Paresh Bhagat Link: https://patch.msgid.link/20260203003703.2334443-3-sen@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index db6913c05378..c0cb271e43cb 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -180,10 +180,16 @@ static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) { - u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; + return !(aclkxctl & TX_ASYNC); +} + +static bool mcasp_is_frame_producer(struct davinci_mcasp *mcasp) +{ + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + + return rxfmctl & AFSRE; } static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) @@ -227,7 +233,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) * synchronously from the transmit clock and frame sync. We need to make * sure that the TX signlas are enabled when starting reception. */ - if (mcasp_is_synchronous(mcasp)) { + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) { mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); mcasp_set_clk_pdir(mcasp, true); @@ -240,7 +246,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); /* Release Frame Sync generator */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - if (mcasp_is_synchronous(mcasp)) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); /* enable receive IRQs */ @@ -306,7 +312,7 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { mcasp_set_clk_pdir(mcasp, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); } @@ -333,7 +339,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) * In synchronous mode keep TX clocks running if the capture stream is * still running. */ - if (mcasp_is_synchronous(mcasp) && mcasp->streams) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; else mcasp_set_clk_pdir(mcasp, false); @@ -1042,7 +1048,8 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, * not running already we need to configure the TX slots in * order to have correct FSX on the bus */ - if (mcasp_is_synchronous(mcasp) && !mcasp->channels) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && + !mcasp->channels) mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXMOD(total_slots), FSXMOD(0x1FF)); } From 016efcaa470cdbc658df46d968d875f6a1cf9a78 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Mon, 2 Feb 2026 18:37:02 -0600 Subject: [PATCH 310/341] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams Simplify the mcasp_set_clk_pdir caller convention in start/stop stream function, to make it so that set_clk_pdir gets called regardless when stream starts and also disables when stream ends. Functionality-wise, everything remains the same as the previously skipped calls are now either correctly configured (when McASP is SND_SOC_DAIFMT_BP_FC - pdir needs to be enabled) or called with a bitmask of zero (when McASP is SND_SOC_DAIFMT_BC_FC - pdir gets disabled). On brief regarding McASP Clock and Frame sync configurations, refer to [0]. [0]:TRM Section 12.1.1.4.2 https://www.ti.com/lit/ug/sprujd4a/sprujd4a.pdf Signed-off-by: Sen Wang Acked-by: Peter Ujfalusi Tested-by: Paresh Bhagat Link: https://patch.msgid.link/20260203003703.2334443-4-sen@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index c0cb271e43cb..12b7d0c65624 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -236,8 +236,8 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) { mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - mcasp_set_clk_pdir(mcasp, true); } + mcasp_set_clk_pdir(mcasp, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -312,10 +312,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { + if (!mcasp->streams) mcasp_set_clk_pdir(mcasp, false); + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); - } mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -341,7 +341,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; - else + if (!mcasp->streams) mcasp_set_clk_pdir(mcasp, false); From 9db327083f7e0da702e2ec0169f8a34f3576f371 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Mon, 2 Feb 2026 18:37:03 -0600 Subject: [PATCH 311/341] ASoC: ti: davinci-mcasp: Add asynchronous mode support McASP has dedicated clock & frame sync registers for both transmit and receive. Currently McASP driver only supports synchronous behavior and couples both TX & RX settings. Add logic that enables asynchronous mode via ti,async-mode property. In async mode, playback & record can be done simultaneously with different audio configurations (tdm slots, tdm width, audio bit depth). Note the ability to have different tx/rx DSP formats (i2s, dsp_a, etc.), while possible in hardware, remains to be a gap as it require changes to the corresponding machine driver interface. Existing IIS (sync mode) and DIT mode logic remains mostly unchanged. Exceptions are IIS mode logic that previously assumed sync mode, which has now been made aware of the distinction. And shared logic across all modes also now checks for McASP tx/rx-specific driver attributes. Those attributes have been populated according to the original extent, ensuring no divergence in functionality. Constraints no longer applicable for async mode are skipped. Clock selection options have also been added to include rx/tx-only clk_ids, exposing independent configuration via the machine driver as well. Note that asynchronous mode is not applicable for McASP in DIT mode, which is a transmitter-only mode to interface w/ self-clocking formats. Signed-off-by: Sen Wang Acked-by: Peter Ujfalusi Tested-by: Paresh Bhagat Link: https://patch.msgid.link/20260203003703.2334443-5-sen@ti.com Signed-off-by: Mark Brown --- include/linux/platform_data/davinci_asp.h | 3 +- sound/soc/ti/davinci-mcasp.c | 489 +++++++++++++++++----- sound/soc/ti/davinci-mcasp.h | 10 + 3 files changed, 401 insertions(+), 101 deletions(-) diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index b9c8520b4bd3..509c5592aab0 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -59,7 +59,8 @@ struct davinci_mcasp_pdata { bool i2s_accurate_sck; /* McASP specific fields */ - int tdm_slots; + int tdm_slots_tx; + int tdm_slots_rx; u8 op_mode; u8 dismod; u8 num_serializer; diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 12b7d0c65624..2d260fbc9b83 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -71,6 +71,7 @@ struct davinci_mcasp_context { struct davinci_mcasp_ruledata { struct davinci_mcasp *mcasp; int serializers; + int stream; }; struct davinci_mcasp { @@ -88,21 +89,27 @@ struct davinci_mcasp { bool missing_audio_param; /* McASP specific data */ - int tdm_slots; + int tdm_slots_tx; + int tdm_slots_rx; u32 tdm_mask[2]; - int slot_width; + int slot_width_tx; + int slot_width_rx; u8 op_mode; u8 dismod; u8 num_serializer; u8 *serial_dir; u8 version; - u8 bclk_div; + u8 bclk_div_tx; + u8 bclk_div_rx; int streams; u32 irq_request[2]; - int sysclk_freq; + unsigned int sysclk_freq_tx; + unsigned int sysclk_freq_rx; bool bclk_master; - u32 auxclk_fs_ratio; + bool async_mode; + u32 auxclk_fs_ratio_tx; + u32 auxclk_fs_ratio_rx; unsigned long pdir; /* Pin direction bitfield */ @@ -204,6 +211,27 @@ static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) } } +static inline void mcasp_set_clk_pdir_stream(struct davinci_mcasp *mcasp, + int stream, bool enable) +{ + u32 bit, bit_end; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit = PIN_BIT_ACLKX; + bit_end = PIN_BIT_AFSX + 1; + } else { + bit = PIN_BIT_ACLKR; + bit_end = PIN_BIT_AFSR + 1; + } + + for_each_set_bit_from(bit, &mcasp->pdir, bit_end) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) { u32 bit; @@ -216,6 +244,36 @@ static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) } } +static inline int mcasp_get_tdm_slots(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->tdm_slots_tx : mcasp->tdm_slots_rx; +} + +static inline int mcasp_get_slot_width(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->slot_width_tx : mcasp->slot_width_rx; +} + +static inline unsigned int mcasp_get_sysclk_freq(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->sysclk_freq_tx : mcasp->sysclk_freq_rx; +} + +static inline unsigned int mcasp_get_bclk_div(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->bclk_div_tx : mcasp->bclk_div_rx; +} + +static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx; +} + static void mcasp_start_rx(struct davinci_mcasp *mcasp) { if (mcasp->rxnumevt) { /* enable FIFO */ @@ -231,13 +289,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) /* * When ASYNC == 0 the transmit and receive sections operate * synchronously from the transmit clock and frame sync. We need to make - * sure that the TX signlas are enabled when starting reception. + * sure that the TX signals are enabled when starting reception, + * when the McASP is the producer. */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) { mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); } - mcasp_set_clk_pdir(mcasp, true); + if (mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir(mcasp, true); + else + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -268,7 +330,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - mcasp_set_clk_pdir(mcasp, true); + if (mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir(mcasp, true); + else + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); @@ -311,9 +376,17 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) /* * In synchronous mode stop the TX clocks if no other stream is * running + * Otherwise in async mode only stop RX clocks */ - if (!mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_clk_pdir(mcasp, false); + else if (!mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, false); + /* + * When McASP is the producer and operating in synchronous mode, + * stop the transmit clocks if no other stream is running. As + * tx & rx operate synchronously from the transmit clock. + */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); @@ -338,11 +411,14 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) /* * In synchronous mode keep TX clocks running if the capture stream is * still running. + * Otherwise in async mode only stop TX clocks */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; - if (!mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_clk_pdir(mcasp, false); + else if (!mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); @@ -626,13 +702,39 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, AHCLKRDIV(div - 1), AHCLKRDIV_MASK); break; + case MCASP_CLKDIV_AUXCLK_TXONLY: /* MCLK divider for TX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXDIV(div - 1), AHCLKXDIV_MASK); + break; + + case MCASP_CLKDIV_AUXCLK_RXONLY: /* MCLK divider for RX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRDIV(div - 1), AHCLKRDIV_MASK); + break; + case MCASP_CLKDIV_BCLK: /* BCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXDIV(div - 1), ACLKXDIV_MASK); mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRDIV(div - 1), ACLKRDIV_MASK); + if (explicit) { + mcasp->bclk_div_tx = div; + mcasp->bclk_div_rx = div; + } + break; + + case MCASP_CLKDIV_BCLK_TXONLY: /* BCLK divider for TX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, + ACLKXDIV(div - 1), ACLKXDIV_MASK); if (explicit) - mcasp->bclk_div = div; + mcasp->bclk_div_tx = div; + break; + + case MCASP_CLKDIV_BCLK_RXONLY: /* BCLK divider for RX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, + ACLKRDIV(div - 1), ACLKRDIV_MASK); + if (explicit) + mcasp->bclk_div_rx = div; break; case MCASP_CLKDIV_BCLK_FS_RATIO: @@ -646,11 +748,33 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, * tdm_slot width by dividing the ratio by the * number of configured tdm slots. */ - mcasp->slot_width = div / mcasp->tdm_slots; - if (div % mcasp->tdm_slots) + mcasp->slot_width_tx = div / mcasp->tdm_slots_tx; + if (div % mcasp->tdm_slots_tx) dev_warn(mcasp->dev, - "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", - __func__, div, mcasp->tdm_slots); + "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots", + __func__, div, mcasp->tdm_slots_tx); + + mcasp->slot_width_rx = div / mcasp->tdm_slots_rx; + if (div % mcasp->tdm_slots_rx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots", + __func__, div, mcasp->tdm_slots_rx); + break; + + case MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY: + mcasp->slot_width_tx = div / mcasp->tdm_slots_tx; + if (div % mcasp->tdm_slots_tx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots", + __func__, div, mcasp->tdm_slots_tx); + break; + + case MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY: + mcasp->slot_width_rx = div / mcasp->tdm_slots_rx; + if (div % mcasp->tdm_slots_rx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots", + __func__, div, mcasp->tdm_slots_rx); break; default: @@ -684,6 +808,20 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + case MCASP_CLK_HCLK_AHCLK_TXONLY: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AHCLK_RXONLY: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + clear_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; break; case MCASP_CLK_HCLK_AUXCLK: mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, @@ -691,22 +829,56 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_TXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_RXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; break; default: dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id); goto out; } } else { - /* Select AUXCLK as HCLK */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + /* McASP is clock master, select AUXCLK as HCLK */ + switch (clk_id) { + case MCASP_CLK_HCLK_AUXCLK_TXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_RXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; + break; + default: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + } } /* * When AHCLK X/R is selected to be output it means that the HCLK is * the same clock - coming via AUXCLK. */ - mcasp->sysclk_freq = freq; out: pm_runtime_put(mcasp->dev); return 0; @@ -718,9 +890,11 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, { struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; unsigned int *list = (unsigned int *) cl->list; - int slots = mcasp->tdm_slots; + int slots; int i, count = 0; + slots = mcasp_get_tdm_slots(mcasp, stream); + if (mcasp->tdm_mask[stream]) slots = hweight32(mcasp->tdm_mask[stream]); @@ -785,27 +959,42 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - mcasp->tdm_slots = slots; + if (mcasp->async_mode) { + if (tx_mask) { + mcasp->tdm_slots_tx = slots; + mcasp->slot_width_tx = slot_width; + } + if (rx_mask) { + mcasp->tdm_slots_rx = slots; + mcasp->slot_width_rx = slot_width; + } + } else { + mcasp->tdm_slots_tx = slots; + mcasp->tdm_slots_rx = slots; + mcasp->slot_width_tx = slot_width; + mcasp->slot_width_rx = slot_width; + } + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; - mcasp->slot_width = slot_width; return davinci_mcasp_set_ch_constraints(mcasp); } static int davinci_config_channel_size(struct davinci_mcasp *mcasp, - int sample_width) + int sample_width, int stream) { u32 fmt; u32 tx_rotate, rx_rotate, slot_width; u32 mask = (1ULL << sample_width) - 1; - if (mcasp->slot_width) - slot_width = mcasp->slot_width; - else if (mcasp->max_format_width) - slot_width = mcasp->max_format_width; - else - slot_width = sample_width; + slot_width = mcasp_get_slot_width(mcasp, stream); + if (!slot_width) { + if (mcasp->max_format_width) + slot_width = mcasp->max_format_width; + else + slot_width = sample_width; + } /* * TX rotation: * right aligned formats: rotate w/ slot_width @@ -828,17 +1017,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, fmt = (slot_width >> 1) - 1; if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), - RXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), - TXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), - TXROT(7)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), - RXROT(7)); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); + if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), + TXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), + TXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); + } + if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), + RXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), + RXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); + } } else { /* + * DIT mode only use TX serializers * according to the TRM it should be TXROT=0, this one works: * 16 bit to 23-8 (TXROT=6, rotate 24 bits) * 24 bit to 23-0 (TXROT=0, rotate 0 bits) @@ -851,10 +1046,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, TXROT(7)); mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15), TXSSZ(0x0F)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); } - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); - return 0; } @@ -865,11 +1059,13 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, int i; u8 tx_ser = 0; u8 rx_ser = 0; - u8 slots = mcasp->tdm_slots; + int slots; u8 max_active_serializers, max_rx_serializers, max_tx_serializers; int active_serializers, numevt; u32 reg; + slots = mcasp_get_tdm_slots(mcasp, stream); + /* In DIT mode we only allow maximum of one serializers for now */ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) max_active_serializers = 1; @@ -997,7 +1193,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, u32 mask = 0; u32 busel = 0; - total_slots = mcasp->tdm_slots; + total_slots = mcasp_get_tdm_slots(mcasp, stream); /* * If more than one serializer is needed, then use them with @@ -1028,7 +1224,10 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, mask |= (1 << i); } - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + if (mcasp->async_mode) + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); if (!mcasp->dat_port) busel = TXSEL; @@ -1127,16 +1326,33 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, unsigned int sysclk_freq, - unsigned int bclk_freq, bool set) + unsigned int bclk_freq, + int stream, + bool set) { - u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG); int div = sysclk_freq / bclk_freq; int rem = sysclk_freq % bclk_freq; int error_ppm; int aux_div = 1; + int bclk_div_id, auxclk_div_id; + bool auxclk_enabled; + + if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE; + bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY; + auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY; + } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; + bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY; + auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY; + } else { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; + bclk_div_id = MCASP_CLKDIV_BCLK; + auxclk_div_id = MCASP_CLKDIV_AUXCLK; + } if (div > (ACLKXDIV_MASK + 1)) { - if (reg & AHCLKXE) { + if (auxclk_enabled) { aux_div = div / (ACLKXDIV_MASK + 1); if (div % (ACLKXDIV_MASK + 1)) aux_div++; @@ -1166,10 +1382,10 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", error_ppm); - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); - if (reg & AHCLKXE) - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK, - aux_div, 0); + __davinci_mcasp_set_clkdiv(mcasp, bclk_div_id, div, false); + if (auxclk_enabled) + __davinci_mcasp_set_clkdiv(mcasp, auxclk_div_id, + aux_div, false); } return error_ppm; @@ -1220,6 +1436,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int channels = params_channels(params); int period_size = params_period_size(params); int ret; + unsigned int sysclk_freq = mcasp_get_sysclk_freq(mcasp, substream->stream); switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -1260,22 +1477,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * If mcasp is BCLK master, and a BCLK divider was not provided by * the machine driver, we need to calculate the ratio. */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int slots = mcasp->tdm_slots; + if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 && + sysclk_freq) { + int slots, slot_width; int rate = params_rate(params); int sbits = params_width(params); unsigned int bclk_target; - if (mcasp->slot_width) - sbits = mcasp->slot_width; + slots = mcasp_get_tdm_slots(mcasp, substream->stream); + + slot_width = mcasp_get_slot_width(mcasp, substream->stream); + if (slot_width) + sbits = slot_width; if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) bclk_target = rate * sbits * slots; else bclk_target = rate * 128; - davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq, - bclk_target, true); + davinci_mcasp_calc_clk_div(mcasp, sysclk_freq, + bclk_target, substream->stream, true); } ret = mcasp_common_hw_param(mcasp, substream->stream, @@ -1292,9 +1513,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - davinci_config_channel_size(mcasp, word_length); + davinci_config_channel_size(mcasp, word_length, substream->stream); - if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { + /* Channel constraints are disabled for async mode */ + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE && !mcasp->async_mode) { mcasp->channels = channels; if (!mcasp->max_format_width) mcasp->max_format_width = word_length; @@ -1338,7 +1560,7 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params, snd_pcm_format_t i; snd_mask_none(&nfmt); - slot_width = rd->mcasp->slot_width; + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); pcm_for_each_format(i) { if (snd_mask_test_format(fmt, i)) { @@ -1388,12 +1610,15 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval *ri = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); int sbits = params_width(params); - int slots = rd->mcasp->tdm_slots; + int slots, slot_width; struct snd_interval range; int i; - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; + slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream); + + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); + if (slot_width) + sbits = slot_width; snd_interval_any(&range); range.empty = 1; @@ -1403,16 +1628,17 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, uint bclk_freq = sbits * slots * davinci_mcasp_dai_rates[i]; unsigned int sysclk_freq; + unsigned int ratio; int ppm; - if (rd->mcasp->auxclk_fs_ratio) - sysclk_freq = davinci_mcasp_dai_rates[i] * - rd->mcasp->auxclk_fs_ratio; + ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream); + if (ratio) + sysclk_freq = davinci_mcasp_dai_rates[i] * ratio; else - sysclk_freq = rd->mcasp->sysclk_freq; + sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream); ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq, - bclk_freq, false); + bclk_freq, rd->stream, false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { if (range.empty) { range.min = davinci_mcasp_dai_rates[i]; @@ -1438,30 +1664,34 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; int rate = params_rate(params); - int slots = rd->mcasp->tdm_slots; + int slots; int count = 0; snd_pcm_format_t i; + slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream); + snd_mask_none(&nfmt); pcm_for_each_format(i) { if (snd_mask_test_format(fmt, i)) { uint sbits = snd_pcm_format_width(i); unsigned int sysclk_freq; - int ppm; + unsigned int ratio; + int ppm, slot_width; - if (rd->mcasp->auxclk_fs_ratio) - sysclk_freq = rate * - rd->mcasp->auxclk_fs_ratio; + ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream); + if (ratio) + sysclk_freq = rate * ratio; else - sysclk_freq = rd->mcasp->sysclk_freq; + sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream); - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); + if (slot_width) + sbits = slot_width; ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq, sbits * slots * rate, - false); + rd->stream, false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set_format(&nfmt, i); count++; @@ -1498,7 +1728,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir, ret; - int tdm_slots = mcasp->tdm_slots; + int tdm_slots; u8 *numevt; /* Do not allow more then one stream per direction */ @@ -1507,6 +1737,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, mcasp->substreams[substream->stream] = substream; + tdm_slots = mcasp_get_tdm_slots(mcasp, substream->stream); + if (mcasp->tdm_mask[substream->stream]) tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); @@ -1528,6 +1760,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, } ruledata->serializers = max_channels; ruledata->mcasp = mcasp; + ruledata->stream = substream->stream; max_channels *= tdm_slots; /* * If the already active stream has less channels than the calculated @@ -1535,9 +1768,13 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * is in use we need to use that as a constraint for the second stream. * Otherwise (first stream or less allowed channels or more than one * serializer in use) we use the calculated constraint. + * + * However, in async mode, TX and RX have independent clocks and can + * use different configurations, so don't apply the constraint. */ if (mcasp->channels && mcasp->channels < max_channels && - ruledata->serializers == 1) + ruledata->serializers == 1 && + !mcasp->async_mode) max_channels = mcasp->channels; /* * But we can always allow channels upto the amount of @@ -1554,10 +1791,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &mcasp->chconstr[substream->stream]); - if (mcasp->max_format_width) { + if (mcasp->max_format_width && !mcasp->async_mode) { /* * Only allow formats which require same amount of bits on the - * bus as the currently running stream + * bus as the currently running stream to ensure sync mode */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, @@ -1566,8 +1803,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) return ret; - } - else if (mcasp->slot_width) { + } else if (mcasp_get_slot_width(mcasp, substream->stream)) { /* Only allow formats require <= slot_width bits on the bus */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, @@ -1582,7 +1818,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * If we rely on implicit BCLK divider setting we should * set constraints based on what we can provide. */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 && + mcasp_get_sysclk_freq(mcasp, substream->stream)) { ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, davinci_mcasp_hw_rule_rate, @@ -1759,8 +1996,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .formats = DAVINCI_MCASP_PCM_FMTS, }, .ops = &davinci_mcasp_dai_ops, - - .symmetric_rate = 1, }, { .name = "davinci-mcasp.1", @@ -1918,18 +2153,33 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, goto out; } + /* Parse TX-specific TDM slot and use it as default for RX */ if (of_property_read_u32(np, "tdm-slots", &val) == 0) { if (val < 2 || val > 32) { - dev_err(&pdev->dev, "tdm-slots must be in rage [2-32]\n"); + dev_err(&pdev->dev, "tdm-slots must be in range [2-32]\n"); return -EINVAL; } - pdata->tdm_slots = val; + pdata->tdm_slots_tx = val; + pdata->tdm_slots_rx = val; } else if (pdata->op_mode == DAVINCI_MCASP_IIS_MODE) { mcasp->missing_audio_param = true; goto out; } + /* Parse RX-specific TDM slot count if provided */ + if (of_property_read_u32(np, "tdm-slots-rx", &val) == 0) { + if (val < 2 || val > 32) { + dev_err(&pdev->dev, "tdm-slots-rx must be in range [2-32]\n"); + return -EINVAL; + } + + pdata->tdm_slots_rx = val; + } + + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) + mcasp->async_mode = of_property_read_bool(np, "ti,async-mode"); + of_serial_dir32 = of_get_property(np, "serial-dir", &val); val /= sizeof(u32); if (of_serial_dir32) { @@ -1955,8 +2205,15 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, if (of_property_read_u32(np, "rx-num-evt", &val) == 0) pdata->rxnumevt = val; - if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) - mcasp->auxclk_fs_ratio = val; + /* Parse TX-specific auxclk/fs ratio and use it as default for RX */ + if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) { + mcasp->auxclk_fs_ratio_tx = val; + mcasp->auxclk_fs_ratio_rx = val; + } + + /* Parse RX-specific auxclk/fs ratio if provided */ + if (of_property_read_u32(np, "auxclk-fs-ratio-rx", &val) == 0) + mcasp->auxclk_fs_ratio_rx = val; if (of_property_read_u32(np, "dismod", &val) == 0) { if (val == 0 || val == 2 || val == 3) { @@ -1985,19 +2242,51 @@ out: mcasp->op_mode = pdata->op_mode; /* sanity check for tdm slots parameter */ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { - if (pdata->tdm_slots < 2) { - dev_warn(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 2; - } else if (pdata->tdm_slots > 32) { - dev_warn(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 32; + if (pdata->tdm_slots_tx < 2) { + dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n", + pdata->tdm_slots_tx); + mcasp->tdm_slots_tx = 2; + } else if (pdata->tdm_slots_tx > 32) { + dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n", + pdata->tdm_slots_tx); + mcasp->tdm_slots_tx = 32; } else { - mcasp->tdm_slots = pdata->tdm_slots; + mcasp->tdm_slots_tx = pdata->tdm_slots_tx; + } + + if (pdata->tdm_slots_rx < 2) { + dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n", + pdata->tdm_slots_rx); + mcasp->tdm_slots_rx = 2; + } else if (pdata->tdm_slots_rx > 32) { + dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n", + pdata->tdm_slots_rx); + mcasp->tdm_slots_rx = 32; + } else { + mcasp->tdm_slots_rx = pdata->tdm_slots_rx; } } else { - mcasp->tdm_slots = 32; + mcasp->tdm_slots_tx = 32; + mcasp->tdm_slots_rx = 32; + } + + /* Different TX/RX slot counts require async mode */ + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE && + mcasp->tdm_slots_tx != mcasp->tdm_slots_rx && !mcasp->async_mode) { + dev_err(&pdev->dev, + "Different TX (%d) and RX (%d) TDM slots require ti,async-mode\n", + mcasp->tdm_slots_tx, mcasp->tdm_slots_rx); + return -EINVAL; + } + + /* Different TX/RX auxclk-fs-ratio require async mode */ + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE && + mcasp->auxclk_fs_ratio_tx && mcasp->auxclk_fs_ratio_rx && + mcasp->auxclk_fs_ratio_tx != mcasp->auxclk_fs_ratio_rx && !mcasp->async_mode) { + dev_err(&pdev->dev, + "Different TX (%d) and RX (%d) auxclk-fs-ratio require ti,async-mode\n", + mcasp->auxclk_fs_ratio_tx, mcasp->auxclk_fs_ratio_rx); + return -EINVAL; } mcasp->num_serializer = pdata->num_serializer; diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h index 5de2b8a31061..83b3c67f4a2b 100644 --- a/sound/soc/ti/davinci-mcasp.h +++ b/sound/soc/ti/davinci-mcasp.h @@ -298,10 +298,20 @@ /* Source of High-frequency transmit/receive clock */ #define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */ #define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */ +#define MCASP_CLK_HCLK_AHCLK_TXONLY 2 /* AHCLKX for TX only */ +#define MCASP_CLK_HCLK_AHCLK_RXONLY 3 /* AHCLKR for RX only */ +#define MCASP_CLK_HCLK_AUXCLK_TXONLY 4 /* AUXCLK for TX only */ +#define MCASP_CLK_HCLK_AUXCLK_RXONLY 5 /* AUXCLK for RX only */ /* clock divider IDs */ #define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */ #define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */ #define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */ +#define MCASP_CLKDIV_AUXCLK_TXONLY 3 /* AUXCLK divider for TX only */ +#define MCASP_CLKDIV_AUXCLK_RXONLY 4 /* AUXCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_TXONLY 5 /* BCLK divider for TX only */ +#define MCASP_CLKDIV_BCLK_RXONLY 6 /* BCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY 7 /* BCLK/FS ratio for TX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY 8 /* BCLK/FS ratio for RX only*/ #endif /* DAVINCI_MCASP_H */ From fef1f756155c30511397bbcd9d55640ab2e44d99 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Sat, 10 Jan 2026 11:53:36 -0800 Subject: [PATCH 312/341] ASoC: cs4271: Fix resource leak in cs4271_soc_resume() Smatch detects this resource leak: sound/soc/codecs/cs4271.c:548 cs4271_soc_resume() warn: 'cs4271->clk' from clk_prepare_enable() not released on lines: 540,546. Instead of direct returns, unprepare the clock and disable regulators on the error paths. Fixes: cf6bf51b5325 ("ASoC: cs4271: Add support for the external mclk") Fixes: 9a397f473657 ("ASoC: cs4271: add regulator consumer support") Signed-off-by: Harshit Mogalapalli Reviewed-by: Charles Keepax Acked-by: Herve Codina Reviewed-by: Alexander Sverdlin Link: https://patch.msgid.link/20260110195337.2522347-1-harshit.m.mogalapalli@oracle.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs4271.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 77dfc83a3c01..d8cdd37e9112 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -528,7 +528,7 @@ static int cs4271_soc_resume(struct snd_soc_component *component) ret = clk_prepare_enable(cs4271->clk); if (ret) { dev_err(component->dev, "Failed to enable clk: %d\n", ret); - return ret; + goto err_disable_regulators; } /* Do a proper reset after power up */ @@ -537,15 +537,21 @@ static int cs4271_soc_resume(struct snd_soc_component *component) /* Restore codec state */ ret = regcache_sync(cs4271->regmap); if (ret < 0) - return ret; + goto err_disable_clk; /* then disable the power-down bit */ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, CS4271_MODE2_PDN, 0); if (ret < 0) - return ret; + goto err_disable_clk; return 0; + +err_disable_clk: + clk_disable_unprepare(cs4271->clk); +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); + return ret; } #else #define cs4271_soc_suspend NULL From 4d1e3e2c404dc30e039d81ba7396c8bb82ade991 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 5 Feb 2026 16:48:36 +0000 Subject: [PATCH 313/341] ASoC: cs35l56: Support for reading speaker ID from on-chip GPIOs Add support for using the state of pins on the amplifier to indicate the type of speaker fitted. Previously, where there were alternate speaker vendors, this was indicated using host CPU GPIOs. Some new Dell models use spare pins on the CS35L63 as GPIOs for the speaker ID detection. Cirrus-specific SDCA Disco properties provide a list of the pins to be used, and pull-up/down settings for the pads. This list is ordered, MSbit to LSbit. The code to set the firmware filename has been modified to check for using chip pins for speaker ID. The entire block of code to set firmware name has been moved out of cs35l56_component_probe() into its own function to make it easier to KUnit test. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260205164838.1611295-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 37 +++++++ sound/soc/codecs/cs35l56-shared.c | 167 ++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.c | 141 ++++++++++++++++++++++++- sound/soc/codecs/cs35l56.h | 2 + 4 files changed, 343 insertions(+), 4 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 5928af539c46..ae1e1489b671 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -9,6 +9,7 @@ #ifndef __CS35L56_H #define __CS35L56_H +#include #include #include #include @@ -26,6 +27,9 @@ struct snd_ctl_elem_value; #define CS35L56_GLOBAL_ENABLES 0x0002014 #define CS35L56_BLOCK_ENABLES 0x0002018 #define CS35L56_BLOCK_ENABLES2 0x000201C +#define CS35L56_SYNC_GPIO1_CFG 0x0002410 +#define CS35L56_ASP2_DIO_GPIO13_CFG 0x0002440 +#define CS35L56_UPDATE_REGS 0x0002A0C #define CS35L56_REFCLK_INPUT 0x0002C04 #define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C #define CS35L56_OTP_MEM_53 0x00300D4 @@ -65,6 +69,9 @@ struct snd_ctl_elem_value; #define CS35L56_IRQ1_MASK_8 0x000E0AC #define CS35L56_IRQ1_MASK_18 0x000E0D4 #define CS35L56_IRQ1_MASK_20 0x000E0DC +#define CS35L56_GPIO_STATUS1 0x000F000 +#define CS35L56_GPIO1_CTRL1 0x000F008 +#define CS35L56_GPIO13_CTRL1 0x000F038 #define CS35L56_MIXER_NGATE_CH1_CFG 0x0010004 #define CS35L56_MIXER_NGATE_CH2_CFG 0x0010008 #define CS35L56_DSP_MBOX_1_RAW 0x0011000 @@ -130,6 +137,17 @@ struct snd_ctl_elem_value; #define CS35L56_MTLREVID_MASK 0x0000000F #define CS35L56_REVID_B0 0x000000B0 +/* PAD_INTF */ +#define CS35L56_PAD_GPIO_PULL_MASK GENMASK(3, 2) +#define CS35L56_PAD_GPIO_IE BIT(0) + +#define CS35L56_PAD_PULL_NONE 0 +#define CS35L56_PAD_PULL_UP 1 +#define CS35L56_PAD_PULL_DOWN 2 + +/* UPDATE_REGS */ +#define CS35L56_UPDT_GPIO_PRES BIT(6) + /* ASP_ENABLES1 */ #define CS35L56_ASP_RX2_EN_SHIFT 17 #define CS35L56_ASP_RX1_EN_SHIFT 16 @@ -185,6 +203,12 @@ struct snd_ctl_elem_value; /* MIXER_NGATE_CHn_CFG */ #define CS35L56_AUX_NGATE_CHn_EN 0x00000001 +/* GPIOn_CTRL1 */ +#define CS35L56_GPIO_DIR_MASK BIT(31) +#define CS35L56_GPIO_FN_MASK GENMASK(2, 0) + +#define CS35L56_GPIO_FN_GPIO 0x00000001 + /* Mixer input sources */ #define CS35L56_INPUT_SRC_NONE 0x00 #define CS35L56_INPUT_SRC_ASP1RX1 0x08 @@ -279,6 +303,7 @@ struct snd_ctl_elem_value; #define CS35L56_HALO_STATE_TIMEOUT_US 250000 #define CS35L56_RESET_PULSE_MIN_US 1100 #define CS35L56_WAKE_HOLD_TIME_US 1000 +#define CS35L56_PAD_PULL_SETTLE_US 10 #define CS35L56_CALIBRATION_POLL_US (100 * USEC_PER_MSEC) #define CS35L56_CALIBRATION_TIMEOUT_US (5 * USEC_PER_SEC) @@ -289,6 +314,9 @@ struct snd_ctl_elem_value; #define CS35L56_NUM_BULK_SUPPLIES 3 #define CS35L56_NUM_DSP_REGIONS 5 +#define CS35L56_MAX_GPIO 13 +#define CS35L63_MAX_GPIO 9 + /* Additional margin for SYSTEM_RESET to control port ready on SPI */ #define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500) @@ -338,6 +366,10 @@ struct cs35l56_base { const struct cirrus_amp_cal_controls *calibration_controls; struct dentry *debugfs; u64 silicon_uid; + u8 onchip_spkid_gpios[5]; + u8 num_onchip_spkid_gpios; + u8 onchip_spkid_pulls[5]; + u8 num_onchip_spkid_pulls; }; static inline bool cs35l56_is_otp_register(unsigned int reg) @@ -413,6 +445,11 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base); void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base); +int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base, + const u32 *gpios, int num_gpios, + const u32 *pulls, int num_pulls); +int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base); +int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base); int cs35l56_get_bclk_freq_id(unsigned int freq); void cs35l56_fill_supply_names(struct regulator_bulk_data *data); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 60100c8f8c95..55c75b9e4172 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -6,12 +6,14 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -182,6 +184,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_OTP_MEM_53: case CS35L56_OTP_MEM_54: case CS35L56_OTP_MEM_55: + case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: + case CS35L56_UPDATE_REGS: case CS35L56_ASP1_ENABLES1: case CS35L56_ASP1_CONTROL1: case CS35L56_ASP1_CONTROL2: @@ -213,6 +217,7 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_IRQ1_MASK_8: case CS35L56_IRQ1_MASK_18: case CS35L56_IRQ1_MASK_20: + case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1: case CS35L56_MIXER_NGATE_CH1_CFG: case CS35L56_MIXER_NGATE_CH2_CFG: case CS35L56_DSP_VIRTUAL1_MBOX_1: @@ -262,6 +267,8 @@ static bool cs35l56_common_volatile_reg(unsigned int reg) case CS35L56_GLOBAL_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */ + case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: + case CS35L56_UPDATE_REGS: case CS35L56_REFCLK_INPUT: /* owned by firmware */ case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */ case CS35L56_DACPCM1_INPUT: /* owned by firmware */ @@ -272,6 +279,7 @@ static bool cs35l56_common_volatile_reg(unsigned int reg) case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: case CS35L56_IRQ1_EINT_18: case CS35L56_IRQ1_EINT_20: + case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1: case CS35L56_MIXER_NGATE_CH1_CFG: case CS35L56_MIXER_NGATE_CH2_CFG: case CS35L56_DSP_VIRTUAL1_MBOX_1: @@ -1552,6 +1560,165 @@ err: } EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED"); +int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base, + const u32 *gpios, int num_gpios, + const u32 *pulls, int num_pulls) +{ + int max_gpio; + int ret = 0; + int i; + + if ((num_gpios > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)) || + (num_pulls > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls))) + return -EOVERFLOW; + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + max_gpio = CS35L56_MAX_GPIO; + break; + default: + max_gpio = CS35L63_MAX_GPIO; + break; + } + + for (i = 0; i < num_gpios; i++) { + if (gpios[i] < 1 || gpios[i] > max_gpio) { + dev_err(cs35l56_base->dev, "Invalid spkid GPIO %d\n", gpios[i]); + /* Keep going so we log all bad values */ + ret = -EINVAL; + } + + /* Change to zero-based */ + cs35l56_base->onchip_spkid_gpios[i] = gpios[i] - 1; + } + + for (i = 0; i < num_pulls; i++) { + switch (pulls[i]) { + case 0: + cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_NONE; + break; + case 1: + cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_UP; + break; + case 2: + cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_DOWN; + break; + default: + dev_err(cs35l56_base->dev, "Invalid spkid pull %d\n", pulls[i]); + /* Keep going so we log all bad values */ + ret = -EINVAL; + break; + } + } + if (ret) + return ret; + + cs35l56_base->num_onchip_spkid_gpios = num_gpios; + cs35l56_base->num_onchip_spkid_pulls = num_pulls; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_check_and_save_onchip_spkid_gpios, "SND_SOC_CS35L56_SHARED"); + +/* Caller must pm_runtime resume before calling this function */ +int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base) +{ + struct regmap *regmap = cs35l56_base->regmap; + unsigned int addr_offset, val; + int num_gpios, num_pulls; + int i, ret; + + if (cs35l56_base->num_onchip_spkid_gpios == 0) + return 0; + + num_gpios = min(cs35l56_base->num_onchip_spkid_gpios, + ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)); + num_pulls = min(cs35l56_base->num_onchip_spkid_pulls, + ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)); + + for (i = 0; i < num_gpios; i++) { + addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32); + + /* Set unspecified pulls to NONE */ + if (i < num_pulls) { + val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, + cs35l56_base->onchip_spkid_pulls[i]); + } else { + val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, CS35L56_PAD_PULL_NONE); + } + + ret = regmap_update_bits(regmap, CS35L56_SYNC_GPIO1_CFG + addr_offset, + CS35L56_PAD_GPIO_PULL_MASK | CS35L56_PAD_GPIO_IE, + val | CS35L56_PAD_GPIO_IE); + if (ret) { + dev_err(cs35l56_base->dev, "GPIO%d set pad fail: %d\n", + cs35l56_base->onchip_spkid_gpios[i] + 1, ret); + return ret; + } + } + + ret = regmap_write(regmap, CS35L56_UPDATE_REGS, CS35L56_UPDT_GPIO_PRES); + if (ret) { + dev_err(cs35l56_base->dev, "UPDT_GPIO_PRES failed:%d\n", ret); + return ret; + } + + usleep_range(CS35L56_PAD_PULL_SETTLE_US, CS35L56_PAD_PULL_SETTLE_US * 2); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_configure_onchip_spkid_pads, "SND_SOC_CS35L56_SHARED"); + +/* Caller must pm_runtime resume before calling this function */ +int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base) +{ + struct regmap *regmap = cs35l56_base->regmap; + unsigned int addr_offset, val; + int num_gpios; + int speaker_id = 0; + int i, ret; + + if (cs35l56_base->num_onchip_spkid_gpios == 0) + return -ENOENT; + + num_gpios = min(cs35l56_base->num_onchip_spkid_gpios, + ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)); + + for (i = 0; i < num_gpios; i++) { + addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32); + + ret = regmap_update_bits(regmap, CS35L56_GPIO1_CTRL1 + addr_offset, + CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK, + CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO); + if (ret) { + dev_err(cs35l56_base->dev, "GPIO%u set func fail: %d\n", + cs35l56_base->onchip_spkid_gpios[i] + 1, ret); + return ret; + } + } + + ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val); + if (ret) { + dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n", + cs35l56_base->onchip_spkid_gpios[i] + 1, ret); + return ret; + } + + for (i = 0; i < num_gpios; i++) { + speaker_id <<= 1; + + if (val & BIT(cs35l56_base->onchip_spkid_gpios[i])) + speaker_id |= 1; + } + + dev_dbg(cs35l56_base->dev, "Onchip GPIO Speaker ID = %d\n", speaker_id); + + return speaker_id; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_read_onchip_spkid, "SND_SOC_CS35L56_SHARED"); + static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = { [0x0C] = 128000, [0x0F] = 256000, diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 31dd2f7b2858..2ff8b172b76e 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1179,15 +1179,28 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) } EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix); -static int cs35l56_component_probe(struct snd_soc_component *component) +VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component) { - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct dentry *debugfs_root = component->debugfs_root; unsigned short vendor, device; int ret; - BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); + if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) { + PM_RUNTIME_ACQUIRE(cs35l56->base.dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = cs35l56_configure_onchip_spkid_pads(&cs35l56->base); + if (ret) + return ret; + + ret = cs35l56_read_onchip_spkid(&cs35l56->base); + if (ret < 0) + return ret; + + cs35l56->speaker_id = ret; + } if (!cs35l56->dsp.system_name && (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) { @@ -1208,6 +1221,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENOMEM; } + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name); + +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct dentry *debugfs_root = component->debugfs_root; + int ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); + if (!wait_for_completion_timeout(&cs35l56->init_completion, msecs_to_jiffies(5000))) { dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__); @@ -1219,6 +1245,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENOMEM; cs35l56->component = component; + ret = cs35l56_set_fw_name(component); + if (ret) + return ret; + ret = cs35l56_set_fw_suffix(cs35l56); if (ret) return ret; @@ -1532,6 +1562,105 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) return 0; } +static int cs35l56_read_fwnode_u32_array(struct device *dev, + struct fwnode_handle *parent_node, + const char *prop_name, + int max_count, + u32 *dest) +{ + int count, ret; + + count = fwnode_property_count_u32(parent_node, prop_name); + if ((count == 0) || (count == -EINVAL) || (count == -ENODATA)) { + dev_dbg(dev, "%s not found in %s\n", prop_name, fwnode_get_name(parent_node)); + return 0; + } + + if (count < 0) { + dev_err(dev, "Get %s error:%d\n", prop_name, count); + return count; + } + + if (count > max_count) { + dev_err(dev, "%s too many entries (%d)\n", prop_name, count); + return -EOVERFLOW; + } + + ret = fwnode_property_read_u32_array(parent_node, prop_name, dest, count); + if (ret) { + dev_err(dev, "Error reading %s: %d\n", prop_name, ret); + return ret; + } + + return count; +} + +static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56, + struct fwnode_handle *ext_node) +{ + static const char * const gpio_name = "01fa-spk-id-gpios-onchip"; + static const char * const pull_name = "01fa-spk-id-gpios-onchip-pull"; + u32 gpios[5], pulls[5]; + int num_gpios, num_pulls; + int ret; + + static_assert(ARRAY_SIZE(gpios) == ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios)); + static_assert(ARRAY_SIZE(pulls) == ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls)); + + num_gpios = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, gpio_name, + ARRAY_SIZE(gpios), gpios); + if (num_gpios < 1) + return num_gpios; + + num_pulls = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, pull_name, + ARRAY_SIZE(pulls), pulls); + if (num_pulls < 0) + return num_pulls; + + if (num_pulls != num_gpios) { + dev_warn(cs35l56->base.dev, "%s count(%d) != %s count(%d)\n", + pull_name, num_pulls, gpio_name, num_gpios); + } + + ret = cs35l56_check_and_save_onchip_spkid_gpios(&cs35l56->base, + gpios, num_gpios, + pulls, num_pulls); + if (ret) { + return dev_err_probe(cs35l56->base.dev, ret, "Error in %s/%s\n", + gpio_name, pull_name); + } + + return 0; +} + +VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56) +{ + struct fwnode_handle *ext_node = NULL; + struct fwnode_handle *link; + int ret; + + if (!cs35l56->sdw_peripheral) + return 0; + + fwnode_for_each_child_node(dev_fwnode(cs35l56->base.dev), link) { + ext_node = fwnode_get_named_child_node(link, + "mipi-sdca-function-expansion-subproperties"); + if (ext_node) { + fwnode_handle_put(link); + break; + } + } + + if (!ext_node) + return 0; + + ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node); + fwnode_handle_put(ext_node); + + return ret; +} +EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties); + static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) { struct device *dev = cs35l56->base.dev; @@ -1712,6 +1841,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) if (ret != 0) goto err; + ret = cs35l56_process_xu_properties(cs35l56); + if (ret) + goto err; + ret = cs35l56_dsp_init(cs35l56); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 7187885a13c1..691f857d0bd8 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -76,6 +76,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56); #if IS_ENABLED(CONFIG_KUNIT) int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56); +int cs35l56_set_fw_name(struct snd_soc_component *component); +int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56); #endif #endif /* ifndef CS35L56_H */ From 9bca0f05cea49ad11b464672ccdf6efd6a814a45 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 5 Feb 2026 16:48:37 +0000 Subject: [PATCH 314/341] ASoC: cs35l56-shared: KUnit tests for onchip speaker ID gpios Add KUnit testing of: cs35l56_check_and_save_onchip_spkid_gpios() cs35l56_configure_onchip_spkid_pads() cs35l56_read_onchip_spkid() The test consists of: - A mock regmap that simulates the pad and pin config registers. - Parameterization of the pin list, pulls list, a simulated value for each pin and the speaker ID value that this should produce. - A self-test of the simulated pin and GPIO registers. - A test that the value returned by cs35l56_read_onchip_spkid() is correct. - A test that the pin pull-up/down are set correctly by cs35l56_configure_onchip_spkid_pads() - A test that cs35l56_configure_onchip_spkid_pads() and cs35l56_read_onchip_spkid(0 return the expected values if cs35l56_base->num_onchip_spkid_gpios == 0. - A test that cs35l56_check_and_save_onchip_spkid_gpios() saves the configuration. - A test that cs35l56_check_and_save_onchip_spkid_gpios() rejects illegal GPIO numbers. - A test that cs35l56_check_and_save_onchip_spkid_gpios() rejects illegal pull types. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260205164838.1611295-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 14 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs35l56-shared-test.c | 680 +++++++++++++++++++++++++ sound/soc/codecs/cs35l56-shared.c | 5 + 4 files changed, 701 insertions(+) create mode 100644 sound/soc/codecs/cs35l56-shared-test.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e78ac302da15..adb3fb923be3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -936,6 +936,20 @@ config SND_SOC_CS35L56_TEST help This builds KUnit tests for the Cirrus Logic cs35l56 codec driver. + + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". + +config SND_SOC_CS35L56_SHARED_TEST + tristate "KUnit test for Cirrus Logic cs35l56-shared" if !KUNIT_ALL_TESTS + depends on SND_SOC_CS35L56_SHARED && KUNIT + default KUNIT_ALL_TESTS + help + This builds KUnit tests for the Cirrus Logic cs35l56-shared + module. + For more information on KUnit and unit tests in general, please refer to the KUnit documentation in Documentation/dev-tools/kunit/. diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c9e3b813653d..3ddee5298721 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,7 @@ snd-soc-cs35l45-spi-y := cs35l45-spi.o snd-soc-cs35l45-i2c-y := cs35l45-i2c.o snd-soc-cs35l56-y := cs35l56.o snd-soc-cs35l56-shared-y := cs35l56-shared.o +snd-soc-cs35l56-shared-test-y := cs35l56-shared-test.o snd-soc-cs35l56-i2c-y := cs35l56-i2c.o snd-soc-cs35l56-spi-y := cs35l56-spi.o snd-soc-cs35l56-sdw-y := cs35l56-sdw.o @@ -512,6 +513,7 @@ obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o +obj-$(CONFIG_SND_SOC_CS35L56_SHARED_TEST) += snd-soc-cs35l56-shared-test.o obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c new file mode 100644 index 000000000000..94db02aef7dc --- /dev/null +++ b/sound/soc/codecs/cs35l56-shared-test.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// KUnit test for the Cirrus Logic cs35l56-shared module. +// +// Copyright (C) 2026 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cs35l56_shared_test_priv { + struct kunit *test; + struct faux_device *amp_dev; + struct regmap *registers; + struct cs35l56_base *cs35l56_base; + u8 applied_pad_pull_state[CS35L56_MAX_GPIO]; +}; + +struct cs35l56_shared_test_param { + int spkid_gpios[4]; + int spkid_pulls[4]; + unsigned long gpio_status; + int spkid; +}; + +KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, + struct faux_device *) + +KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *) + +static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CS35L56_DSP1_PMEM_5114, + .cache_type = REGCACHE_MAPLE, +}; + +static const struct regmap_bus cs35l56_shared_test_mock_registers_regmap_bus = { + /* No handlers because it is always in cache-only */ +}; + +static unsigned int cs35l56_shared_test_read_gpio_status(struct cs35l56_shared_test_priv *priv) +{ + const struct cs35l56_shared_test_param *param = priv->test->param_value; + unsigned int reg_offs, pad_cfg, val; + unsigned int status = 0; + unsigned int mask = 1; + + for (reg_offs = 0; reg_offs < CS35L56_MAX_GPIO * sizeof(u32); reg_offs += sizeof(u32)) { + regmap_read(priv->registers, CS35L56_SYNC_GPIO1_CFG + reg_offs, &pad_cfg); + regmap_read(priv->registers, CS35L56_GPIO1_CTRL1 + reg_offs, &val); + + /* Only read a value if set as an input pin and as a GPIO */ + val &= (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK); + if ((pad_cfg & CS35L56_PAD_GPIO_IE) && + (val == (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO))) + status |= (param->gpio_status & mask); + + mask <<= 1; + } + + return status; +} + +static int cs35l56_shared_test_updt_gpio_pres(struct cs35l56_shared_test_priv *priv, + unsigned int reg, unsigned int val) +{ + int i, ret; + + ret = regmap_write(priv->registers, reg, val); + if (ret) + return ret; + + if (val & CS35L56_UPDT_GPIO_PRES) { + /* Simulate transferring register state to internal latches */ + for (i = 0; i < ARRAY_SIZE(priv->applied_pad_pull_state); i++) { + reg = CS35L56_SYNC_GPIO1_CFG + (i * sizeof(u32)); + regmap_read(priv->registers, reg, &val); + val = FIELD_GET(CS35L56_PAD_GPIO_PULL_MASK, val); + priv->applied_pad_pull_state[i] = val; + } + } + + return 0; +} + +static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct cs35l56_shared_test_priv *priv = context; + + switch (reg) { + case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: + case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1: + return regmap_read(priv->registers, reg, val); + case CS35L56_UPDATE_REGS: + *val = 0; + return 0; + case CS35L56_GPIO_STATUS1: + *val = cs35l56_shared_test_read_gpio_status(priv); + return 0; + default: + kunit_fail_current_test("Bad regmap read address %#x\n", reg); + return -EINVAL; + } +} + +static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct cs35l56_shared_test_priv *priv = context; + + switch (reg) { + case CS35L56_UPDATE_REGS: + return cs35l56_shared_test_updt_gpio_pres(priv, reg, val); + case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: + case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1: + return regmap_write(priv->registers, reg, val); + default: + kunit_fail_current_test("Bad regmap write address %#x\n", reg); + return -EINVAL; + } +} + +static const struct regmap_bus cs35l56_shared_test_regmap_bus = { + .reg_read = cs35l56_shared_test_reg_read, + .reg_write = cs35l56_shared_test_reg_write, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +/* + * Self-test that the mock GPIO registers obey the configuration bits. + * Other tests rely on the mocked registers only returning a GPIO state + * if the pin is correctly set as a GPIO input. + */ +static void cs35l56_shared_test_mock_gpio_status_selftest(struct kunit *test) +{ + const struct cs35l56_shared_test_param *param = test->param_value; + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + unsigned int reg, val; + + KUNIT_ASSERT_NOT_NULL(test, param); + + /* Set all pins non-GPIO and output. Mock GPIO_STATUS should read 0 */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + /* Set all pads as inputs */ + for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_PAD_GPIO_IE)); + + KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val)); + KUNIT_EXPECT_EQ(test, val, 0); + + /* Set all pins as GPIO outputs. Mock GPIO_STATUS should read 0 */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_FN_GPIO)); + + KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val)); + KUNIT_EXPECT_EQ(test, val, 0); + + /* Set all pins as non-GPIO inputs. Mock GPIO_STATUS should read 0 */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_DIR_MASK)); + + KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val)); + KUNIT_EXPECT_EQ(test, val, 0); + + /* Set all pins as GPIO inputs. Mock GPIO_STATUS should match param->gpio_status */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, + regmap_write(priv->registers, reg, + CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO)); + + KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val)); + KUNIT_EXPECT_EQ(test, val, param->gpio_status); + + /* Set all pads as outputs. Mock GPIO_STATUS should read 0 */ + for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val)); + KUNIT_EXPECT_EQ(test, val, 0); +} + +/* Test that the listed chip pins are assembled into a speaker ID integer. */ +static void cs35l56_shared_test_get_onchip_speaker_id(struct kunit *test) +{ + const struct cs35l56_shared_test_param *param = test->param_value; + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + unsigned int i, reg; + + /* Set all pins non-GPIO and output */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + /* Init GPIO array */ + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1; + cs35l56_base->num_onchip_spkid_gpios++; + } + + cs35l56_base->num_onchip_spkid_pulls = 0; + + KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0); + KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), param->spkid); +} + +/* Test that the listed chip pins and the corresponding pads are configured correctly. */ +static void cs35l56_shared_test_onchip_speaker_id_pad_config(struct kunit *test) +{ + const struct cs35l56_shared_test_param *param = test->param_value; + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + unsigned int i, reg, val; + + /* Init values in all pin registers */ + for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32)) + KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0)); + + /* Init GPIO array */ + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1; + cs35l56_base->num_onchip_spkid_gpios++; + } + + /* Init pulls array */ + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) { + if (param->spkid_pulls[i] < 0) + break; + + cs35l56_base->onchip_spkid_pulls[i] = param->spkid_pulls[i]; + cs35l56_base->num_onchip_spkid_pulls++; + } + + KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0); + + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + /* Pad should be an input */ + reg = CS35L56_SYNC_GPIO1_CFG + ((param->spkid_gpios[i] - 1) * sizeof(u32)); + KUNIT_EXPECT_EQ(test, regmap_read(priv->registers, reg, &val), 0); + KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_IE, CS35L56_PAD_GPIO_IE); + + /* Specified pulls should be set, others should be none */ + if (i < cs35l56_base->num_onchip_spkid_pulls) { + KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK, + FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, + param->spkid_pulls[i])); + } else { + KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK, + CS35L56_PAD_PULL_NONE); + } + + /* Pulls for all specfied GPIOs should have been transferred to AO latch */ + if (i < cs35l56_base->num_onchip_spkid_pulls) { + KUNIT_EXPECT_EQ(test, + priv->applied_pad_pull_state[param->spkid_gpios[i] - 1], + param->spkid_pulls[i]); + } else { + KUNIT_EXPECT_EQ(test, + priv->applied_pad_pull_state[param->spkid_gpios[i] - 1], + CS35L56_PAD_PULL_NONE); + } + } +} + +/* Test that the listed chip pins are stashed correctly. */ +static void cs35l56_shared_test_stash_onchip_spkid_pins(struct kunit *test) +{ + const struct cs35l56_shared_test_param *param = test->param_value; + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + u32 gpios[5], pulls[5]; + int i, num_gpios, num_pulls; + + static_assert(ARRAY_SIZE(gpios) >= ARRAY_SIZE(param->spkid_gpios)); + static_assert(ARRAY_SIZE(pulls) >= ARRAY_SIZE(param->spkid_pulls)); + + num_gpios = 0; + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + gpios[i] = (u32)param->spkid_gpios[i]; + num_gpios++; + } + + num_pulls = 0; + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) { + if (param->spkid_pulls[i] < 0) + break; + + pulls[i] = (u32)param->spkid_pulls[i]; + num_pulls++; + } + + cs35l56_base->num_onchip_spkid_gpios = 0; + cs35l56_base->num_onchip_spkid_pulls = 0; + + KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)); + KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)); + + KUNIT_EXPECT_EQ(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, num_gpios, + pulls, num_pulls), + 0); + + KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_gpios, num_gpios); + KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_pulls, num_pulls); + + /* GPIO numbers are adjusted from 1-based to 0-based */ + for (i = 0; i < num_gpios; i++) + KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_gpios[i], gpios[i] - 1); + + for (i = 0; i < num_pulls; i++) + KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_pulls[i], pulls[i]); +} + +/* Test that illegal GPIO numbers are rejected. */ +static void cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid(struct kunit *test) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + u32 gpios[8] = { }, pulls[8] = { }; + + KUNIT_EXPECT_LE(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, 1, + pulls, 0), + 0); + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + gpios[0] = CS35L56_MAX_GPIO + 1; + break; + case 0x63: + gpios[0] = CS35L63_MAX_GPIO + 1; + break; + default: + kunit_fail_current_test("Unsupported type:%#x\n", cs35l56_base->type); + return; + } + KUNIT_EXPECT_LE(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, 1, + pulls, 0), + 0); + + gpios[0] = 1; + pulls[0] = 3; + KUNIT_EXPECT_LE(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, 1, + pulls, 1), + 0); + + static_assert(ARRAY_SIZE(gpios) > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)); + static_assert(ARRAY_SIZE(pulls) > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)); + KUNIT_EXPECT_EQ(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, ARRAY_SIZE(gpios), + pulls, 0), + -EOVERFLOW); + KUNIT_EXPECT_EQ(test, + cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base, + gpios, 1, + pulls, ARRAY_SIZE(pulls)), + -EOVERFLOW); +} + +static void cs35l56_shared_test_onchip_speaker_id_not_defined(struct kunit *test) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base = priv->cs35l56_base; + + memset(cs35l56_base->onchip_spkid_gpios, 0, sizeof(cs35l56_base->onchip_spkid_gpios)); + memset(cs35l56_base->onchip_spkid_pulls, 0, sizeof(cs35l56_base->onchip_spkid_pulls)); + cs35l56_base->num_onchip_spkid_gpios = 0; + cs35l56_base->num_onchip_spkid_pulls = 0; + KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0); + KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), -ENOENT); +} + +static int cs35l56_shared_test_case_regmap_init(struct kunit *test, + const struct regmap_config *regmap_config) +{ + struct cs35l56_shared_test_priv *priv = test->priv; + struct cs35l56_base *cs35l56_base; + + /* + * Create a dummy regmap to simulate a register map by holding the + * values of all simulated registers in the regmap cache. + */ + priv->registers = regmap_init(&priv->amp_dev->dev, + &cs35l56_shared_test_mock_registers_regmap_bus, + priv, + &cs35l56_shared_test_mock_registers_regmap); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->registers); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, regmap_exit_wrapper, + priv->registers)); + regcache_cache_only(priv->registers, true); + + /* Create dummy regmap for cs35l56 driver */ + cs35l56_base = priv->cs35l56_base; + cs35l56_base->regmap = regmap_init(cs35l56_base->dev, + &cs35l56_shared_test_regmap_bus, + priv, + regmap_config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cs35l56_base->regmap); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, regmap_exit_wrapper, + cs35l56_base->regmap)); + + return 0; +} + +static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 rev, + const struct regmap_config *regmap_config) +{ + struct cs35l56_shared_test_priv *priv; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + priv->test = test; + + /* Create dummy amp driver dev */ + priv->amp_dev = faux_device_create("cs35l56_shared_test_drv", NULL, NULL); + KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + faux_device_destroy_wrapper, + priv->amp_dev)); + + priv->cs35l56_base = kunit_kzalloc(test, sizeof(*priv->cs35l56_base), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_base); + priv->cs35l56_base->dev = &priv->amp_dev->dev; + priv->cs35l56_base->type = type; + priv->cs35l56_base->rev = rev; + + if (regmap_config) { + ret = cs35l56_shared_test_case_regmap_init(test, regmap_config); + if (ret) + return ret; + } + + return 0; +} + +static int cs35l56_shared_test_case_regmap_init_L56_B0_sdw(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_sdw); +} + +static int cs35l56_shared_test_case_regmap_init_L56_B0_spi(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_spi); +} + +static int cs35l56_shared_test_case_regmap_init_L56_B0_i2c(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_i2c); +} + +static int cs35l56_shared_test_case_regmap_init_L56_B2_sdw(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_sdw); +} + +static int cs35l56_shared_test_case_regmap_init_L56_B2_spi(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_spi); +} + +static int cs35l56_shared_test_case_regmap_init_L56_B2_i2c(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_i2c); +} + +static int cs35l56_shared_test_case_regmap_init_L63_A1_sdw(struct kunit *test) +{ + return cs35l56_shared_test_case_base_init(test, 0x63, 0xa1, &cs35l63_regmap_sdw); +} + +static void cs35l56_shared_test_gpio_param_desc(const struct cs35l56_shared_test_param *param, + char *desc) +{ + DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios))); + DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls))); + int i; + + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]); + } + + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) { + if (param->spkid_pulls[i] < 0) + break; + + seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]); + } + + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s} status:%#lx spkid:%d", + seq_buf_str(&gpios), seq_buf_str(&pulls), param->gpio_status, param->spkid); +} + +static const struct cs35l56_shared_test_param cs35l56_shared_test_gpios_selftest_cases[] = { + { .spkid_gpios = { -1 }, .gpio_status = GENMASK(12, 0) }, +}; +KUNIT_ARRAY_PARAM(cs35l56_shared_test_gpios_selftest, + cs35l56_shared_test_gpios_selftest_cases, + cs35l56_shared_test_gpio_param_desc); + +static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_cases[] = { + { .spkid_gpios = { 1, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 1, -1 }, .gpio_status = ~BIT(0), .spkid = 0 }, + { .spkid_gpios = { 1, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + + { .spkid_gpios = { 7, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 7, -1 }, .gpio_status = ~BIT(6), .spkid = 0 }, + { .spkid_gpios = { 7, -1 }, .gpio_status = BIT(6), .spkid = 1 }, + + { .spkid_gpios = { 1, 7, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 1, 7, -1 }, .gpio_status = ~(BIT(0) | BIT(6)), .spkid = 0 }, + { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6), .spkid = 1 }, + { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(0), .spkid = 2 }, + { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + + { .spkid_gpios = { 7, 1, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 7, 1, -1 }, .gpio_status = ~(BIT(6) | BIT(0)), .spkid = 0 }, + { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 }, + { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = 0, .spkid = 0 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2), .spkid = 4 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(0), .spkid = 5 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6), .spkid = 6 }, + { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6) | BIT(0), .spkid = 7 }, +}; +KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid, cs35l56_shared_test_onchip_spkid_cases, + cs35l56_shared_test_gpio_param_desc); + +static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_pull_cases[] = { + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, }, + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, }, + + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, }, + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, }, + + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, }, + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, }, + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, }, + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, }, +}; +KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid_pull, + cs35l56_shared_test_onchip_spkid_pull_cases, + cs35l56_shared_test_gpio_param_desc); + +static struct kunit_case cs35l56_shared_test_cases[] = { + /* Tests for speaker id */ + KUNIT_CASE_PARAM(cs35l56_shared_test_mock_gpio_status_selftest, + cs35l56_shared_test_gpios_selftest_gen_params), + KUNIT_CASE_PARAM(cs35l56_shared_test_get_onchip_speaker_id, + cs35l56_shared_test_onchip_spkid_gen_params), + KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config, + cs35l56_shared_test_onchip_spkid_gen_params), + KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config, + cs35l56_shared_test_onchip_spkid_pull_gen_params), + KUNIT_CASE_PARAM(cs35l56_shared_test_stash_onchip_spkid_pins, + cs35l56_shared_test_onchip_spkid_pull_gen_params), + KUNIT_CASE(cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid), + KUNIT_CASE(cs35l56_shared_test_onchip_speaker_id_not_defined), + { } +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B0_sdw = { + .name = "snd-soc-cs35l56-shared-test_L56_B0_sdw", + .init = cs35l56_shared_test_case_regmap_init_L56_B0_sdw, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B2_sdw = { + .name = "snd-soc-cs35l56-shared-test_L56_B2_sdw", + .init = cs35l56_shared_test_case_regmap_init_L56_B2_sdw, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L63_A1_sdw = { + .name = "snd-soc-cs35l56-shared-test_L63_A1_sdw", + .init = cs35l56_shared_test_case_regmap_init_L63_A1_sdw, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B0_spi = { + .name = "snd-soc-cs35l56-shared-test_L56_B0_spi", + .init = cs35l56_shared_test_case_regmap_init_L56_B0_spi, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B2_spi = { + .name = "snd-soc-cs35l56-shared-test_L56_B2_spi", + .init = cs35l56_shared_test_case_regmap_init_L56_B2_spi, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B0_i2c = { + .name = "snd-soc-cs35l56-shared-test_L56_B0_i2c", + .init = cs35l56_shared_test_case_regmap_init_L56_B0_i2c, + .test_cases = cs35l56_shared_test_cases, +}; + +static struct kunit_suite cs35l56_shared_test_suite_L56_B2_i2c = { + .name = "snd-soc-cs35l56-shared-test_L56_B2_i2c", + .init = cs35l56_shared_test_case_regmap_init_L56_B2_i2c, + .test_cases = cs35l56_shared_test_cases, +}; + +kunit_test_suites( + &cs35l56_shared_test_suite_L56_B0_sdw, + &cs35l56_shared_test_suite_L56_B2_sdw, + &cs35l56_shared_test_suite_L63_A1_sdw, + + &cs35l56_shared_test_suite_L56_B0_spi, + &cs35l56_shared_test_suite_L56_B2_spi, + + &cs35l56_shared_test_suite_L56_B0_i2c, + &cs35l56_shared_test_suite_L56_B2_i2c, +); + +MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); +MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +MODULE_DESCRIPTION("KUnit test for cs35l56-shared module"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 55c75b9e4172..4707f28bfca2 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -5,6 +5,7 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include #include #include #include @@ -1630,6 +1631,8 @@ int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base) int num_gpios, num_pulls; int i, ret; + KUNIT_STATIC_STUB_REDIRECT(cs35l56_configure_onchip_spkid_pads, cs35l56_base); + if (cs35l56_base->num_onchip_spkid_gpios == 0) return 0; @@ -1680,6 +1683,8 @@ int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base) int speaker_id = 0; int i, ret; + KUNIT_STATIC_STUB_REDIRECT(cs35l56_read_onchip_spkid, cs35l56_base); + if (cs35l56_base->num_onchip_spkid_gpios == 0) return -ENOENT; From 6f220440399afba29165e0597fa2c3aa836191d7 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 5 Feb 2026 16:48:38 +0000 Subject: [PATCH 315/341] ASoC: cs35l56: KUnit tests for parsing and using onchip GPIOs Add KUnit test cases for: - cs35l56_process_xu_properties() which reads the onchip GPIO definitions. - cs35l56_set_fw_name() calls functions to configure and set the GPIOs. - cs35l56_set_fw_name() saves the ID value in cs35l56_priv.speaker_id. - cs35l56_set_fw_name() does not overwrite a speaker ID that was already found some other way. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260205164838.1611295-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-test.c | 273 ++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c index a7b21660c402..b6c8c08e3ade 100644 --- a/sound/soc/codecs/cs35l56-test.c +++ b/sound/soc/codecs/cs35l56-test.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,16 +25,46 @@ KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, struct faux_device *) +KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper, + software_node_unregister_node_group, + const struct software_node * const *) + +KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper, + software_node_unregister, + const struct software_node *) + +KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper, + device_remove_software_node, + struct device *) + struct cs35l56_test_priv { struct faux_device *amp_dev; struct cs35l56_private *cs35l56_priv; const char *ssidexv2; + + bool read_onchip_spkid_called; + bool configure_onchip_spkid_pads_called; }; struct cs35l56_test_param { u8 type; u8 rev; + + s32 spkid_gpios[4]; + s32 spkid_pulls[4]; +}; + +static const struct software_node cs35l56_test_dev_sw_node = + SOFTWARE_NODE("SWD1", NULL, NULL); + +static const struct software_node cs35l56_test_af01_sw_node = + SOFTWARE_NODE("AF01", NULL, &cs35l56_test_dev_sw_node); + +static const struct software_node *cs35l56_test_dev_and_af01_node_group[] = { + &cs35l56_test_dev_sw_node, + &cs35l56_test_af01_sw_node, + NULL }; static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev, @@ -232,6 +264,190 @@ static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test) KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); } +/* + * Test that cs35l56_process_xu_properties() correctly parses the GPIO and + * pull values from properties into the arrays in struct cs35l56_base. + * + * This test creates the node tree: + * + * Node("SWD1") { // top-level device node + * Node("AF01") { + * Node("mipi-sdca-function-expansion-subproperties") { + * property: "01fa-spk-id-gpios-onchip" + * property: 01fa-spk-id-gpios-onchip-pull + * } + * } + * } + * + * Note that in ACPI "mipi-sdca-function-expansion-subproperties" is + * a special _DSD property that points to a Device(EXT0) node but behaves + * as an alias of the EXT0 node. The equivalent in software nodes is to + * create a Node named "mipi-sdca-function-expansion-subproperties" with + * the properties. + * + */ +static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test) +{ + const struct cs35l56_test_param *param = test->param_value; + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + struct software_node *ext0_node; + int num_gpios = 0; + int num_pulls = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) { + if (param->spkid_gpios[i] < 0) + break; + } + KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios)); + + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) { + if (param->spkid_pulls[i] < 0) + break; + } + KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls)); + + const struct property_entry ext0_props[] = { + PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip", + param->spkid_gpios, num_gpios), + PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip-pull", + param->spkid_pulls, num_pulls), + { } + }; + + KUNIT_ASSERT_EQ(test, + software_node_register_node_group(cs35l56_test_dev_and_af01_node_group), + 0); + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, + software_node_unregister_node_group_wrapper, + cs35l56_test_dev_and_af01_node_group), + 0); + + ext0_node = kunit_kzalloc(test, sizeof(*ext0_node), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ext0_node); + *ext0_node = SOFTWARE_NODE("mipi-sdca-function-expansion-subproperties", + ext0_props, &cs35l56_test_af01_sw_node); + + KUNIT_ASSERT_EQ(test, software_node_register(ext0_node), 0); + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, + software_node_unregister_wrapper, + ext0_node), + 0); + + KUNIT_ASSERT_EQ(test, + device_add_software_node(cs35l56->base.dev, &cs35l56_test_dev_sw_node), 0); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + device_remove_software_node_wrapper, + cs35l56->base.dev)); + + KUNIT_EXPECT_EQ(test, cs35l56_process_xu_properties(cs35l56), 0); + + KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_gpios, num_gpios); + KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_pulls, num_pulls); + + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + /* + * cs35l56_process_xu_properties() stores the GPIO numbers + * zero-based, which is one less than the value in the property. + */ + KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_gpios[i], + param->spkid_gpios[i] - 1, + "i=%d", i); + } + + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) { + if (param->spkid_pulls[i] < 0) + break; + + KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_pulls[i], + param->spkid_pulls[i], "i=%d", i); + } +} + +static int cs35l56_test_dummy_read_onchip_spkid(struct cs35l56_base *cs35l56_base) +{ + struct kunit *test = kunit_get_current_test(); + struct cs35l56_test_priv *priv = test->priv; + + priv->read_onchip_spkid_called = true; + + return 4; +} + +static int cs35l56_test_dummy_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base) +{ + struct kunit *test = kunit_get_current_test(); + struct cs35l56_test_priv *priv = test->priv; + + priv->configure_onchip_spkid_pads_called = true; + + return 0; +} + +static void cs35l56_test_set_fw_name_reads_onchip_spkid(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Provide some on-chip GPIOs for spkid */ + cs35l56->base.onchip_spkid_gpios[0] = 1; + cs35l56->base.num_onchip_spkid_gpios = 1; + + cs35l56->speaker_id = -ENOENT; + + kunit_activate_static_stub(test, + cs35l56_configure_onchip_spkid_pads, + cs35l56_test_dummy_configure_onchip_spkid_pads); + kunit_activate_static_stub(test, + cs35l56_read_onchip_spkid, + cs35l56_test_dummy_read_onchip_spkid); + + priv->configure_onchip_spkid_pads_called = false; + priv->read_onchip_spkid_called = false; + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_TRUE(test, priv->configure_onchip_spkid_pads_called); + KUNIT_EXPECT_TRUE(test, priv->read_onchip_spkid_called); + KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, + cs35l56_test_dummy_read_onchip_spkid(&cs35l56->base)); +} + +static void cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + /* Provide some on-chip GPIOs for spkid */ + cs35l56->base.onchip_spkid_gpios[0] = 1; + cs35l56->base.num_onchip_spkid_gpios = 1; + + /* Simulate that the driver already got a spkid from somewhere */ + cs35l56->speaker_id = 15; + + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15); +} + +static void cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios(struct kunit *test) +{ + struct cs35l56_test_priv *priv = test->priv; + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; + + cs35l56->base.num_onchip_spkid_gpios = 0; + + /* Simulate that the driver already got a spkid from somewhere */ + cs35l56->speaker_id = 15; + + KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0); + KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15); +} + static int cs35l56_test_case_init_common(struct kunit *test) { struct cs35l56_test_priv *priv; @@ -263,6 +479,7 @@ static int cs35l56_test_case_init_common(struct kunit *test) cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, cs35l56->component); cs35l56->component->dev = cs35l56->base.dev; + snd_soc_component_set_drvdata(cs35l56->component, cs35l56); cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card), GFP_KERNEL); @@ -299,6 +516,50 @@ static int cs35l56_test_case_init_soundwire(struct kunit *test) return 0; } +static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, char *desc) +{ + DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios))); + DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls))); + int i; + + for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) { + if (param->spkid_gpios[i] < 0) + break; + + seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]); + } + + for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) { + if (param->spkid_pulls[i] < 0) + break; + + seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]); + } + + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s}", + seq_buf_str(&gpios), seq_buf_str(&pulls)); +} + +static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = { + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, }, + { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, }, + + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, }, + { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, }, + + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, }, + { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, }, + { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, }, + + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, }, + { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, }, +}; +KUNIT_ARRAY_PARAM(cs35l56_test_onchip_spkid, + cs35l56_test_onchip_spkid_cases, + cs35l56_test_gpio_param_desc); + static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param, char *desc) { @@ -331,6 +592,13 @@ static struct kunit_case cs35l56_test_cases_soundwire[] = { cs35l56_test_type_rev_ex_b0_gen_params), KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw), + KUNIT_CASE_PARAM(cs35l56_test_parse_xu_onchip_spkid, + cs35l56_test_onchip_spkid_gen_params), + + KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid), + KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios), + KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios), + { } /* terminator */ }; @@ -339,6 +607,10 @@ static struct kunit_case cs35l56_test_cases_not_soundwire[] = { KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params), + KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid), + KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios), + KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios), + { } /* terminator */ }; @@ -360,6 +632,7 @@ kunit_test_suites( ); MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver"); MODULE_AUTHOR("Richard Fitzgerald "); From 84faa91585fa22a161763f2fe8f84a602a196c87 Mon Sep 17 00:00:00 2001 From: Ziyi Guo Date: Thu, 5 Feb 2026 05:24:29 +0000 Subject: [PATCH 316/341] ASoC: fsl: imx-rpmsg: use snd_soc_find_dai_with_mutex() in probe imx_rpmsg_probe() calls snd_soc_find_dai() without holding client_mutex. However, snd_soc_find_dai() has lockdep_assert_held(&client_mutex) indicating callers must hold this lock, as the function iterates over the global component list. All other callers of snd_soc_find_dai() either hold client_mutex via the snd_soc_bind_card() path or use the snd_soc_find_dai_with_mutex() wrapper. Use snd_soc_find_dai_with_mutex() instead to fix the missing lock protection. Signed-off-by: Ziyi Guo Reviewed-by: Frank Li Link: https://patch.msgid.link/20260205052429.4046903-1-n7l8m4@u.northwestern.edu Signed-off-by: Mark Brown --- sound/soc/fsl/imx-rpmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 53f04d1f3280..76a8e68c1b62 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -145,7 +145,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai.ignore_pmdown_time = 1; data->dai.cpus->dai_name = pdev->dev.platform_data; - cpu_dai = snd_soc_find_dai(data->dai.cpus); + cpu_dai = snd_soc_find_dai_with_mutex(data->dai.cpus); if (!cpu_dai) { ret = -EPROBE_DEFER; goto fail; From 27b5096ef0f3a78441128b993f0353a77f2f5f53 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 3 Feb 2026 17:46:23 +0100 Subject: [PATCH 317/341] ASoC: rockchip: spdif: Use device_get_match_data() Use device_get_match_data(), so that the probe routine does not directly reference the of_match_table. This allows moving the table at the end of the file where most recent drivers have it. Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-1-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index d365168934dc..23f14274a6e0 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -283,14 +283,14 @@ static const struct regmap_config rk_spdif_regmap_config = { static int rk_spdif_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + enum rk_spdif_type spdif_type; struct rk_spdif_dev *spdif; - const struct of_device_id *match; struct resource *res; void __iomem *regs; int ret; - match = of_match_node(rk_spdif_match, np); - if (match->data == (void *)RK_SPDIF_RK3288) { + spdif_type = (uintptr_t) device_get_match_data(&pdev->dev); + if (spdif_type == RK_SPDIF_RK3288) { struct regmap *grf; grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); From 45df1f66b99340e1d6e90501f1e938400a3e9768 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 3 Feb 2026 17:46:24 +0100 Subject: [PATCH 318/341] ASoC: rockchip: spdif: Move DT compatible table Move rk_spdif_match DT compatible table to the usual place before the platform-driver struct definition and drop the useless of_match_ptr(), since it is fine to reference the DT id table even when OF support is disabled (which makes the driver useless anyways). Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-2-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 23f14274a6e0..331a23d5173f 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -40,29 +40,6 @@ struct rk_spdif_dev { struct regmap *regmap; }; -static const struct of_device_id rk_spdif_match[] __maybe_unused = { - { .compatible = "rockchip,rk3066-spdif", - .data = (void *)RK_SPDIF_RK3066 }, - { .compatible = "rockchip,rk3188-spdif", - .data = (void *)RK_SPDIF_RK3188 }, - { .compatible = "rockchip,rk3228-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - { .compatible = "rockchip,rk3288-spdif", - .data = (void *)RK_SPDIF_RK3288 }, - { .compatible = "rockchip,rk3328-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - { .compatible = "rockchip,rk3366-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - { .compatible = "rockchip,rk3368-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - { .compatible = "rockchip,rk3399-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - { .compatible = "rockchip,rk3568-spdif", - .data = (void *)RK_SPDIF_RK3366 }, - {}, -}; -MODULE_DEVICE_TABLE(of, rk_spdif_match); - static int rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); @@ -377,12 +354,35 @@ static const struct dev_pm_ops rk_spdif_pm_ops = { RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL) }; +static const struct of_device_id rk_spdif_match[] = { + { .compatible = "rockchip,rk3066-spdif", + .data = (void *)RK_SPDIF_RK3066 }, + { .compatible = "rockchip,rk3188-spdif", + .data = (void *)RK_SPDIF_RK3188 }, + { .compatible = "rockchip,rk3228-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3288-spdif", + .data = (void *)RK_SPDIF_RK3288 }, + { .compatible = "rockchip,rk3328-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3366-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3368-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3399-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3568-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk_spdif_match); + static struct platform_driver rk_spdif_driver = { .probe = rk_spdif_probe, .remove = rk_spdif_remove, .driver = { .name = "rockchip-spdif", - .of_match_table = of_match_ptr(rk_spdif_match), + .of_match_table = rk_spdif_match, .pm = pm_ptr(&rk_spdif_pm_ops), }, }; From 7e2de68e4dbaee65864b0c5972e1b03037243632 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 3 Feb 2026 17:46:25 +0100 Subject: [PATCH 319/341] ASoC: rockchip: spdif: Fully convert to device managed resources This driver mixes device managed resources with unmanaged ones and (as a lot of them do) gets the order wrong resulting in potential race condition problems at module removal time. Let's go to full device managed resources to cleanup the code and get rid of the potential race condition. Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-3-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 37 +++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 331a23d5173f..841ef499ed7f 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -257,6 +257,14 @@ static const struct regmap_config rk_spdif_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static void rk_spdif_suspend(void *data) +{ + struct device *dev = data; + + if (!pm_runtime_status_suspended(dev)) + rk_spdif_runtime_suspend(dev); +} + static int rk_spdif_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -311,11 +319,16 @@ static int rk_spdif_probe(struct platform_device *pdev) spdif->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, spdif); - pm_runtime_enable(&pdev->dev); + ret = devm_add_action_or_reset(&pdev->dev, rk_spdif_suspend, &pdev->dev); + if (ret) + return ret; + + devm_pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { ret = rk_spdif_runtime_resume(&pdev->dev); if (ret) - goto err_pm_runtime; + return ret; } ret = devm_snd_soc_register_component(&pdev->dev, @@ -323,31 +336,16 @@ static int rk_spdif_probe(struct platform_device *pdev) &rk_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); - goto err_pm_suspend; + return ret; } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); - goto err_pm_suspend; + return ret; } return 0; - -err_pm_suspend: - if (!pm_runtime_status_suspended(&pdev->dev)) - rk_spdif_runtime_suspend(&pdev->dev); -err_pm_runtime: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static void rk_spdif_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - rk_spdif_runtime_suspend(&pdev->dev); } static const struct dev_pm_ops rk_spdif_pm_ops = { @@ -379,7 +377,6 @@ MODULE_DEVICE_TABLE(of, rk_spdif_match); static struct platform_driver rk_spdif_driver = { .probe = rk_spdif_probe, - .remove = rk_spdif_remove, .driver = { .name = "rockchip-spdif", .of_match_table = rk_spdif_match, From 730b0af2748a74528e0ad25f2bcd3272e96db10a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 3 Feb 2026 17:46:26 +0100 Subject: [PATCH 320/341] ASoC: rockchip: spdif: Use dev_err_probe Cleanup the probe routine a little bit by using dev_err_probe instead of dev_err. Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-4-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 841ef499ed7f..1c62fcb0d8d4 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -279,11 +279,9 @@ static int rk_spdif_probe(struct platform_device *pdev) struct regmap *grf; grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR(grf)) { - dev_err(&pdev->dev, + if (IS_ERR(grf)) + return dev_err_probe(&pdev->dev, PTR_ERR(grf), "rockchip_spdif missing 'rockchip,grf'\n"); - return PTR_ERR(grf); - } /* Select the 8 channel SPDIF solution on RK3288 as * the 2 channel one does not appear to work @@ -334,16 +332,12 @@ static int rk_spdif_probe(struct platform_device *pdev) ret = devm_snd_soc_register_component(&pdev->dev, &rk_spdif_component, &rk_spdif_dai, 1); - if (ret) { - dev_err(&pdev->dev, "Could not register DAI\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) { - dev_err(&pdev->dev, "Could not register PCM\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); return 0; } From 72bcc223032cb71e640e466eb644537e369959a5 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 3 Feb 2026 17:46:27 +0100 Subject: [PATCH 321/341] ASoC: rockchip: spdif: Improve sample rate support The hardware supports all sample rates up to 192kHz. Signed-off-by: Sugar Zhang Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-5-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 1c62fcb0d8d4..55d64fd4c93b 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -188,11 +188,7 @@ static struct snd_soc_dai_driver rk_spdif_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = (SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000), + .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE), From 7bdde9a2fd6577e18cd99e4f0e71e466ba626e77 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 3 Feb 2026 17:46:28 +0100 Subject: [PATCH 322/341] ASoC: rockchip: spdif: Swap PCM and DAI component registration order PCM should be registered before the DAI component, as the second one triggers snd_soc_try_rebind_card. Signed-off-by: Sugar Zhang Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-6-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 55d64fd4c93b..c1221ff00ed7 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -325,16 +325,16 @@ static int rk_spdif_probe(struct platform_device *pdev) return ret; } + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); + 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"); - 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 0; } From 298082783a0d6d07109f8ce09ab410a1de12876d Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 3 Feb 2026 17:46:29 +0100 Subject: [PATCH 323/341] ASoC: rockchip: spdif: Add support for set mclk rate Allow setting the mclk rate from the machine driver. Signed-off-by: Sugar Zhang Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-7-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 34 +++++++++++++++++++---------- sound/soc/rockchip/rockchip_spdif.h | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index c1221ff00ed7..3b66d97f0582 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -86,12 +86,15 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int mclk_rate = clk_get_rate(spdif->mclk); unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; - int srate, mclk; + int bmc, div; int ret; - srate = params_rate(params); - mclk = srate * 128; + /* bmc = 128fs */ + bmc = 128 * params_rate(params); + div = DIV_ROUND_CLOSEST(mclk_rate, bmc); + val |= SPDIF_CFGR_CLK_DIV(div); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -107,14 +110,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* Set clock and calculate divider */ - ret = clk_set_rate(spdif->mclk, mclk); - if (ret != 0) { - dev_err(spdif->dev, "Failed to set module clock rate: %d\n", - ret); - return ret; - } - ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | @@ -177,7 +172,24 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai) return 0; } +static int rk_spdif_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + int ret; + + if (!freq) + return 0; + + ret = clk_set_rate(spdif->mclk, freq); + if (ret) + dev_err(spdif->dev, "Failed to set mclk: %d\n", ret); + + return ret; +} + static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .set_sysclk = rk_spdif_set_sysclk, .probe = rk_spdif_dai_probe, .hw_params = rk_spdif_hw_params, .trigger = rk_spdif_trigger, diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h index d8be9aae5b19..fcc28b6c4f58 100644 --- a/sound/soc/rockchip/rockchip_spdif.h +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -15,7 +15,7 @@ */ #define SPDIF_CFGR_CLK_DIV_SHIFT (16) #define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) -#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLK_DIV(x) ((x-1) << SPDIF_CFGR_CLK_DIV_SHIFT) #define SPDIF_CFGR_HALFWORD_SHIFT 2 #define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) From c43ec509019842c0b836e858dc486c216b51b087 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 3 Feb 2026 17:46:30 +0100 Subject: [PATCH 324/341] ASoC: rockchip: spdif: Add support for format S32_LE Treat 32 bit sample width as if it was 24 bits using only the 24 most significant bits. [I've merged the channel-swapping fix from Zohn Ni into Sugar Zhang's patch introducing the problem in the first place] Co-developed-by: Zohn Ni Signed-off-by: Zohn Ni Signed-off-by: Sugar Zhang Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-8-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 22 ++++++++++++++++++++-- sound/soc/rockchip/rockchip_spdif.h | 8 ++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 3b66d97f0582..d06573f22805 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -99,21 +99,38 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val |= SPDIF_CFGR_VDW_16; + val |= SPDIF_CFGR_ADJ_RIGHT_J; break; case SNDRV_PCM_FORMAT_S20_3LE: val |= SPDIF_CFGR_VDW_20; + val |= SPDIF_CFGR_ADJ_RIGHT_J; break; case SNDRV_PCM_FORMAT_S24_LE: val |= SPDIF_CFGR_VDW_24; + val |= SPDIF_CFGR_ADJ_RIGHT_J; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val |= SPDIF_CFGR_VDW_24; + val |= SPDIF_CFGR_ADJ_LEFT_J; break; default: return -EINVAL; } + /* + * clear MCLK domain logic before setting Fmclk and Fsdo to ensure + * that switching between S16_LE and S32_LE audio does not result + * in accidential channels swap. + */ + regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLR_MASK, + SPDIF_CFGR_CLR_EN); + udelay(1); + ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | - SDPIF_CFGR_VDW_MASK, val); + SDPIF_CFGR_VDW_MASK | + SPDIF_CFGR_ADJ_MASK, val); return ret; } @@ -203,7 +220,8 @@ static struct snd_soc_dai_driver rk_spdif_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_LE), + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), }, .ops = &rk_spdif_dai_ops, }; diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h index fcc28b6c4f58..acf64986a2e0 100644 --- a/sound/soc/rockchip/rockchip_spdif.h +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -17,6 +17,14 @@ #define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) #define SPDIF_CFGR_CLK_DIV(x) ((x-1) << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLR_MASK BIT(7) +#define SPDIF_CFGR_CLR_EN BIT(7) +#define SPDIF_CFGR_CLR_DIS 0 + +#define SPDIF_CFGR_ADJ_MASK BIT(3) +#define SPDIF_CFGR_ADJ_LEFT_J BIT(3) +#define SPDIF_CFGR_ADJ_RIGHT_J 0 + #define SPDIF_CFGR_HALFWORD_SHIFT 2 #define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) #define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT) From 07a791020be9b190d4a57b5ee823ede1e98df493 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 3 Feb 2026 17:46:31 +0100 Subject: [PATCH 325/341] ASoC: rockchip: spdif: Fill IEC958 CS info per params Add support to fill IEC958 channel status information. Signed-off-by: Sugar Zhang Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-9-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 1 + sound/soc/rockchip/rockchip_spdif.c | 45 ++++++++++++++++++++++++++--- sound/soc/rockchip/rockchip_spdif.h | 8 +++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index bd210fafe9fe..391ce2225fde 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ROCKCHIP_SAI config SND_SOC_ROCKCHIP_SPDIF tristate "Rockchip SPDIF Device Driver" + select SND_PCM_IEC958 select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF driver for diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index d06573f22805..e64c24897f29 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "rockchip_spdif.h" @@ -27,7 +28,25 @@ enum rk_spdif_type { RK_SPDIF_RK3366, }; -#define RK3288_GRF_SOC_CON2 0x24c +/* + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * CS0: | Mode | d | c | b | a | + * CS1: | Category Code | + * CS2: | Channel Number | Source Number | + * CS3: | Clock Accuracy | Sample Freq | + * CS4: | Ori Sample Freq | Word Length | + * CS5: | | CGMS-A | + * CS6~CS23: Reserved + * + * a: use of channel status block + * b: linear PCM identification: 0 for lpcm, 1 for nlpcm + * c: copyright information + * d: additional format information + */ +#define CS_BYTE 6 +#define CS_FRAME(c) ((c) << 16 | (c)) + +#define RK3288_GRF_SOC_CON2 0x24c struct rk_spdif_dev { struct device *dev; @@ -88,8 +107,20 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); unsigned int mclk_rate = clk_get_rate(spdif->mclk); unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; - int bmc, div; - int ret; + int bmc, div, ret, i; + u16 *fc; + u8 cs[CS_BYTE]; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, sizeof(cs)); + if (ret < 0) + return ret; + + fc = (u16 *)cs; + for (i = 0; i < CS_BYTE / 2; i++) + regmap_write(spdif->regmap, SPDIF_CHNSRn(i), CS_FRAME(fc[i])); + + regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CSE_MASK, + SPDIF_CFGR_CSE_EN); /* bmc = 128fs */ bmc = 128 * params_rate(params); @@ -239,6 +270,9 @@ static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg) case SPDIF_INTCR: case SPDIF_XFER: case SPDIF_SMPDR: + case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11): + case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11): + case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11): return true; default: return false; @@ -254,6 +288,9 @@ static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg) case SPDIF_INTSR: case SPDIF_XFER: case SPDIF_SMPDR: + case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11): + case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11): + case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11): return true; default: return false; @@ -276,7 +313,7 @@ static const struct regmap_config rk_spdif_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = SPDIF_SMPDR, + .max_register = SPDIF_VERSION, .writeable_reg = rk_spdif_wr_reg, .readable_reg = rk_spdif_rd_reg, .volatile_reg = rk_spdif_volatile_reg, diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h index acf64986a2e0..b837b1f8d57f 100644 --- a/sound/soc/rockchip/rockchip_spdif.h +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -21,6 +21,10 @@ #define SPDIF_CFGR_CLR_EN BIT(7) #define SPDIF_CFGR_CLR_DIS 0 +#define SPDIF_CFGR_CSE_MASK BIT(6) +#define SPDIF_CFGR_CSE_EN BIT(6) +#define SPDIF_CFGR_CSE_DIS 0 + #define SPDIF_CFGR_ADJ_MASK BIT(3) #define SPDIF_CFGR_ADJ_LEFT_J BIT(3) #define SPDIF_CFGR_ADJ_RIGHT_J 0 @@ -64,5 +68,9 @@ #define SPDIF_INTSR (0x0010) #define SPDIF_XFER (0x0018) #define SPDIF_SMPDR (0x0020) +#define SPDIF_VLDFRn(x) (0x0060 + (x) * 4) +#define SPDIF_USRDRn(x) (0x0090 + (x) * 4) +#define SPDIF_CHNSRn(x) (0x00c0 + (x) * 4) +#define SPDIF_VERSION (0x01c0) #endif /* _ROCKCHIP_SPDIF_H */ From d94ea902462ac39846d878aa67b78408727e0674 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 3 Feb 2026 17:46:32 +0100 Subject: [PATCH 326/341] ASoC: rockchip: spdif: Convert to FIELD_PREP Convert the driver to use FIELD_PREP to increase readability. This also fixes an issue that the SDPIF_CFGR_VDW_MASK was wrong, which didn't have any effects as the only user in the driver updates the other bits at the same time. Signed-off-by: Sebastian Reichel Link: https://patch.msgid.link/20260203-rockchip-spdif-cleanup-and-bsp-sync-v2-10-4412016cf577@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 13 +++---- sound/soc/rockchip/rockchip_spdif.h | 53 ++++++++++++++--------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index e64c24897f29..581624f2682e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -5,10 +5,11 @@ * * Copyright (c) 2014 Rockchip Electronics Co. Ltd. * Author: Jianqun - * Copyright (c) 2015 Collabora Ltd. + * Copyright (c) 2015-2026 Collabora Ltd. * Author: Sjoerd Simons */ +#include #include #include #include @@ -159,7 +160,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLK_DIV_MASK | - SPDIF_CFGR_HALFWORD_ENABLE | + SPDIF_CFGR_HALFWORD_MASK | SDPIF_CFGR_VDW_MASK | SPDIF_CFGR_ADJ_MASK, val); @@ -177,7 +178,7 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, - SPDIF_DMACR_TDE_ENABLE | + SPDIF_DMACR_TDE_MASK | SPDIF_DMACR_TDL_MASK, SPDIF_DMACR_TDE_ENABLE | SPDIF_DMACR_TDL(16)); @@ -186,21 +187,21 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream, return ret; ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, - SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_MASK, SPDIF_XFER_TXS_START); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, - SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_MASK, SPDIF_DMACR_TDE_DISABLE); if (ret != 0) return ret; ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, - SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_MASK, SPDIF_XFER_TXS_STOP); break; default: diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h index b837b1f8d57f..ec33295e2512 100644 --- a/sound/soc/rockchip/rockchip_spdif.h +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -2,7 +2,7 @@ /* * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver * - * Copyright (c) 2015 Collabora Ltd. + * Copyright (c) 2015-2026 Collabora Ltd. * Author: Sjoerd Simons */ @@ -13,53 +13,50 @@ * CFGR * transfer configuration register */ -#define SPDIF_CFGR_CLK_DIV_SHIFT (16) -#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) -#define SPDIF_CFGR_CLK_DIV(x) ((x-1) << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLK_DIV_MASK GENMASK(23, 16) +#define SPDIF_CFGR_CLK_DIV(x) FIELD_PREP(SPDIF_CFGR_CLK_DIV_MASK, x-1) #define SPDIF_CFGR_CLR_MASK BIT(7) -#define SPDIF_CFGR_CLR_EN BIT(7) -#define SPDIF_CFGR_CLR_DIS 0 +#define SPDIF_CFGR_CLR_EN FIELD_PREP(SPDIF_CFGR_CLR_MASK, 1) +#define SPDIF_CFGR_CLR_DIS FIELD_PREP(SPDIF_CFGR_CLR_MASK, 0) #define SPDIF_CFGR_CSE_MASK BIT(6) -#define SPDIF_CFGR_CSE_EN BIT(6) -#define SPDIF_CFGR_CSE_DIS 0 +#define SPDIF_CFGR_CSE_EN FIELD_PREP(SPDIF_CFGR_CSE_MASK, 1) +#define SPDIF_CFGR_CSE_DIS FIELD_PREP(SPDIF_CFGR_CSE_MASK, 0) #define SPDIF_CFGR_ADJ_MASK BIT(3) -#define SPDIF_CFGR_ADJ_LEFT_J BIT(3) -#define SPDIF_CFGR_ADJ_RIGHT_J 0 +#define SPDIF_CFGR_ADJ_LEFT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 1) +#define SPDIF_CFGR_ADJ_RIGHT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 0) -#define SPDIF_CFGR_HALFWORD_SHIFT 2 -#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) -#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT) +#define SPDIF_CFGR_HALFWORD_MASK BIT(2) +#define SPDIF_CFGR_HALFWORD_DISABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 0) +#define SPDIF_CFGR_HALFWORD_ENABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 1) -#define SPDIF_CFGR_VDW_SHIFT 0 -#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT) -#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT) +#define SDPIF_CFGR_VDW_MASK GENMASK(1, 0) +#define SPDIF_CFGR_VDW(x) FIELD_PREP(SDPIF_CFGR_VDW_MASK, x) -#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0) -#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1) -#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2) +#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0) +#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1) +#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2) /* * DMACR * DMA control register */ -#define SPDIF_DMACR_TDE_SHIFT 5 -#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT) -#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT) +#define SPDIF_DMACR_TDE_MASK BIT(5) +#define SPDIF_DMACR_TDE_DISABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 0) +#define SPDIF_DMACR_TDE_ENABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 1) -#define SPDIF_DMACR_TDL_SHIFT 0 -#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT) -#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT) +#define SPDIF_DMACR_TDL_MASK GENMASK(4, 0) +#define SPDIF_DMACR_TDL(x) FIELD_PREP(SPDIF_DMACR_TDL_MASK, x) /* * XFER * Transfer control register */ -#define SPDIF_XFER_TXS_SHIFT 0 -#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT) -#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT) +#define SPDIF_XFER_TXS_MASK BIT(0) +#define SPDIF_XFER_TXS_STOP FIELD_PREP(SPDIF_XFER_TXS_MASK, 0) +#define SPDIF_XFER_TXS_START FIELD_PREP(SPDIF_XFER_TXS_MASK, 1) #define SPDIF_CFGR (0x0000) #define SPDIF_SDBLR (0x0004) From ee1afacc356c84bba4b89e0655ffdcfa84d4f714 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 6 Feb 2026 16:41:47 +0300 Subject: [PATCH 327/341] ALSA: oss: delete self assignment No need to assign "uctl" to itself. Delete it. Fixes: 55f98ece9939 ("ALSA: oss: Relax __free() variable declarations") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/aYXvm2YoV2yRimhk@stanley.mountain Signed-off-by: Takashi Iwai --- 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 69422ab2d808..8d2d46d03301 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -792,7 +792,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned struct snd_ctl_elem_info *uinfo __free(kfree) = kzalloc(sizeof(*uinfo), GFP_KERNEL); struct snd_ctl_elem_value *uctl __free(kfree) = - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) return -ENOMEM; guard(rwsem_read)(&card->controls_rwsem); From 37bb773b4a5a5107b92beda3447a7c6c0cfc1237 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 6 Feb 2026 09:48:02 +0800 Subject: [PATCH 328/341] ASoC: dt-bindings: fsl,imx-asrc: Add support for i.MX952 platform Add new compatible string 'fsl,imx952-asrc' for i.MX952 platform, below are the differences that make this ASRC not fallback compatible with other platforms. 1) There is a power domain on i.MX952 for the wakeupmix system where ASRC is in. But it is enabled by default, ASRC device don't need to enable it, so it is optional for i.MX952. 2) The clock sources of ASRC are different on i.MX952. 3) There is a limitation on i.MX952 that DMA request is not cleared at the end of conversion with dma slave mode. Which causes sample is dropped from the input fifo on the second time if DMA is triggered before the client device and DMA may copy wrong data from output fifo as the output fifo is not ready in the beginning. So there is specially handling in the driver. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta Link: https://patch.msgid.link/20260206014805.3897764-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml index c9152bac7421..608defc93c1e 100644 --- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml @@ -25,6 +25,7 @@ properties: - fsl,imx53-asrc - fsl,imx8qm-asrc - fsl,imx8qxp-asrc + - fsl,imx952-asrc - items: - enum: - fsl,imx6sx-asrc From 83447a38ba9abac52bc110566d3e117753899f69 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 6 Feb 2026 09:48:03 +0800 Subject: [PATCH 329/341] ASoC: fsl_asrc_m2m: Add option to start ASRC before DMA device for M2M There is a limitation on i.MX952 that dma request is not cleared at the end of conversion with dma slave mode. Which causes sample is dropped from the input fifo on the second time if dma is triggered before the client device and EDMA may copy wrong data from output fifo as the output fifo is not ready in the beginning. The solution is to trigger asrc before dma on i.MX952, and add delay to wait output data is generated then start the EDMA for output, otherwise the m2m function has noise issues. So add an option to start ASRC first for M2M before ASRC is enabled on i.MX952. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Link: https://patch.msgid.link/20260206014805.3897764-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 22 ++++++++++++++++++++++ sound/soc/fsl/fsl_asrc.h | 4 ++++ sound/soc/fsl/fsl_asrc_common.h | 4 ++++ sound/soc/fsl/fsl_asrc_m2m.c | 8 +++++++- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 92fb16f7be45..2fe25667c888 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1078,6 +1078,26 @@ static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair) return val >> ASRFSTi_OUTPUT_FIFO_SHIFT; } +static bool fsl_asrc_m2m_output_ready(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 val; + int ret; + + /* Check output fifo status if it exceeds the watermark. */ + ret = regmap_read_poll_timeout(asrc->regmap, REG_ASRFST(index), val, + (ASRFSTi_OUTPUT_FIFO_FILL(val) >= ASRC_M2M_OUTPUTFIFO_WML), + 1, 1000); + + if (ret) { + pair_warn("output is not ready\n"); + return false; + } + + return true; +} + static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair) { struct fsl_asrc_pair_priv *pair_priv = pair->private; @@ -1275,6 +1295,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc_priv->soc = of_device_get_match_data(&pdev->dev); asrc->use_edma = asrc_priv->soc->use_edma; + asrc->start_before_dma = asrc_priv->soc->start_before_dma; asrc->get_dma_channel = fsl_asrc_get_dma_channel; asrc->request_pair = fsl_asrc_request_pair; asrc->release_pair = fsl_asrc_release_pair; @@ -1289,6 +1310,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst; asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume; asrc->m2m_get_cap = fsl_asrc_m2m_get_cap; + asrc->m2m_output_ready = fsl_asrc_m2m_output_ready; if (of_device_is_compatible(np, "fsl,imx35-asrc")) { asrc_priv->clk_map[IN] = input_clk_map_imx35; diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 1c492eb237f5..60b6865ca952 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -257,6 +257,8 @@ #define ASRFSTi_OUTPUT_FIFO_WIDTH 7 #define ASRFSTi_OUTPUT_FIFO_SHIFT 12 #define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT) +#define ASRFSTi_OUTPUT_FIFO_FILL(v) \ + (((v) & ASRFSTi_OUTPUT_FIFO_MASK) >> ASRFSTi_OUTPUT_FIFO_SHIFT) #define ASRFSTi_IAEi_SHIFT 11 #define ASRFSTi_IAEi_MASK (1 << ASRFSTi_IAEi_SHIFT) #define ASRFSTi_IAEi (1 << ASRFSTi_IAEi_SHIFT) @@ -432,10 +434,12 @@ struct dma_block { * * @use_edma: using edma as dma device or not * @channel_bits: width of ASRCNCR register for each pair + * @start_before_dma: start asrc before dma */ struct fsl_asrc_soc_data { bool use_edma; unsigned int channel_bits; + bool start_before_dma; }; /** diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h index 0cd595b0f629..c8a1a2b5915d 100644 --- a/sound/soc/fsl/fsl_asrc_common.h +++ b/sound/soc/fsl/fsl_asrc_common.h @@ -107,6 +107,7 @@ struct fsl_asrc_pair { * @asrc_rate: default sample rate for ASoC Back-Ends * @asrc_format: default sample format for ASoC Back-Ends * @use_edma: edma is used + * @start_before_dma: start asrc before dma * @get_dma_channel: function pointer * @request_pair: function pointer * @release_pair: function pointer @@ -116,6 +117,7 @@ struct fsl_asrc_pair { * @m2m_start: function pointer * @m2m_unprepare: function pointer * @m2m_stop: function pointer + * @m2m_output_ready: function pointer, check output fifo ready or not * @m2m_calc_out_len: function pointer * @m2m_get_maxburst: function pointer * @m2m_pair_suspend: function pointer @@ -143,6 +145,7 @@ struct fsl_asrc { int asrc_rate; snd_pcm_format_t asrc_format; bool use_edma; + bool start_before_dma; struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir); int (*request_pair)(int channels, struct fsl_asrc_pair *pair); @@ -154,6 +157,7 @@ struct fsl_asrc { int (*m2m_start)(struct fsl_asrc_pair *pair); int (*m2m_unprepare)(struct fsl_asrc_pair *pair); int (*m2m_stop)(struct fsl_asrc_pair *pair); + bool (*m2m_output_ready)(struct fsl_asrc_pair *pair); int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length); int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair); diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c index f46881f71e43..77999526dd9e 100644 --- a/sound/soc/fsl/fsl_asrc_m2m.c +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -253,15 +253,21 @@ static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task reinit_completion(&pair->complete[IN]); reinit_completion(&pair->complete[OUT]); + if (asrc->start_before_dma) + asrc->m2m_start(pair); + /* Submit DMA request */ dmaengine_submit(pair->desc[IN]); dma_async_issue_pending(pair->desc[IN]->chan); if (out_dma_len > 0) { + if (asrc->start_before_dma && asrc->m2m_output_ready) + asrc->m2m_output_ready(pair); dmaengine_submit(pair->desc[OUT]); dma_async_issue_pending(pair->desc[OUT]->chan); } - asrc->m2m_start(pair); + if (!asrc->start_before_dma) + asrc->m2m_start(pair); if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) { dev_err(dev, "out DMA task timeout\n"); From 6a8c6f5587337eceb387812b6f47bc16c125b883 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 6 Feb 2026 09:48:04 +0800 Subject: [PATCH 330/341] ASoC: fsl_asrc: Add support for i.MX952 platform Add a compatible string, clock mapping table and enable the option 'start_before_dma' to support ASRC on the i.MX952 platform. The clock mapping table is to map the clock sources on i.MX952 to the clock ids in the driver, the clock ids are for all the clock sources on all supported platforms. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Link: https://patch.msgid.link/20260206014805.3897764-4-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 16 ++++++++++++++++ sound/soc/fsl/fsl_asrc.h | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 2fe25667c888..5fda9b647c70 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -106,6 +106,12 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { }, }; +static unsigned char clk_map_imx952[ASRC_CLK_MAP_LEN] = { + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x4, 0x5, 0x6, 0x8, 0xf, 0xf, + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0x9, 0xa, 0xb, 0xc, 0xd, 0xf, 0xf, 0xf, 0xf, +}; + /* * According to RM, the divider range is 1 ~ 8, * prescaler is power of 2 from 1 ~ 128. @@ -1337,6 +1343,9 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx]; asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx]; } + } else if (of_device_is_compatible(np, "fsl,imx952-asrc")) { + asrc_priv->clk_map[IN] = clk_map_imx952; + asrc_priv->clk_map[OUT] = clk_map_imx952; } asrc->channel_avail = 10; @@ -1575,11 +1584,18 @@ static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = { .channel_bits = 4, }; +static const struct fsl_asrc_soc_data fsl_asrc_imx952_data = { + .use_edma = true, + .channel_bits = 4, + .start_before_dma = true, +}; + static const struct of_device_id fsl_asrc_ids[] = { { .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data }, { .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data }, { .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data }, { .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data }, + { .compatible = "fsl,imx952-asrc", .data = &fsl_asrc_imx952_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_asrc_ids); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 60b6865ca952..7a81366a0ee4 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -326,6 +326,13 @@ enum asrc_inclk { INCLK_SAI6_TX_BCLK = 0x22, INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24, INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25, + + INCLK_SAI2_TX_BCLK = 0x26, + INCLK_SAI3_TX_BCLK = 0x27, + INCLK_SAI4_RX_BCLK = 0x28, + INCLK_SAI4_TX_BCLK = 0x29, + INCLK_SAI5_RX_BCLK = 0x2a, + INCLK_SAI5_TX_BCLK = 0x2b, }; enum asrc_outclk { @@ -366,6 +373,13 @@ enum asrc_outclk { OUTCLK_SAI6_TX_BCLK = 0x22, OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24, OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25, + + OUTCLK_SAI2_TX_BCLK = 0x26, + OUTCLK_SAI3_TX_BCLK = 0x27, + OUTCLK_SAI4_RX_BCLK = 0x28, + OUTCLK_SAI4_TX_BCLK = 0x29, + OUTCLK_SAI5_RX_BCLK = 0x2a, + OUTCLK_SAI5_TX_BCLK = 0x2b, }; #define ASRC_CLK_MAX_NUM 16 From b010c782341b79edbeb80706360b772db908daa6 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 6 Feb 2026 09:48:05 +0800 Subject: [PATCH 331/341] ASoC: fsl_asrc_dma: allocate memory from dma device The dma-coherent property is used to indicate a device is capable of coherent DMA operations. On i.MX952, one of EDMA devices support such feature, in order to support the EDMA device, the memory needs to be allocated from the DMA device. Make this driver to support both non dma-coherent and dma-coherent dma engine. Remove dma coerce_mask_and coherent() because DMA provider already set it according to its capability. Signed-off-by: Shengjiu Wang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260206014805.3897764-5-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc_dma.c | 48 ++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 7dacc06b2f02..348b0aabfa68 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -449,18 +449,52 @@ fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component, static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct snd_card *card = rtd->card->snd_card; + struct device *dev = component->dev; + struct fsl_asrc *asrc = dev_get_drvdata(dev); + struct fsl_asrc_pair *pair; struct snd_pcm *pcm = rtd->pcm; + struct dma_chan *chan; int ret; - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(card->dev, "failed to set DMA mask\n"); - return ret; + pair = kzalloc(size_add(sizeof(*pair), asrc->pair_priv_size), GFP_KERNEL); + if (!pair) + return -ENOMEM; + + pair->asrc = asrc; + pair->private = (void *)pair + sizeof(struct fsl_asrc_pair); + + /* Request a pair, which will be released later. + * Request pair function needs channel num as input, for this + * pair, we just request "1" channel temporarily. + */ + ret = asrc->request_pair(1, pair); + if (ret < 0) { + dev_err(dev, "failed to request asrc pair\n"); + goto req_pair_err; } - return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, - card->dev, FSL_ASRC_DMABUF_SIZE); + /* Request a dma channel, which will be released later. */ + chan = asrc->get_dma_channel(pair, IN); + if (!chan) { + dev_err(dev, "failed to get dma channel\n"); + ret = -EINVAL; + goto dma_chan_err; + } + + ret = snd_pcm_set_fixed_buffer_all(pcm, + SNDRV_DMA_TYPE_DEV, + chan->device->dev, + FSL_ASRC_DMABUF_SIZE); + + dma_release_channel(chan); + +dma_chan_err: + asrc->release_pair(pair); + +req_pair_err: + kfree(pair); + + return ret; } struct snd_soc_component_driver fsl_asrc_component = { From 1585cf83e98db32463e5d54161b06a5f01fe9976 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 7 Feb 2026 14:13:17 +0100 Subject: [PATCH 332/341] ALSA: hda/conexant: Add quirk for HP ZBook Studio G4 It was reported that we need the same quirk for HP ZBook Studio G4 (SSID 103c:826b) as other HP models to make the mute-LED working. Cc: Link: https://lore.kernel.org/64d78753-b9ff-4c64-8920-64d8d31cd20c@gmail.com Link: https://bugzilla.kernel.org/show_bug.cgi?id=221002 Link: https://patch.msgid.link/20260207131324.2428030-1-tiwai@suse.de 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 2384e64eada3..5623d8c0a0f7 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1081,6 +1081,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x826b, "HP ZBook Studio G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), From ac1ff574bbc09a6c90f4fe8f9e6b8d66c983064c Mon Sep 17 00:00:00 2001 From: Illia Barbashyn <04baril@gmail.com> Date: Sat, 7 Feb 2026 23:19:37 +0100 Subject: [PATCH 333/341] ALSA: hda/realtek - Enable mute LEDs on HP ENVY x360 15-es0xxx The mute and mic-mute LEDs on HP ENVY x360 Convertible 15-es0xxx (PCI SSID 103c:88b3) do not work with the current driver. This model requires a combination of COEFBIT and GPIO fixups to correctly control the LEDs. Introduce a new fixup function alc245_fixup_hp_envy_x360_mute_led and add a quirk to apply it. Signed-off-by: Illia Barbashyn <04baril@gmail.com> Link: https://patch.msgid.link/20260207221955.24132-1-04baril@gmail.com 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 249586ea78ca..e9f64ed0b84b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1660,6 +1660,13 @@ static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec, alc285_fixup_hp_gpio_micmute_led(codec, fix, action); } +static void alc245_fixup_hp_envy_x360_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc245_fixup_hp_mute_led_v1_coefbit(codec, fix, action); + alc245_fixup_hp_gpio_led(codec, fix, action); +} + static void alc236_fixup_hp_mute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3908,6 +3915,7 @@ enum { ALC285_FIXUP_HP_GPIO_LED, ALC285_FIXUP_HP_MUTE_LED, ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED, + ALC245_FIXUP_HP_ENVY_X360_MUTE_LED, ALC285_FIXUP_HP_BEEP_MICMUTE_LED, ALC236_FIXUP_HP_MUTE_LED_COEFBIT2, ALC236_FIXUP_HP_GPIO_LED, @@ -5546,6 +5554,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_spectre_x360_mute_led, }, + [ALC245_FIXUP_HP_ENVY_X360_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_envy_x360_mute_led, + }, [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_beep, @@ -6814,6 +6826,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x103c, 0x88b3, "HP ENVY x360 Convertible 15-es0xxx", ALC245_FIXUP_HP_ENVY_X360_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), From f5183ee97bd67b3dd3cc6fb2fc97f2a6d3e36458 Mon Sep 17 00:00:00 2001 From: Pedro Amarante Date: Sat, 7 Feb 2026 21:24:39 -0800 Subject: [PATCH 334/341] ALSA: hda/generic: fix typos in comments Fix 'ony' -> 'only' and 'evalute' -> 'evaluate' in code comments. Signed-off-by: Pedro Amarante Link: https://patch.msgid.link/20260208052439.11498-1-ppedrolia@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 443500a3518f..b75a5e9470df 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -1517,7 +1517,7 @@ static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) /* * multi-io helper * - * When hardwired is set, try to fill ony hardwired pins, and returns + * When hardwired is set, try to fill only hardwired pins, and returns * zero if any pins are filled, non-zero if nothing found. * When hardwired is off, try to fill possible input pins, and returns * the badness value. @@ -4889,7 +4889,7 @@ static int check_auto_mic_availability(struct hda_codec *codec) * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets * into power down * @codec: the HDA codec - * @nid: NID to evalute + * @nid: NID to evaluate * @power_state: target power state */ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, From 3a92733e052753d87fdd56bd6f621f969be28447 Mon Sep 17 00:00:00 2001 From: Harin Lee Date: Sun, 8 Feb 2026 22:30:01 +0900 Subject: [PATCH 335/341] ALSA: ctxfi: Add quirk for SE-300PCIE variant (160b:0102) Add quirk for the Onkyo SE-300PCIE variant with PCI subsystem ID (160b:0102). This variant (OK0011) was found in the official Windows driver packages. Also, reorder entries and fix the indentation to maintain consistency. Signed-off-by: Harin Lee Link: https://patch.msgid.link/20260208133001.680550-1-me@harin.net Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 227d8c8490e1..a25a599fc5be 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -52,18 +52,19 @@ static const struct snd_pci_quirk subsys_20k1_list[] = { static const struct snd_pci_quirk subsys_20k2_list[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, "SB0760", CTSB0760), - SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270, - "SB1270", CTSB1270), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270, + "SB1270", CTSB1270), + SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010), + SND_PCI_QUIRK(0x160b, 0x0102, "OK0010", CTOK0010), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", CTHENDRIX), - SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010), { } /* terminator */ }; @@ -78,8 +79,8 @@ static const char *ct_subsys_name[NUM_CTCARDS] = { [CTSB0760] = "SB076x", [CTHENDRIX] = "Hendrix", [CTSB0880] = "SB0880", - [CTSB1270] = "SB1270", - [CTOK0010] = "OK0010", + [CTSB1270] = "SB1270", + [CTOK0010] = "OK0010", [CT20K2_UNKNOWN] = "Unknown", }; From 77d31948a88368f1e8516cb74614ab2e0340e840 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 7 Feb 2026 11:53:51 +0530 Subject: [PATCH 336/341] ASoC: amd: maintainer information Update MAINTAINERS file for AMD ASoC drivers. Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown Link: https://patch.msgid.link/20260207062433.1465232-1-Vijendar.Mukunda@amd.com --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4a48a11072e4..f27485e54645 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1030,6 +1030,13 @@ L: dmaengine@vger.kernel.org S: Supported F: drivers/dma/amd/ae4dma/ +AMD ASoC DRIVERS +M: Vijendar Mukunda +R: Venkata Prasad Potturu +L: linux-sound@vger.kernel.org +S: Supported +F: sound/soc/amd/ + AMD AXI W1 DRIVER M: Kris Chaplin R: Thomas Delev From ac656d7d7c70f7c352c7652bc2bb0c1c8c2dde08 Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Mon, 9 Feb 2026 08:38:29 +0000 Subject: [PATCH 337/341] ALSA: usb-audio: Add iface reset and delay quirk for AB13X USB Audio Setting up the interface when suspended/resumeing fail on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: New USB device found, idVendor=001f, idProduct=0b21 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: AB13X USB Audio usb 1-1: Manufacturer: Generic usb 1-1: SerialNumber: 20210926172016 Signed-off-by: Lianqin Hu Link: https://patch.msgid.link/TYUPR06MB6217522D0DB6E2C9DF46B56ED265A@TYUPR06MB6217.apcprd06.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4f9d19bf1cca..e3f737f18386 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2146,6 +2146,8 @@ struct usb_audio_quirk_flags_table { static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { /* Device matches */ + DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ From 3a7dbc729e42b95f1a82806a11128c1926ab26d8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 9 Feb 2026 08:08:38 +0100 Subject: [PATCH 338/341] ASoC: SOF: Intel: select CONFIG_SND_HDA_EXT_CORE from SND_SOC_SOF_HDA_COMMON The _hda_dsp_stream_put() function now depends on the snd_hdac_ext_stream_release() interface from SND_HDA_EXT_CORE: x86_64-linux-ld: vmlinux.o: in function `_hda_dsp_stream_put': hda-stream.c:(.text+0xfac605): undefined reference to `snd_hdac_ext_stream_release' Select this symbol the same way the other users do. Fixes: 89e1d632bb29 ("ASoC: SOF: Intel: add hda_dsp_stream_pair_get/put helpers") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260209070901.857700-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/sof/intel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 54cd3807f8c6..e31f4c4061d8 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -319,6 +319,7 @@ config SND_SOC_SOF_NOVALAKE config SND_SOC_SOF_HDA_COMMON tristate + select SND_HDA_EXT_CORE config SND_SOC_SOF_HDA_GENERIC tristate From f8f774913b4b599169381073f6674e20976e5529 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 Feb 2026 14:02:39 +0200 Subject: [PATCH 339/341] ASoC: SOF: ipc4-control: Set correct error code in refresh_bytes_control Return -EINVAL in case the scontrol contains more data than the amount of space available for it to store in sof_ipc4_refresh_bytes_control(). Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-sound/aYXvFr-LVHVJSvS7@stanley.mountain/ Fixes: 2a28b5240f2b ("ASoC: SOF: ipc4-control: Add support for generic bytes control") Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260209120239.6066-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 0500b690f9a3..596c3d77a34e 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -362,6 +362,7 @@ sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock) "%s: no space for data in %s (%zu, %zu)\n", __func__, scontrol->name, msg->data_size, scontrol->max_size - sizeof(*data)); + ret = -EINVAL; goto out; } From 084d5d44418148662365eced3e126ad1a81ee3e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Feb 2026 13:12:11 +0100 Subject: [PATCH 340/341] ALSA: mixer: oss: Add card disconnect checkpoints ALSA OSS mixer layer calls the kcontrol ops rather individually, and pending calls might be not always caught at disconnecting the device. For avoiding the potential UAF scenarios, add sanity checks of the card disconnection at each entry point of OSS mixer accesses. The rwsem is taken just before that check, hence the rest context should be covered by that properly. Link: https://patch.msgid.link/20260209121212.171430-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/oss/mixer_oss.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 8d2d46d03301..f4ad0bfb4dac 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -523,6 +523,8 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return; kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; @@ -557,6 +559,8 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return; kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; @@ -618,6 +622,8 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return; kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; @@ -656,6 +662,8 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return; kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; @@ -796,6 +804,8 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (uinfo == NULL || uctl == NULL) return -ENOMEM; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return -ENODEV; kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); if (!kctl) return -ENOENT; @@ -839,6 +849,8 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (uinfo == NULL || uctl == NULL) return -ENOMEM; guard(rwsem_read)(&card->controls_rwsem); + if (card->shutdown) + return -ENODEV; kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); if (!kctl) return -ENOENT; @@ -885,6 +897,8 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl if (!info) return -ENOMEM; scoped_guard(rwsem_read, &card->controls_rwsem) { + if (card->shutdown) + return -ENODEV; kcontrol = snd_mixer_oss_test_id(mixer, name, index); if (kcontrol == NULL) return 0; @@ -1006,6 +1020,8 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, if (snd_mixer_oss_build_test_all(mixer, ptr, &slot)) return 0; guard(rwsem_read)(&mixer->card->controls_rwsem); + if (mixer->card->shutdown) + return -ENODEV; kctl = NULL; if (!ptr->index) kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); From fe7cd89f0e29f0852316857b4861309f9b891370 Mon Sep 17 00:00:00 2001 From: Qihang Guo Date: Mon, 9 Feb 2026 18:04:18 +0800 Subject: [PATCH 341/341] ALSA: usb-audio: Add DSD support for iBasso DC04U Vendor ID 0x0661 is assigned to Hamamatsu Photonics K.K., but is used by iBasso for iBasso DC04U (0x0661:0x0883), which supports native DSD playback. This patch adds QUIRK_FLAG_DSD_RAW for iBasso DC04U, enabling native DSD playback (DSD_U32_BE). The change has been verified on Arch Linux using mpd and pw-cat. Signed-off-by: Qihang Guo Link: https://patch.msgid.link/TYYPR01MB14098529E0BD900921BE6F42CF465A@TYYPR01MB14098.jpnprd01.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e3f737f18386..86c329632e39 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2237,6 +2237,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x0644, 0x806c, /* Esoteric XD */ QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY | QUIRK_FLAG_IFACE_DELAY | QUIRK_FLAG_FORCE_IFACE_RESET), + DEVICE_FLG(0x0661, 0x0883, /* iBasso DC04 Ultra */ + QUIRK_FLAG_DSD_RAW), DEVICE_FLG(0x06f8, 0xb000, /* Hercules DJ Console (Windows Edition) */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x06f8, 0xd002, /* Hercules DJ Console (Macintosh Edition) */