diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-17 09:57:09 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-17 09:57:09 -0700 |
| commit | 4982d3552a3bf94de503acf93433277d08421de6 (patch) | |
| tree | 94956c7fb806cee3dd376119b19f0df2fa5be69f | |
| parent | f143ea21cf834b4d57d70b47b99e582461f2dfaf (diff) | |
| parent | 546b928da0427b0d6c663cbb992bd7bfa9ac7971 (diff) | |
| download | linux-stable-4982d3552a3bf94de503acf93433277d08421de6.tar.gz linux-stable-4982d3552a3bf94de503acf93433277d08421de6.zip | |
Pull sound fixes from Takashi Iwai:
"A collection of small fixes. Most of them are device-specific fixes
while there are a few core fixes. The continued flux, but not too
scaring yet. Some highlights below.
ALSA Core:
- Fix potential UAF after asynchronous card release
- Fix a race condition in PCM timer initialization order
USB-Audio:
- Hardening fixes for issues reported by fuzzer for 6fire, bcd2000,
and implicit FB packets
- Fix double list addition in implicit FB handling
- Quirks for AVerMedia GC553Pro and Behringer FCA1616
HD-Audio:
- Quirks / fixes for HP OmniBook 7, OMEN 15, and Victus 15 laptops
ASoC:
- Support for DAI link codec channel mask to avoid mismatches
- Fix HDMI-codec channel status change report
- Fixes for various codecs and platforms: Realtek rt712/rt721
(calibration, reset fixes), Cirrus Logic (empty EFI variable
validation, capture channel fixup), AMD ACP SoundWire (bounds
checks, refactorings), ADAU1977 (OF match table support, SPI
cleanups), ES8336 (Huawei Matebook B3-420 quirk), UX500 (macro
fix)"
* tag 'sound-7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (33 commits)
ASoC: adau1977-i2c: add OF match table for I2C
ASoC: adau1977-spi: drop __maybe_unused and of_match_ptr()
ASoC: adau1977: make the Kconfig symbols user selectable
ASoC: amd: acp: fix card name length warning in SOF SoundWire machine driver
ASoC: amd: acp: fix ffs() operator precedence for SoundWire link ID
ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver
ASoC: amd: acp: bounds-check SoundWire link ID in machine drivers
ASoC: cs-amp-lib: Prevent NULL pointer if efi variable is zero length
ASoC: codecs: rt712-sdca-dmic: fix uninitialized stream_config->type
ASoC: hdmi-codec: Report a change when the channel status moves
ASoC: ux500: Parenthesize MSP_{RX,TX}_CLKPOL_BIT() arguments
ASoC: rt721: Reset codec to fix abnormal sound
ALSA: usb-audio: fix list_add double-add in push_back_to_ready_list
ALSA: hda: trace PCM open only after assigning a stream
ALSA: usb-audio: skip the broken mute control on AVerMedia GC553Pro
ALSA: hda/realtek: Enable mute LEDs on HP OmniBook 7 17-dc0xxx
ALSA: 6fire: fix OOB write from device-reported iso length
ALSA: usb-audio: Add capture quirk for Behringer FCA1616
ALSA: hda/realtek: Add mute LED quirk for HP OMEN 15-ax
ASoC: Intel: sof_es8336: Add a quirk for Huawei Matebook B3-420
...
32 files changed, 316 insertions, 119 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h index f46b2bc2a022..5afc34b147b5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -699,7 +699,8 @@ struct snd_soc_dai_link_component { struct snd_soc_dai_link_ch_map { unsigned int cpu; unsigned int codec; - unsigned int ch_mask; + unsigned int cpu_ch_mask; + unsigned int codec_ch_mask; }; struct snd_soc_dai_link { diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 9b28e9aef4f1..9fbb69b9052d 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct asoc_sdw_codec_info *info, bool playback); -int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai); int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix); /* MAXIM codec support */ diff --git a/sound/core/init.c b/sound/core/init.c index 9693e646b3bb..1bcb6a2e7550 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -310,7 +310,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent, kfree(card); /* manually free here, as no destructor called */ return err; } - card->dev = parent; + card->dev = get_device(parent); card->number = idx; WARN_ON(IS_MODULE(CONFIG_SND) && !module); card->module = module; @@ -603,6 +603,7 @@ static int snd_card_do_free(struct snd_card *card) dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ } + put_device(card->dev); if (card->release_completion) complete(card->release_completion); if (!managed) diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index ab0e5bd70f8f..18bedd66435d 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -111,12 +111,15 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream) snd_pcm_direction_name(substream->stream), tid.card, tid.device, tid.subdevice); timer->hw = snd_pcm_timer; + /* Set before registering: a concurrent reader can invoke our hw + * callbacks as soon as the timer is on the global list. + */ + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free; if (snd_device_register(timer->card, timer) < 0) { snd_device_free(timer->card, timer); return; } - timer->private_data = substream; - timer->private_free = snd_pcm_timer_free; substream->timer = timer; } diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 3abee617e86e..5127a111c59c 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4303,6 +4303,7 @@ enum { ALC287_FIXUP_LEGION_16ACHG6, ALC287_FIXUP_CS35L41_I2C_2, ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, + ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS, ALC287_FIXUP_CS35L41_I2C_4, ALC245_FIXUP_CS35L41_SPI_1, ALC245_FIXUP_CS35L41_SPI_2, @@ -6614,6 +6615,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_HP_MUTE_LED, }, + [ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + .chained = true, + .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS, + }, [ALC287_FIXUP_CS35L41_I2C_4] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_four, @@ -7358,6 +7365,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x8259, "HP OMEN 15-ax202nf", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), @@ -7568,6 +7576,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb1, "HP Victus 15-fa1xxx (MB 8BB1)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), @@ -7660,7 +7669,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), diff --git a/sound/hda/common/controller.c b/sound/hda/common/controller.c index afec5c5546ec..18dae022b324 100644 --- a/sound/hda/common/controller.c +++ b/sound/hda/common/controller.c @@ -586,11 +586,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); - trace_azx_pcm_open(chip, azx_dev); if (azx_dev == NULL) { err = -EBUSY; goto unlock; } + trace_azx_pcm_open(chip, azx_dev); runtime->private_data = azx_dev; runtime->hw = azx_pcm_hw; diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 6eac42bac855..1a05d4288a46 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -205,9 +205,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -EINVAL; } + if (!soc_end->link_mask) { + dev_err(dev, "invalid zero link_mask\n"); + return -EINVAL; + } + if ((ffs(soc_end->link_mask) - 1) >= amd_ctx->max_sdw_links) { + dev_err(dev, "link_id %d exceeds max_sdw_links %d\n", + ffs(soc_end->link_mask) - 1, amd_ctx->max_sdw_links); + return -EINVAL; + } + switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: - ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1), + ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -215,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, case ACP70_PCI_REV: case ACP71_PCI_REV: case ACP72_PCI_REV: - ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1), + ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a9cd1f335167..ec3e1f5f1052 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -121,9 +121,18 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -EINVAL; } + if (!sof_end->link_mask) { + dev_err(dev, "invalid zero link_mask\n"); + return -EINVAL; + } + if ((ffs(sof_end->link_mask) - 1) >= amd_ctx->max_sdw_links) { + dev_err(dev, "link_id %d exceeds max_sdw_links %d\n", + ffs(sof_end->link_mask) - 1, amd_ctx->max_sdw_links); + return -EINVAL; + } switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: - ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1), + ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -131,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, case ACP70_PCI_REV: case ACP71_PCI_REV: case ACP72_PCI_REV: - ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1), + ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -277,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int num_devs = 0; int num_ends = 0; int num_aux = 0; + int num_confs; int num_links; int be_id = 0; int ret; @@ -287,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) return ret; } + num_confs = num_ends; /* One per DAI link, worst case is a DAI link for every endpoint */ struct asoc_sdw_dailink *sof_dais __free(kfree) = kzalloc_objs(*sof_dais, num_ends); @@ -303,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) return ret; @@ -315,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); - codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL); + codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL); if (!codec_conf) return -ENOMEM; @@ -326,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) return -ENOMEM; card->codec_conf = codec_conf; - card->num_configs = num_devs; + card->num_configs = num_confs; card->dai_link = dai_links; card->num_links = num_links; card->aux_dev = sof_aux; @@ -379,7 +390,7 @@ static int mc_probe(struct platform_device *pdev) ctx->private = amd_ctx; card = &ctx->card; card->dev = &pdev->dev; - card->name = "amd-soundwire"; + card->name = "amd-sdw"; card->owner = THIS_MODULE; card->late_probe = asoc_sdw_card_late_probe; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f9a47e262a77..d88593c2bac8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -524,13 +524,13 @@ config SND_SOC_ADAU1977 tristate config SND_SOC_ADAU1977_SPI - tristate + tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI" depends on SPI_MASTER select SND_SOC_ADAU1977 select REGMAP_SPI config SND_SOC_ADAU1977_I2C - tristate + tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C" depends on I2C select SND_SOC_ADAU1977 select REGMAP_I2C diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index d1c6c4ddf506..5a11cafdff36 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); +static const struct of_device_id adau1977_i2c_of_match[] = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); + static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", + .of_match_table = adau1977_i2c_of_match, }, .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c index 878cde9d1014..c98da5ba9e9e 100644 --- a/sound/soc/codecs/adau1977-spi.c +++ b/sound/soc/codecs/adau1977-spi.c @@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); -static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = { - { .compatible = "adi,adau1977" }, - { .compatible = "adi,adau1978" }, - { .compatible = "adi,adau1979" }, - { }, +static const struct of_device_id adau1977_spi_of_match[] = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, }; MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); static struct spi_driver adau1977_spi_driver = { .driver = { .name = "adau1977", - .of_match_table = of_match_ptr(adau1977_spi_of_match), + .of_match_table = adau1977_spi_of_match, }, .probe = adau1977_spi_probe, .id_table = adau1977_spi_ids, diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 41a9a5b005c6..9bc19d2e1639 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -317,6 +317,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name, unsigned long size = 0; status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL); + if (status == EFI_SUCCESS) + return ERR_PTR(-ENOENT); if (status != EFI_BUFFER_TOO_SMALL) return ERR_PTR(cs_amp_convert_efi_status(status)); diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index bc2c22436ba6..7aa50c5bd3df 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -426,10 +426,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + if (!memcmp(hcp->iec_status, ucontrol->value.iec958.status, + sizeof(hcp->iec_status))) + return 0; + memcpy(hcp->iec_status, ucontrol->value.iec958.status, sizeof(hcp->iec_status)); - return 0; + return 1; } static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 8860d81134e7..a9f3aa4e143a 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -13,6 +13,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt712-sdca.h" #include "rt712-sdca-dmic.h" @@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; struct sdw_stream_runtime *sdw_stream; - int retval, num_channels; + int retval; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, if (!rt712->slave) return -EINVAL; - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = SDW_DATA_DIR_TX; - - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = 2; retval = sdw_stream_add_slave(rt712->slave, &stream_config, diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index c50e74e20a88..edba0367d9ce 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -18,12 +18,16 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0x201a ... 0x201f: case 0x2029 ... 0x202a: case 0x202d ... 0x2034: case 0x2230 ... 0x2232: case 0x2f01 ... 0x2f0a: case 0x2f35 ... 0x2f36: + case 0x2f3a: + case 0x2f3d: + case 0x2f41: case 0x2f50: case 0x2f54: case 0x2f58 ... 0x2f5d: @@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0x201b: case 0x201c: case 0x201d: @@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x2230: case 0x2f01: case 0x2f35: + case 0x2f3a: + case 0x2f3d: + case 0x2f41: case 0x320c: case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index eda87eb9ab66..38052cb19790 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -73,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, return rt712_sdca_index_write(rt712, nid, reg, tmp); } +static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); + usleep_range(1000, 1100); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010); + usleep_range(1000, 1100); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); +} + +static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800, + 0x0000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000, + 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000, + 0xc000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000, + 0xc000); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); + usleep_range(1000, 1100); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); +} + static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) { unsigned int val, loop_rc = 0, loop_dc = 0; struct device *dev; struct regmap *regmap = rt712->regmap; + unsigned int clk_base; int chk_cnt = 100; int ret = 0; + regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); + mutex_lock(&rt712->calibrate_mutex); dev = regmap_get_device(regmap); @@ -109,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) if (ret < 0) goto _cali_fail_; } - if (loop_dc == chk_cnt) - dev_err(dev, "%s, calibration time-out!\n", __func__); + + if (loop_dc == chk_cnt) { + if (clk_base == RT712_CLK_FREQ_24_576MHZ) { + rt712_sdca_clk_patch(rt712); + rt712_sdca_clk_patch2(rt712); + } + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, 0x7883); + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, 0xf893); + rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + for (loop_dc = 0; loop_dc < chk_cnt && + (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { + usleep_range(10000, 11000); + ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + if (ret < 0) + goto _cali_fail_; + } + + if (loop_dc == chk_cnt) + dev_err(dev, "%s, calibration time-out!\n", __func__); + else + dev_dbg(dev, "%s, calibration success!\n", __func__); + } if (loop_dc == chk_cnt || loop_rc == chk_cnt) ret = -ETIMEDOUT; @@ -1759,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) { - int ret = 0; unsigned int jack_func_status, mic_func_status, amp_func_status; struct device *dev = &rt712->slave->dev; + unsigned int clk_base; + int ret = 0; + + regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); + dev_dbg(dev, "%s clk_base=%x", __func__, clk_base); regmap_read(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); @@ -1773,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) __func__, jack_func_status, mic_func_status, amp_func_status); rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778); + + if (clk_base == RT712_CLK_FREQ_24_576MHZ) { + rt712_sdca_clk_patch(rt712); + rt712_sdca_clk_patch2(rt712); + } + /* DMIC */ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index 46740281a5c1..6229fe341bb5 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv { #define RT712_EAPD_HIGH 0x2 #define RT712_EAPD_LOW 0x0 +/* SDW clock root frequency */ +#define RT712_SDW_ROOT_CLK 0x004d +#define RT712_SDW_SCALE_CLK0 0x0062 +#define RT712_SDW_SCALE_CLK1 0x0072 + +/* PLL2 config */ +#define RT712_PLL2_CONF1 0x2f3a +#define RT712_PLL2_CONF2 0x2f3d +#define RT712_PLL2_CONF3 0x2f41 + /* RC Calibration register */ #define RT712_RC_CAL 0x3201 @@ -254,6 +264,14 @@ enum rt712_sdca_version { RT712_VB, }; +enum { + RT712_CLK_FREQ_19_2_MHZ = 1, + RT712_CLK_FREQ_24MHZ = 2, + RT712_CLK_FREQ_24_576MHZ = 3, + RT712_CLK_FREQ_22_5792MHZ = 4, +}; + + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave); diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index eae7d662efae..910583162d3e 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -70,6 +70,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x0310100: case 0x2000000 ... 0x2000003: case 0x2000013: + case 0x2000026: case 0x200002c: case 0x200003c: case 0x2000046: @@ -142,6 +143,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x200000d: case 0x2000019: case 0x2000020: + case 0x2000026: case 0x200002c: case 0x2000030: case 0x2000046: @@ -155,6 +157,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x5810039: case 0x5b10018: case 0x5b10019: + case 0x6100006: return true; default: return false; diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index a9479d0e4941..738644018fb9 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -1497,6 +1497,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap, &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai)); } +static void rt721_sdca_reset(struct rt721_sdca_priv *rt721) +{ + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG, + RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET, + RT721_HIDDEN_REG_SW_RESET); + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1); +} + int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); @@ -1530,9 +1539,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) } pm_runtime_get_noresume(&slave->dev); + + if (!rt721->first_hw_init) + rt721_sdca_reset(rt721); + rt721_sdca_dmic_preset(rt721); rt721_sdca_amp_preset(rt721); rt721_sdca_jack_preset(rt721); + + if (rt721->hs_jack && (!rt721->first_hw_init)) + rt721_sdca_jack_init(rt721); + if (rt721->first_hw_init) { regcache_cache_bypass(rt721->regmap, false); regcache_mark_dirty(rt721->regmap); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 90c24c4b318e..b8f3035af3ba 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, s++; } + adsp_dbg(dsp, "Try '%s'\n", fw->filename); ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev); if (ret < 0) { - adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); + if (ret != -ENOENT) + adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); + kfree(fw->filename); fw->filename = NULL; if (ret != -ENOENT) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 9b016136c639..f1e62c2e79fe 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | SOC_ES8336_HEADSET_MIC1) }, + { + .callback = sof_es8336_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), + DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"), + }, + .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | + SOC_ES8336_HEADSET_MIC1) + }, {} }; diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c index 325ab7230481..6e21ef8f87e2 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c @@ -14,7 +14,6 @@ #include <sound/soc-dai.h> #include <sound/soc_sdw_utils.h> -#define CS_AMP_CHANNELS_PER_AMP 4 #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix) @@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai } EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS"); -int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) -{ - const struct snd_soc_dai_link *dai_link = rtd->dai_link; - const struct snd_soc_dai_link_ch_map *ch_map; - const struct snd_soc_dai_link_component *codec_dlc; - struct snd_soc_dai *codec_dai; - u8 ch_slot[8] = {}; - unsigned int amps_per_bus, ch_per_amp, mask; - int i, ret; - - WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot)); - - /* - * CS35L56 has 4 TX channels. When the capture is aggregated the - * same bus slots will be allocated to all the amps on a bus. Only - * one amp on that bus can be transmitting in each slot so divide - * the available 4 slots between all the amps on a bus. - */ - amps_per_bus = dai_link->num_codecs / dai_link->num_cpus; - if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) { - dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n", - dai_link->num_codecs, dai_link->num_cpus); - return -EINVAL; - } - - ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus; - - for_each_rtd_ch_maps(rtd, i, ch_map) { - codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i); - codec_dai = snd_soc_find_dai(codec_dlc); - mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu]; - - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret); - return ret; - } - - ch_slot[ch_map->cpu] += ch_per_amp; - } - - return 0; -} -EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS"); - int asoc_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct asoc_sdw_codec_info *info, diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index a66dcc02fb59..d2eeef4931c6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -811,7 +811,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -840,7 +839,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -869,7 +867,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -898,7 +895,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -1565,7 +1561,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai_link_ch_map *ch_maps; int ch = params_channels(params); - unsigned int ch_mask; + unsigned int cpu_ch_mask, codec_ch_mask; int num_codecs; int step; int i; @@ -1575,8 +1571,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, /* Identical data will be sent to all codecs in playback */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ch_mask = GENMASK(ch - 1, 0); + cpu_ch_mask = GENMASK(ch - 1, 0); step = 0; + codec_ch_mask = 0; } else { num_codecs = rtd->dai_link->num_codecs; @@ -1586,17 +1583,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ch_mask = GENMASK(ch / num_codecs - 1, 0); - step = hweight_long(ch_mask); + cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0); + step = hweight_long(cpu_ch_mask); + codec_ch_mask = cpu_ch_mask; } /* * The captured data will be combined from each cpu DAI if the dai * link has more than one codec DAIs. Set codec channel mask and * ASoC will set the corresponding channel numbers for each cpu dai. + * + * sdw_stream_add_slave() assigns different payload offsets to each + * codec in a capture stream, so that the same channels on each + * codec map to different channels on the CPU. */ - for_each_link_ch_maps(rtd->dai_link, i, ch_maps) - ch_maps->ch_mask = ch_mask << (i * step); + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { + ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step); + ch_maps->codec_ch_mask = codec_ch_mask; + } return 0; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0e49290a8c90..3137c091bdb8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1206,7 +1206,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, goto out; for_each_rtd_codec_dais(rtd, i, codec_dai) { - unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); + unsigned int ch_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); + struct snd_soc_dai_link_ch_map *ch_maps; + int j; /* * Skip CODECs which don't support the current stream type, @@ -1228,9 +1230,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, /* copy params for each codec */ tmp_params = *params; - /* fixup params based on TDM slot masks */ - if (tdm_mask) - soc_pcm_codec_params_fixup(&tmp_params, tdm_mask); + /* fixup params based on TDM or ch_map masks */ + if (!ch_mask) { + for_each_rtd_ch_maps(rtd, j, ch_maps) + if (ch_maps->codec == i) + ch_mask |= ch_maps->codec_ch_mask; + } + + if (ch_mask) + soc_pcm_codec_params_fixup(&tmp_params, ch_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, &tmp_params); @@ -1264,7 +1272,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, */ for_each_rtd_ch_maps(rtd, j, ch_maps) if (ch_maps->cpu == i) - ch_mask |= ch_maps->ch_mask; + ch_mask |= ch_maps->cpu_ch_mask; /* fixup cpu channel number */ if (ch_mask) diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 2bf2699bdc49..c66ef455e138 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -147,8 +147,8 @@ enum msp_direction { #define RCKPOL_MASK BIT(0) #define TCKPOL_MASK BIT(0) #define SPICKM_MASK (BIT(1) | BIT(0)) -#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) -#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) +#define MSP_RX_CLKPOL_BIT(n) (((n) & RCKPOL_MASK) << RCKPOL_SHIFT) +#define MSP_TX_CLKPOL_BIT(n) (((n) & TCKPOL_MASK) << TCKPOL_SHIFT) #define P1ELEN_SHIFT 0 #define P1FLEN_SHIFT 3 diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 21789db6657d..0285d79ace0f 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -335,11 +335,19 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) /* setup out urb structure */ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + unsigned int frames = 0; + isoc_out = &out_urb->instance->iso_frame_desc[i]; isoc_in = &in_urb->instance->iso_frame_desc[i]; + if (isoc_in->actual_length > 4) + frames = (isoc_in->actual_length - 4) + / (rt->in_n_analog << 2); + frames = min_t(unsigned int, frames, + (rt->out_packet_size - 4) + / (rt->out_n_analog << 2)); + isoc_out->offset = total_length; - isoc_out->length = (isoc_in->actual_length - 4) / (rt->in_n_analog << 2) - * (rt->out_n_analog << 2) + 4; + isoc_out->length = frames * (rt->out_n_analog << 2) + 4; isoc_out->status = 0; total_length += isoc_out->length; } diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index c5c542d17ccc..2bd49bf82748 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -43,6 +43,7 @@ struct bcd2000 { struct usb_interface *intf; int card_index; + spinlock_t midi_lock; int midi_out_active; struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *midi_receive_substream; @@ -90,6 +91,8 @@ static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) { struct bcd2000 *bcd2k = substream->rmidi->private_data; + + guard(spinlock_irqsave)(&bcd2k->midi_lock); bcd2k->midi_receive_substream = up ? substream : NULL; } @@ -195,6 +198,8 @@ static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream, { struct bcd2000 *bcd2k = substream->rmidi->private_data; + guard(spinlock_irqsave)(&bcd2k->midi_lock); + if (up) { bcd2k->midi_out_substream = substream; /* check if there is data userspace wants to send */ @@ -219,6 +224,7 @@ static void bcd2000_output_complete(struct urb *urb) return; /* check if there is more data userspace wants to send */ + guard(spinlock_irqsave)(&bcd2k->midi_lock); bcd2000_midi_send(bcd2k); } @@ -234,6 +240,8 @@ static void bcd2000_input_complete(struct urb *urb) if (!bcd2k || urb->status == -ESHUTDOWN) return; + guard(spinlock_irqsave)(&bcd2k->midi_lock); + if (urb->actual_length > 0) bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer, urb->actual_length); @@ -348,16 +356,26 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) return 0; } +static void bcd2000_midi_free(struct bcd2000 *bcd2k, + struct urb **urb_p) +{ + struct urb *urb = *urb_p; + + if (!urb) + return; + + usb_poison_urb(urb); + scoped_guard(spinlock_irq, &bcd2k->midi_lock) + *urb_p = NULL; + + usb_free_urb(urb); +} + static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k, struct usb_interface *interface) { - usb_poison_urb(bcd2k->midi_out_urb); - usb_poison_urb(bcd2k->midi_in_urb); - - usb_free_urb(bcd2k->midi_out_urb); - usb_free_urb(bcd2k->midi_in_urb); - bcd2k->midi_out_urb = NULL; - bcd2k->midi_in_urb = NULL; + bcd2000_midi_free(bcd2k, &bcd2k->midi_out_urb); + bcd2000_midi_free(bcd2k, &bcd2k->midi_in_urb); if (bcd2k->intf) { usb_set_intfdata(bcd2k->intf, NULL); @@ -393,6 +411,7 @@ static int bcd2000_probe(struct usb_interface *interface, bcd2k->card = card; bcd2k->card_index = card_index; bcd2k->intf = interface; + spin_lock_init(&bcd2k->midi_lock); snd_card_set_dev(card, &interface->dev); diff --git a/sound/usb/card.h b/sound/usb/card.h index e34d92d576a2..8299ac241c60 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -116,6 +116,7 @@ struct snd_usb_endpoint { unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ + unsigned int max_urb_packs; /* packets allocated per data URB */ unsigned int max_urb_frames; /* max URB size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 0835943d7b0e..879d0451536e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep, struct snd_urb_ctx *ctx) { guard(spinlock_irqsave)(&ep->lock); - list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + /* ctx may still be linked: a stale completion racing a stop/restart. */ + if (list_empty(&ctx->ready_list)) + list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); } /* @@ -492,9 +494,10 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, /* copy over the length information */ if (implicit_fb) { - ctx->packets = packet->packets; + ctx->packets = min_t(int, packet->packets, + ep->max_urb_packs); memcpy(ctx->packet_size, packet->packet_size, - packet->packets * sizeof(packet->packet_size[0])); + ctx->packets * sizeof(packet->packet_size[0])); } /* call the data handler to fill in playback data */ @@ -1036,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) */ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) { + struct snd_urb_ctx *ctx, *n; unsigned int i; if (!force && atomic_read(&ep->running)) @@ -1045,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) return 0; scoped_guard(spinlock_irqsave, &ep->lock) { - INIT_LIST_HEAD(&ep->ready_playback_urbs); + /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */ + list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list) + list_del_init(&ctx->ready_list); ep->next_packet_head = 0; ep->next_packet_queued = 0; } @@ -1242,15 +1248,16 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } + if (fmt->fmt_type == UAC_FORMAT_TYPE_II) + urb_packs++; /* for transfer delimiter */ + ep->max_urb_packs = urb_packs; + /* allocate and initialize data urbs */ for (i = 0; i < ep->nurbs; i++) { struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; u->packets = urb_packs; - - if (fmt->fmt_type == UAC_FORMAT_TYPE_II) - u->packets++; /* for transfer delimiter */ u->buffer_size = maxsize * u->packets; u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); if (!u->urb) diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 77f06da93151..bd4d569a8190 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -76,6 +76,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { /* Implicit feedback quirk table for capture: only FIXED type */ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0004, 0x01, 1), /* Behringer FCA1616 */ {} /* terminator */ }; diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 69093c666282..41454cb403d0 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -532,6 +532,15 @@ static const struct usbmix_name_map audient_id24_map[] = { }; /* + * The GC553Pro returns no data for GET_CUR on its advertised mute control. + * SET_CUR succeeds but does not mute capture, so skip the control entirely. + */ +static const struct usbmix_name_map avermedia_gc553pro_map[] = { + { 3, NULL, UAC_FU_MUTE }, + {} +}; + +/* * Control map entries */ @@ -578,6 +587,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .selector_map = c400_selectors, }, { + .id = USB_ID(0x07ca, 0x1553), + .map = avermedia_gc553pro_map, + }, + { .id = USB_ID(0x08bb, 0x2702), .map = linex_map, }, diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index 647190f4d5af..6f35276416fe 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -354,8 +354,8 @@ static void virtsnd_remove(struct virtio_device *vdev) if (snd->card) snd_card_free(snd->card); - vdev->config->del_vqs(vdev); virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) { struct virtio_pcm_substream *vss = &snd->substreams[i]; @@ -383,8 +383,8 @@ static int virtsnd_freeze(struct virtio_device *vdev) virtsnd_disable_event_vq(snd); virtsnd_ctl_msg_cancel_all(snd); - vdev->config->del_vqs(vdev); virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); for (i = 0; i < snd->nsubstreams; ++i) cancel_work_sync(&snd->substreams[i].elapsed_period); |
