sound fixes for 7.3-rc4

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)
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmqrwIYOHHRpd2FpQHN1
 c2UuZGUACgkQLtJE4w1nLE8WeQ//eN0ufLXXqy5U6X7kri8eM4vjRDZa5v1z8oD/
 oHBdkgPmSAOJOddCVHuyPyx5BP4reBgKbukxUTBjuJvVRD4iOYfvXSW1LzVCVcly
 f60rJl1/Ck3RcXfVabNV9eeGCFtAwR00U/3oH7z0eDhjwisX2F4ucAMmgxyJUKTh
 pLdMPFsI/XW5CWeVbPVObGlOB8Bf/c79wy5NAnpixAkR1WaXUSLKoF5djO9qIQex
 eTIknPmMYLLSfzFfO0TWY1PPRPz5qJHDr6Acer0VMTHyZF0yg2bR+gRInbM1at2H
 c/lg8m899ZbobSCiHFEJdPH/W5x3iHqFi3hKVtKtQ5niot+gWTirQpjUCZ2HxNOp
 5fYEyieSJ0W/t2NfNW0SP+DUOvKaIxY9VUuGCW7z/YgjW5DvSxipd2FOC4Av0s6F
 l67vYazzPzf9d34NIM333FHeSZ4WMXVKKTfB34CQD93lVB1GladNA6lFFer5zuqG
 Sc8q+YGUF65OZEnbslANIDvqPG0eMNlqBn/iyqXQO+P/C6oXWIlqFm4DUtM5a8wB
 3Vf/e9L4mzXwKZfoW2xsJLgfpAO0faYiTAO6fr7wjBPrRl7VoenOlsCshN5GLTmM
 b4bQ29LZrpk9e3e8BdGdZzT/kfQEZ31O5sawaj6f+a2JG09xCi9qxklxV5wFvZ5E
 FTO+HgE=
 =PnHM
 -----END PGP SIGNATURE-----

Merge tag 'sound-7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

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
  ...
This commit is contained in:
Linus Torvalds 2026-09-17 09:57:09 -07:00
commit 4982d3552a
32 changed files with 316 additions and 119 deletions

View File

@ -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 {

View File

@ -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 */

View File

@ -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)

View File

@ -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;
}

View File

@ -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),

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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));

View File

@ -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,

View File

@ -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,

View File

@ -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) ...

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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)
},
{}
};

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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) */

View File

@ -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)

View File

@ -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 */
};

View File

@ -531,6 +531,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
*/
@ -577,6 +586,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x0763, 0x2031),
.selector_map = c400_selectors,
},
{
.id = USB_ID(0x07ca, 0x1553),
.map = avermedia_gc553pro_map,
},
{
.id = USB_ID(0x08bb, 0x2702),
.map = linex_map,

View File

@ -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);