diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-30 10:59:32 +0000 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-30 10:59:32 +0000 |
| commit | b50d35a6a0dc2cc35fc0eb7cdf1ec6467f54e48b (patch) | |
| tree | 31da4c94324331f6414f9c09d117796287e7252f | |
| parent | 84c657e31b3119e82b9734a7ab08c7766c7fdd5b (diff) | |
| parent | a4311e9447bbb219044db38897f95be112f46a8a (diff) | |
| download | linux-next-b50d35a6a0dc2cc35fc0eb7cdf1ec6467f54e48b.tar.gz linux-next-b50d35a6a0dc2cc35fc0eb7cdf1ec6467f54e48b.zip | |
Merge branch 'for-linus' of https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
| -rw-r--r-- | include/sound/info.h | 2 | ||||
| -rw-r--r-- | include/sound/ump.h | 5 | ||||
| -rw-r--r-- | sound/core/info.c | 23 | ||||
| -rw-r--r-- | sound/core/pcm.c | 56 | ||||
| -rw-r--r-- | sound/core/seq/seq_ump_client.c | 5 | ||||
| -rw-r--r-- | sound/core/ump.c | 63 | ||||
| -rw-r--r-- | sound/drivers/opl3/opl3_midi.c | 15 | ||||
| -rw-r--r-- | sound/drivers/opl3/opl3_oss.c | 1 | ||||
| -rw-r--r-- | sound/drivers/opl3/opl3_seq.c | 13 | ||||
| -rw-r--r-- | sound/drivers/opl3/opl3_voice.h | 1 | ||||
| -rw-r--r-- | sound/drivers/pcmtest.c | 24 | ||||
| -rw-r--r-- | sound/hda/codecs/realtek/alc269.c | 12 | ||||
| -rw-r--r-- | sound/hda/common/codec.c | 6 | ||||
| -rw-r--r-- | sound/hda/common/sysfs.c | 4 | ||||
| -rw-r--r-- | sound/hda/controllers/intel.c | 2 | ||||
| -rw-r--r-- | sound/pci/ac97/ac97_codec.c | 29 | ||||
| -rw-r--r-- | sound/pci/emu10k1/emufx.c | 18 | ||||
| -rw-r--r-- | sound/sh/aica.c | 2 | ||||
| -rw-r--r-- | sound/usb/endpoint.c | 6 | ||||
| -rw-r--r-- | sound/usb/misc/ua101.c | 29 | ||||
| -rw-r--r-- | sound/usb/pcm.c | 20 | ||||
| -rw-r--r-- | sound/usb/quirks-table.h | 65 | ||||
| -rw-r--r-- | sound/usb/quirks.c | 6 |
23 files changed, 333 insertions, 74 deletions
diff --git a/include/sound/info.h b/include/sound/info.h index adbc506860d6..f216291e5c41 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -117,6 +117,7 @@ struct snd_info_entry *snd_info_create_module_entry(struct module *module, struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent); +void snd_info_disconnect_entry(struct snd_info_entry *entry); void snd_info_free_entry(struct snd_info_entry *entry); int snd_info_card_create(struct snd_card *card); @@ -164,6 +165,7 @@ static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; } static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; } +static inline void snd_info_disconnect_entry(struct snd_info_entry *entry) { } static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; } static inline int snd_info_card_create(struct snd_card *card) { return 0; } diff --git a/include/sound/ump.h b/include/sound/ump.h index 73f97f88e2ed..387143783a9b 100644 --- a/include/sound/ump.h +++ b/include/sound/ump.h @@ -66,6 +66,7 @@ struct snd_ump_endpoint { #if IS_ENABLED(CONFIG_SND_SEQUENCER) struct snd_seq_device *seq_dev; + /* Published after seq_client; calls are covered by UMP core SRCU. */ const struct snd_seq_ump_ops *seq_ops; void *seq_client; #endif @@ -126,6 +127,10 @@ int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val); int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol); void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump); +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +void snd_ump_detach_seq(struct snd_ump_endpoint *ump); +#endif + /* * Some definitions for UMP */ diff --git a/sound/core/info.c b/sound/core/info.c index c4d57d0809ad..023b4736aa6d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -738,6 +738,23 @@ static void snd_info_clear_entries(struct snd_info_entry *entry) } /** + * snd_info_disconnect_entry - remove proc nodes and drain their callbacks + * @entry: root of the info entry subtree + * + * Retains the info entries for snd_info_free_entry(). Callers must serialize + * registration and destruction, and must not hold locks needed by callbacks. + */ +void snd_info_disconnect_entry(struct snd_info_entry *entry) +{ + if (entry && entry->p) { + proc_remove(entry->p); + guard(mutex)(&info_mutex); + snd_info_clear_entries(entry); + } +} +EXPORT_SYMBOL(snd_info_disconnect_entry); + +/** * snd_info_free_entry - release the info entry * @entry: the info entry * @@ -749,11 +766,7 @@ void snd_info_free_entry(struct snd_info_entry * entry) if (!entry) return; - if (entry->p) { - proc_remove(entry->p); - guard(mutex)(&info_mutex); - snd_info_clear_entries(entry); - } + snd_info_disconnect_entry(entry); /* free all children at first */ list_for_each_entry_safe(p, n, &entry->children, list) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1e4e0ecb3dbd..064df4cd3b50 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -520,6 +520,11 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) return 0; } +static void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) +{ + snd_info_disconnect_entry(pstr->proc_root); +} + static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { snd_info_free_entry(pstr->proc_root); @@ -580,6 +585,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) #else /* !CONFIG_SND_VERBOSE_PROCFS */ static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } +static inline void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) { } static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } #endif /* CONFIG_SND_VERBOSE_PROCFS */ @@ -1094,33 +1100,37 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int cidx; guard(mutex)(®ister_mutex); - guard(mutex)(&pcm->open_mutex); - wake_up(&pcm->open_wait); - list_del_init(&pcm->list); - - for_each_pcm_substream(pcm, cidx, substream) { - snd_pcm_stream_lock_irq(substream); - if (substream->runtime) { - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - /* to be sure, set the state unconditionally */ - __snd_pcm_set_state(substream->runtime, - SNDRV_PCM_STATE_DISCONNECTED); - wake_up(&substream->runtime->sleep); - wake_up(&substream->runtime->tsleep); + scoped_guard(mutex, &pcm->open_mutex) { + wake_up(&pcm->open_wait); + list_del_init(&pcm->list); + + for_each_pcm_substream(pcm, cidx, substream) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime) { + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + /* to be sure, set the state unconditionally */ + __snd_pcm_set_state(substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + wake_up(&substream->runtime->sleep); + wake_up(&substream->runtime->tsleep); + } + snd_pcm_stream_unlock_irq(substream); } - snd_pcm_stream_unlock_irq(substream); - } - for_each_pcm_substream(pcm, cidx, substream) - snd_pcm_sync_stop(substream, false); + for_each_pcm_substream(pcm, cidx, substream) + snd_pcm_sync_stop(substream, false); - pcm_call_notify(pcm, n_disconnect); - for (cidx = 0; cidx < 2; cidx++) { - if (pcm->streams[cidx].dev) - snd_unregister_device(pcm->streams[cidx].dev); - free_chmap(&pcm->streams[cidx]); + pcm_call_notify(pcm, n_disconnect); + for (cidx = 0; cidx < 2; cidx++) { + if (pcm->streams[cidx].dev) + snd_unregister_device(pcm->streams[cidx].dev); + free_chmap(&pcm->streams[cidx]); + } } + /* Proc rundown can run release callbacks which take open_mutex. */ + for (cidx = 0; cidx < 2; cidx++) + snd_pcm_stream_proc_disconnect(&pcm->streams[cidx]); return 0; } diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 4c1e81db376f..ca9dcec19883 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -353,12 +353,12 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) /* release the client resources */ static void seq_ump_client_free(struct seq_ump_client *client) { + snd_ump_detach_seq(client->ump); cancel_work_sync(&client->group_notify_work); if (client->seq_client >= 0) snd_seq_delete_kernel_client(client->seq_client); - client->ump->seq_ops = NULL; client->ump->seq_client = NULL; kfree(client); @@ -523,7 +523,8 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) snd_seq_kernel_client_put(cptr); ump->seq_client = client; - ump->seq_ops = &seq_ump_ops; + /* Publish the initialized client before callbacks can observe seq_ops. */ + smp_store_release(&ump->seq_ops, &seq_ump_ops); return 0; error: diff --git a/sound/core/ump.c b/sound/core/ump.c index f6482a9bfe61..d5a5322082f1 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/export.h> #include <linux/mm.h> +#include <linux/srcu.h> #include <sound/core.h> #include <sound/rawmidi.h> #include <sound/ump.h> @@ -18,6 +19,31 @@ #define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args) #define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +DEFINE_STATIC_SRCU(ump_seq_srcu); + +static const struct snd_seq_ump_ops *seq_ops_load(struct snd_ump_endpoint *ump) +{ + /* Pair with publication after seq_client has been initialized. */ + return smp_load_acquire(&ump->seq_ops); +} + +/** + * snd_ump_detach_seq - stop callbacks to a sequencer binding + * @ump: UMP endpoint + * + * Withdraw the binding and wait for all callbacks already in progress. + * The caller can then drain binding work and free the sequencer client. + * Must not be called from one of the binding callbacks. + */ +void snd_ump_detach_seq(struct snd_ump_endpoint *ump) +{ + WRITE_ONCE(ump->seq_ops, NULL); + synchronize_srcu(&ump_seq_srcu); +} +EXPORT_SYMBOL_GPL(snd_ump_detach_seq); +#endif + static int snd_ump_dev_register(struct snd_rawmidi *rmidi); static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi); static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd, @@ -348,8 +374,13 @@ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count) continue; ump_handle_stream_msg(ump, ump->input_buf, n); #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops) - ump->seq_ops->input_receive(ump, ump->input_buf, n); + scoped_guard(srcu, &ump_seq_srcu) { + const struct snd_seq_ump_ops *ops; + + ops = seq_ops_load(ump); + if (ops) + ops->input_receive(ump, ump->input_buf, n); + } #endif process_legacy_input(ump, ump->input_buf, n); } @@ -559,6 +590,7 @@ static void snd_ump_proc_read(struct snd_info_entry *entry, /* update dir_bits and active flag for all groups in the client */ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) { + static const char separator[] = ", "; struct snd_ump_block *fb; struct snd_ump_group *group; int i; @@ -598,7 +630,8 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) if (!*fb->info.name) continue; if (*group->name) - strlcat(group->name, ", ", sizeof(group->name)); + safe_append_string(group->name, sizeof(group->name), + separator, sizeof(separator)); safe_append_string(group->name, sizeof(group->name), fb->info.name, sizeof(fb->info.name)); } @@ -699,8 +732,12 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump) static void seq_notify_ep_change(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change) - ump->seq_ops->notify_ep_change(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ump->parsed && ops && ops->notify_ep_change) + ops->notify_ep_change(ump); #endif } @@ -797,8 +834,12 @@ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump, static void seq_notify_protocol(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->switch_protocol) - ump->seq_ops->switch_protocol(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->switch_protocol) + ops->switch_protocol(ump); #endif /* CONFIG_SND_SEQUENCER */ } @@ -893,8 +934,12 @@ static void seq_notify_fb_change(struct snd_ump_endpoint *ump, struct snd_ump_block *fb) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->notify_fb_change) - ump->seq_ops->notify_fb_change(ump, fb); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->notify_fb_change) + ops->notify_fb_change(ump, fb); #endif } diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 6d3c5b5a35ff..de611214cdac 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -632,6 +632,21 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice) } +void snd_opl3_clear_voices(struct snd_opl3 *opl3) +{ + int i; + + guard(spinlock_irqsave)(&opl3->voice_lock); + /* A 4-op primary clears its higher-numbered partner's state. */ + for (i = MAX_OPL3_VOICES - 1; i >= 0; i--) { + if (opl3->voices[i].state > 0) + snd_opl3_kill_voice(opl3, i); + opl3->voices[i].chan = NULL; + opl3->voices[i].note_off_check = 0; + opl3->voices[i].state = SNDRV_OPL3_ST_OFF; + } +} + /* * Release a note in response to a midi note off. */ diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 6d39b2b77b80..1f01d27955fe 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -41,6 +41,7 @@ static void snd_opl3_oss_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->oss_chset); } diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index b77d8e8f12fb..1004a7df1b8c 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -152,6 +152,7 @@ static void snd_opl3_synth_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->chset); } @@ -214,6 +215,11 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) spin_lock_init(&opl3->voice_lock); + /* Initialize the timer before an attached port can receive events. */ + timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + opl3->seq_client = -1; /* allocate new client */ @@ -232,11 +238,6 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) return err; } - /* setup system timer */ - timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); - spin_lock_init(&opl3->sys_timer_lock); - opl3->sys_timer_status = 0; - #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_init_seq_oss(opl3, name); #endif @@ -258,6 +259,8 @@ static void snd_opl3_seq_remove(struct snd_seq_device *dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } + /* Stop the shared timer after all event callbacks have been drained. */ + timer_shutdown_sync(&opl3->tlist); } static struct snd_seq_driver opl3_seq_driver = { diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h index be9ccca2d952..aa81512d23c9 100644 --- a/sound/drivers/opl3/opl3_voice.h +++ b/sound/drivers/opl3/opl3_voice.h @@ -13,6 +13,7 @@ int snd_opl3_synth_use_inc(struct snd_opl3 * opl3); void snd_opl3_synth_use_dec(struct snd_opl3 * opl3); int snd_opl3_synth_setup(struct snd_opl3 * opl3); void snd_opl3_synth_cleanup(struct snd_opl3 * opl3); +void snd_opl3_clear_voices(struct snd_opl3 *opl3); /* Prototypes for opl3_midi.c */ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index d1b0dff56cd9..66bee8a2ce34 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -72,13 +72,32 @@ static u8 playback_capture_test; static u8 ioctl_reset_test; static struct dentry *driver_debug_dir; +static int fill_mode_set(const char *val, const struct kernel_param *kp) +{ + short mode; + int err; + + err = kstrtos16(val, 0, &mode); + if (err) + return err; + if (mode != FILL_MODE_RAND && mode != FILL_MODE_PAT) + return -EINVAL; + fill_mode = mode; + return 0; +} + +static const struct kernel_param_ops fill_mode_ops = { + .set = fill_mode_set, + .get = param_get_short, +}; + module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for pcmtest soundcard"); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for pcmtest soundcard"); module_param(enable, bool, 0444); MODULE_PARM_DESC(enable, "Enable pcmtest soundcard."); -module_param(fill_mode, short, 0600); +module_param_cb(fill_mode, &fill_mode_ops, &fill_mode, 0600); MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)"); module_param(inject_delay, int, 0600); MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)"); @@ -693,6 +712,9 @@ static int __init mod_init(void) { int err = 0; + if (!enable) + return -ENODEV; + buf_allocated = setup_patt_bufs(); if (!buf_allocated) return -ENOMEM; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 23aa175faedd..23329d5887a5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4492,6 +4492,7 @@ enum { ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, + ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS, ALC287_FIXUP_THINKPAD_I2S_SPK, ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC287_FIXUP_CS35L41_I2C_4_THINKPAD_ACPI, @@ -7113,6 +7114,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC245_FIXUP_HP_GPIO_LED }, + [ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_v2_coefbit, + .chained = true, + .chain_id = ALC245_FIXUP_HP_GPIO_LED + }, [ALC287_FIXUP_THINKPAD_I2S_SPK] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_bind_dacs, @@ -7869,6 +7876,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), @@ -7951,6 +7959,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c31, "HP Pavilion Plus Laptop 14-ew1xxx", ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS), SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -8060,6 +8069,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + HDA_CODEC_QUIRK(0x103c, 0x84ee, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK), SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), @@ -8214,6 +8224,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e03, "ASUS ExpertBook B3402FVA", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e3e, "ASUS VivoBook M515DA/X515DAP", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), @@ -8226,6 +8237,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), + SND_PCI_QUIRK(0x1043, 0x1ec1, "ASUS X509JB", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 31e5fad78b0f..e367cc1c6e78 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1807,6 +1807,12 @@ int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; + /* ASoC: driver unbind frees the card, incl. the hwdep issuing the reset */ + if (bus->core.ext_ops) { + codec_err(codec, "Codec reset is not supported with ASoC\n"); + return -EOPNOTSUPP; + } + if (snd_hda_lock_devices(bus) < 0) return -EBUSY; diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c index bedf10b30885..bf65223fde10 100644 --- a/sound/hda/common/sysfs.c +++ b/sound/hda/common/sysfs.c @@ -117,6 +117,8 @@ static int clear_codec(struct hda_codec *codec) int err; err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't free.\n"); return err; @@ -132,6 +134,8 @@ static int reconfig_codec(struct hda_codec *codec) CLASS(snd_hda_power, pm)(codec); codec_info(codec, "hda-codec: reconfiguring\n"); err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't reconfigure.\n"); diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 6b80b59b8743..29679fe7f401 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -1668,6 +1668,7 @@ static const struct snd_pci_quirk msi_deny_list[] = { SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ + SND_PCI_QUIRK(0x1d94, 0xc950, "Hygon H34M51A", 1), /* INTx broken */ {} }; @@ -2329,6 +2330,7 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */ SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP 89E9", 0), {} }; diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 207ce083593f..f0ebe6d45836 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1832,36 +1832,39 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, size_t maxlen, int modem) { const struct ac97_codec_id *pid; + size_t len; - sprintf(name, "0x%x %c%c%c", id, - printable(id >> 24), - printable(id >> 16), - printable(id >> 8)); pid = look_for_codec_id(snd_ac97_codec_id_vendors, id); - if (! pid) + if (!pid) { + snprintf(name, maxlen, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); return; + } strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); - } + } + len = strnlen(name, maxlen); pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strlcat(name, " ", maxlen); - strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) - sprintf(name + strlen(name), " rev %u", id & ~pid->mask); + snprintf(name + len, maxlen - len, " %s rev %u", + pid->name, id & ~pid->mask); + else + snprintf(name + len, maxlen - len, " %s", pid->name); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } } else { - int l = strlen(name); - snprintf(name + l, maxlen - l, " id %x", id & 0xff); + snprintf(name + len, maxlen - len, " id %x", id & 0xff); } } diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index a33817aba17f..d49866d9961e 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2423,12 +2423,12 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file) return 0; } -static void copy_string(char *dst, const char *src, const char *null, int idx) +static void copy_string(char *dst, size_t size, const char *src, const char *null, int idx) { if (src == NULL) - sprintf(dst, "%s %02X", null, idx); + snprintf(dst, size, "%s %02X", null, idx); else - strcpy(dst, src); + strscpy(dst, src, size); } static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, @@ -2446,12 +2446,16 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask; extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask; for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { - copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res); - copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->fxbus_names[res], sizeof(info->fxbus_names[res]), + *fxbus, "FXBUS", res); + copy_string(info->extin_names[res], sizeof(info->extin_names[res]), + extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); } for (res = 16; res < 32; res++, extout++) - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); info->gpr_controls = emu->fx8010.gpr_count; } diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 078fc7fc08f9..04f80c8c89c0 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -94,7 +94,7 @@ static void spu_memset(u32 toi, u32 what, int length) local_irq_save(flags); writel(what, toi + SPU_MEMORY_BASE); local_irq_restore(flags); - toi++; + toi += sizeof(what); } } diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 9c844c55ad3f..8dd0b3525e4b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1642,9 +1642,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) err = -ENODEV; if (err < 0) { if (!atomic_read(&ep->chip->shutdown)) - usb_audio_err(ep->chip, - "cannot submit urb %d, error %d: %s\n", - i, err, usb_error_string(err)); + usb_audio_err_ratelimited(ep->chip, + "cannot submit urb %d, error %d: %s\n", + i, err, usb_error_string(err)); goto __error; } set_bit(i, &ep->active_mask); diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 58d8a3cdc8b9..bd8e0ed0ba5d 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -285,13 +285,29 @@ static void playback_work(struct work_struct *work) /* fill packet with data or silence */ urb->iso_frame_desc[0].length = frames * ua->playback.frame_bytes; - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { + /* + * The packet size comes from the capture stream, + * i.e. from the device, while the ring buffer can + * be as small as two tiny periods. The single wrap + * in copy_playback_data() is only valid for + * frames <= buffer_size, so copy at most one + * buffer and send silence for the rest. + */ + unsigned int copy_frames = min(frames, + ua->playback.substream->runtime->buffer_size); + do_period_elapsed |= copy_playback_data(&ua->playback, urb, - frames); - else + copy_frames); + memset(urb->transfer_buffer + + copy_frames * ua->playback.frame_bytes, 0, + (frames - copy_frames) * + ua->playback.frame_bytes); + } else { memset(urb->transfer_buffer, 0, urb->iso_frame_desc[0].length); + } /* and off you go ... */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -320,6 +336,13 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, runtime = stream->substream->runtime; frame_bytes = stream->frame_bytes; + /* + * A packet can carry up to max_packet_bytes, the device's + * wMaxPacketSize, which may exceed the whole ring buffer; keep what + * fits so the single wrap below stays valid. + */ + if (unlikely(frames > runtime->buffer_size)) + frames = runtime->buffer_size; dest = runtime->dma_area + stream->buffer_pos * frame_bytes; if (stream->buffer_pos + frames <= runtime->buffer_size) { memcpy(dest, urb->transfer_buffer, frames * frame_bytes); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 682b6c1fe76b..c2f7b08607ac 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1343,6 +1343,18 @@ static void retire_capture_urb(struct snd_usb_substream *subs, bytes -= adj; subs->stream_offset_adj -= adj; } + /* + * A packet can be as large as the endpoint's maxpacksize, which + * the device chooses, while the ring buffer can be as small as + * two 64-byte periods. The hwptr_done wrap below handles one + * wrap only, so never take more than one buffer from a packet. + */ + if (unlikely(bytes > subs->buffer_bytes)) { + dev_warn_ratelimited(&subs->dev->dev, + "%u-byte packet exceeds %u-byte buffer, truncating\n", + bytes, subs->buffer_bytes); + bytes = subs->buffer_bytes; + } frames = bytes / stride; if (!subs->txfr_quirk) bytes = frames * stride; @@ -1553,8 +1565,14 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, for (i = 0; i < ctx->packets; i++) { counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); + /* + * Bound by the ring buffer as well as by the URB: + * copy_to_urb() wraps hwptr_done once, so one URB must + * never carry more than the whole buffer. + */ if (counts < 0 || - (frames + counts) * stride > ctx->buffer_size) + (frames + counts) * stride > ctx->buffer_size || + (frames + counts) * stride > subs->buffer_bytes) break; /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 6343a50fb273..4793fd1f85fe 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2360,6 +2360,69 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + /* + * Novation UltraNova: vendor-specific class descriptors, fixed + * audio and MIDI layouts. Only the audio and MIDI interfaces + * (0 and 1) are grabbed; the vendor control interfaces (2/3) + * are left unbound for userspace access. + */ + USB_DEVICE_INTERFACE_NUMBER(0x1235, 0x0011, 0), + QUIRK_DRIVER_INFO { + .vendor_name = "Novation", + .product_name = "UltraNova", + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* playback */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 392, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* capture */ + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x82, + .ep_idx = 1, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .maxpacksize = 294, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { QUIRK_DATA_RAW_BYTES(1) }, + QUIRK_COMPOSITE_END + } + } +}, +{ USB_DEVICE(0x1235, 0x001e), QUIRK_DRIVER_INFO { /* .vendor_name = "Novation", */ @@ -3005,6 +3068,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, @@ -3027,6 +3091,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 43eea724779a..4ec1f2a0b359 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1794,7 +1794,7 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, /* the quirk seems breaking on some Behringer devices */ switch (id) { case USB_ID(0x1397, 0x0508): /* UMC204HD */ - case USB_ID(0x1397, 0x0509): /* UMC204HD */ + case USB_ID(0x1397, 0x0509): /* UMC404HD */ return 0; } return snd_usb_cm1a_boot_quirk(dev); @@ -2496,6 +2496,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x0124, 0x0c36, /* RYDZ Technology USB Headphone */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x0c56, /* HP Elite x3 Lap Dock */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ @@ -2659,6 +2661,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0db0, 0xa073, /* MSI MAG X570S Tomahawk/Torpedo Max (ALC4080) */ + QUIRK_FLAG_SKIP_IFACE_SETUP), DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ |
