diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-31 12:30:31 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-31 12:30:31 -0700 |
| commit | e9954f3fbddd25e1a01b7e5a0b72fb8b564204e0 (patch) | |
| tree | 6932c388b83c3c2366a340117adb4a515f7572cf /sound | |
| parent | ca62fc8c6b1841039aee6de1ef03c9863751ec04 (diff) | |
| parent | 74f6055d0c5997b13e6a5253fdc2263f3af90408 (diff) | |
Merge tag 'sound-fix-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/soundHEADmaster
Pull sound fixes from Takashi Iwai:
"A collection of fixes across the sound subsystem. Nothing particularly
stands out, mostly driver- and device-specific fixes along with a few
race / UAF fixes and cleanups. Here are some highlights:
ALSA core:
- Fix potential race in PCM proc handling at disconnection
- Fix potential race in UMP sequencer callback at releasing
HD-audio:
- Reject codec reset/reconfig on ASoC buses to prevent deadlock
- Force MSI for Hygon H34M51A board
- Usual Realtek quirks for ASUS and HP laptops/AiO
USB-audio:
- Bound packet copies by PCM buffer size in generic and ua101 drivers
- Rate-limit URB submission errors at stream start
- Skip redundant probe-time interface setup on MSI ALC4080
- Device quirks for Novation UltraNova, MOTU MicroBook II, and RYDZ
Headphone
ASoC:
- Fixes for runtime PM and IRQ handling in hdac_hda and SDCA
- SOF: Fix potential UAF at release, fix for Kconfig dependency
- Qualcomm: Fixes for sm8250 and q6apm/q6afe
- NXP / i.MX: Fix for pending work in hw_free
- Xilinx: Fix read-modify-write of AUD_CTRL in formatter_pcm
- Intel: Arrow Lake SoundWire match table additions (RT712, RT713 +
dual RT1320)
- AMD: DMI quirk for HP Laptop 14-em0xxx and memory leak fix in Raven
DMA close
- Various codec fixes for midas_wm1811, wm8994, cs35l56, cs42l56,
WSA881x/883x/884x, es8326/es8375/es9356, rt721/rt722, and
tlv320aic32x4
Misc:
- OPL3: Fix races in timer initialization, shutdown, and voice
cleanup
- Bounds checks and string helpers for AC97 and emu10k1
- Fix spu_memset() stride in legacy SH AICA driver
- Parameter handling fixes in pcmtest virtual driver"
* tag 'sound-fix-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (71 commits)
ASoC: SOF: Intel: hda: Stop the controller before freeing its DMA buffers
ASoC: imx-pcm-rpmsg: Flush pending work in hw_free
ASoC: xilinx: formatter_pcm: Fix read-modify-write of AUD_CTRL in hw_params
ASoC: hdac_hda: Keep runtime PM state in sync across codec remove/probe
ASoC: rt721: Remove invalid register defaults
ASoC: hdac_hda: Balance the runtime PM disable in codec remove
ASoC: hdac_hda: Restore the codec device type on unbind
ASoC: hdac_hda: Allocate the private data at codec driver bind
ASoC: qcom: sm8250: Limit RX digital gain to 0 dB
ALSA: hda: Reject codec reset on ASoC (ext_ops) buses
ALSA: usb-audio: add quirk for Novation UltraNova (1235:0011)
ALSA: usb-audio: Add correct datainterval in the quirks-table for Microbook II
ALSA: usb-audio: Skip probe-time interface setup for MSI MAG X570S ALC4080
ALSA: hda/realtek: Extend quirk for HP Pavilion All-in-One 27-ca1xxx
ALSA: pcmtest: Honor the enable parameter
ALSA: ua101: Bound packet copies by the PCM buffer size
ALSA: usb-audio: Bound packet copies by the PCM buffer size
ASoC: SDCA: re-enable IRQ on pm_runtime_force_suspend failure
ASoC: SDCA: drop pm_runtime reference on probe failure path
ASoC: SDCA: propagate sdw_slave_read_prop() failure in class_read_prop
...
Diffstat (limited to 'sound')
58 files changed, 1007 insertions, 322 deletions
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/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 765057499021..0862a431b2d1 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -360,6 +360,8 @@ static int acp3x_dma_close(struct snd_soc_component *component, } } + kfree(ins); + return 0; } diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 41f8c0885865..ae26524453e8 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -763,6 +763,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8DC2"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), DMI_MATCH(DMI_BOARD_NAME, "8EE4"), } }, diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index e10f04d677f4..a49ad69644b5 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/soundwire/sdw.h> @@ -36,6 +37,9 @@ #define CS35L56_LATE_READ_POLL_US 10 #define CS35L56_LATE_READ_TIMEOUT_US 1000 +static DEFINE_MUTEX(cs35l56_fw_idle_wait_lock); +static bool cs35l56_fw_idle_wait_pending; + static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral, unsigned int mask, unsigned int match) @@ -387,14 +391,34 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) return 0; } +static int cs35l56_sdw_system_suspend_prepare(struct device *dev) +{ + cs35l56_fw_idle_wait_pending = true; + + return 0; +} + static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; if (cs35l56->sdw_attached) cs35l56_mask_soundwire_interrupts(cs35l56); - return cs35l56_system_suspend(dev); + ret = cs35l56_system_suspend(dev); + if (ret < 0) + return ret; + + scoped_guard(mutex, &cs35l56_fw_idle_wait_lock) { + if (cs35l56_fw_idle_wait_pending && cs35l56->needs_wait_for_fw_idle) { + dev_dbg(cs35l56->base.dev, "Wait for FW timer expiry\n"); + msleep(CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 10); + cs35l56_fw_idle_wait_pending = false; + } + } + + return 0; } static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) @@ -460,6 +484,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral) } static const struct dev_pm_ops cs35l56_sdw_pm = { + .prepare = cs35l56_sdw_system_suspend_prepare, SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 7b3e37d462d6..b37f65cd4d33 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1379,6 +1379,44 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base) } EXPORT_SYMBOL_NS_GPL(cs35l56_warn_if_firmware_missing, "SND_SOC_CS35L56_SHARED"); +bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base) +{ + unsigned int fw_ver; + bool prot_sts; + int ret; + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + switch (cs35l56_base->rev) { + case 0xb0: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x30d07) + return true; + + return false; + case 0xb2: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x40710) + return true; + + return false; + default: + return false; + } + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs35l56_needs_wait_for_firmware_timer_expiry, "SND_SOC_CS35L56_SHARED"); + void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) { __be32 pid, sid, tid; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index dd264c310e1e..a4d25c287941 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1703,6 +1703,10 @@ int cs35l56_system_resume(struct device *dev) dev_dbg(dev, "system_resume\n"); + /* Assume the firmware reset to default until we can check it */ + if (cs35l56->base.init_done) + cs35l56->needs_wait_for_fw_idle = true; + /* * We might have done a hard reset or the CS35L56 was power-cycled * so wait for control port to be ready. @@ -1717,6 +1721,16 @@ int cs35l56_system_resume(struct device *dev) if (ret) return ret; + if (cs35l56->sdw_peripheral && cs35l56->base.init_done) { + ret = pm_runtime_resume_and_get(cs35l56->base.dev); + if (ret < 0) + return ret; + + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + pm_runtime_put_autosuspend(cs35l56->base.dev); + } + /* Firmware won't have been loaded if the component hasn't probed */ if (!cs35l56->component) return 0; @@ -2241,6 +2255,9 @@ post_soft_reset: if (ret) return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + cs35l56->base.init_done = true; complete_all(&cs35l56->init_completion); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index f7cf8aa653e2..b46f410f0e09 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -43,6 +43,7 @@ struct cs35l56_private { bool soft_resetting; bool sdw_attached; bool component_registered; + bool needs_wait_for_fw_idle; struct completion init_completion; int speaker_id; diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 54d23857b0ad..ecce51985a11 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -419,7 +419,6 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = { CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv), SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR, CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv), - SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1), SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1), SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME, diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a95b3b8c75dd..a1b93f6fafd9 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -353,7 +353,7 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE, 4, 0x07, 0, dre_gate_tlv), SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), - SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, + SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_RECOVERY, 0, 4, 0, drc_recovery_tlv), SOC_ENUM("ALC Capture Winsize", alc_winsize), SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index bed872eafc95..501a3eda5a89 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -199,68 +199,68 @@ struct _coeff_div { static const struct _coeff_div coeff_div[] = { {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, - {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, + {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {32, 1536000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, - {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, - {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, - {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, - {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, - {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2}, - {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, - {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, - {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, - {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, - {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, - {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2}, - {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, - {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, - {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, - {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, - {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2}, - {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, - {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2}, - {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, - {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, - {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2}, - {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, - {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, - {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2}, - {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, - {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, - {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, - {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, - {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, - {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, - {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, - {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, - {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, - {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, - {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, - {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, - {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, - {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2}, - {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2}, - {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2}, - {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2}, - {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, - {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2}, + {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {36, 1728000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {48, 2304000, 48000, 0x05, 0x91, 0x53, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2}, + {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {50, 2400000, 48000, 0x05, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {64, 3072000, 48000, 0x05, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {72, 3456000, 48000, 0x05, 0x91, 0x53, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {96, 4608000, 48000, 0x15, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {100, 4800000, 48000, 0x03, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2}, + {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {128, 6144000, 48000, 0x03, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2}, + {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {144, 6912000, 48000, 0x03, 0x80, 0x31, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2}, + {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {192, 9216000, 48000, 0x15, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2}, + {250, 12000000, 48000, 0x25, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {256, 12288000, 48000, 0x0B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2}, + {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {384, 18432000, 48000, 0x13, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2}, + {400, 19200000, 48000, 0x1B, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {500, 24000000, 48000, 0x23, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2}, + {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {512, 24576000, 48000, 0x1B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2}, + {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2}, + {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2}, + {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2}, + {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2}, + {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2}, + {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2}, + {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2}, + {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x1F, 0x1F, 2, 2}, + {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x23, 0x1F, 2, 2}, + {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x25, 0x27, 2, 2}, + {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x2F, 0x1F, 2, 2}, + {32, 3072000, 96000, 0x05, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {64, 6144000, 96000, 0x03, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {96, 9216000, 96000, 0x15, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2}, + {128, 12288000, 96000, 0x0B, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x01, 0x37, 2, 2}, }; static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) @@ -543,18 +543,20 @@ static void es8375_init(struct snd_soc_component *component) regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02); regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05); regmap_write(es8375->regmap, ES8375_CSM1, 0x82); - regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20); - regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20); - regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28); - regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x10); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x10); + regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x2F); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xEC); regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0); regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE); regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8); + regmap_write(es8375->regmap, ES8375_VSEL2, 0x05); + regmap_write(es8375->regmap, ES8375_LP_DAC, 0xCC); regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03); regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16); regmap_write(es8375->regmap, ES8375_RESET1, 0x00); msleep(80); - regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00); + regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x08); regmap_write(es8375->regmap, ES8375_CSM1, 0x86); regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B); regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00); @@ -566,7 +568,7 @@ static void es8375_init(struct snd_soc_component *component) regmap_write(es8375->regmap, ES8375_ADC2, 0x00); regmap_write(es8375->regmap, ES8375_DAC2, 0x00); regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88); - regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE3); regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0); regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40); regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE); diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h index 11e3ceec9b68..de133437cefe 100644 --- a/sound/soc/codecs/es8375.h +++ b/sound/soc/codecs/es8375.h @@ -49,6 +49,9 @@ #define ES8375_ANALOG_SPK2 0x29 #define ES8375_VMID_SEL 0x2D #define ES8375_ANALOG1 0x2E +#define ES8375_VSEL1 0x2F +#define ES8375_VSEL2 0x30 +#define ES8375_LP_DAC 0x31 #define ES8375_ANALOG2 0x32 #define ES8375_ANALOG3 0x37 #define ES8375_ADC2DAC_CLKTRI 0xF8 @@ -91,7 +94,7 @@ // Function values #define ES8375_ADC_OSR_GAIN_MAX 0x3F -#define ES8375_DMIC_GAIN_MAX 0x04 +#define ES8375_DMIC_GAIN_MAX 0x03 #define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F #define ES8375_AUTOMUTE_NG_MAX 0x07 #define ES8375_ADC_VOLUME_MAX 0xFF diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index b8b3ffc13fd5..9c70ccd5b310 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -352,7 +352,7 @@ static int es9356_sdca_headset_detect(struct es9356_sdw_priv *es9356) if (reg) { regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0), reg); - regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x75); + regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x74); } else { regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); } @@ -403,7 +403,7 @@ static void es9356_interrupt_handler(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4); mod_delayed_work(system_power_efficient_wq, - &es9356->button_detect_work, msecs_to_jiffies(280)); + &es9356->button_detect_work, msecs_to_jiffies(50)); } } @@ -450,7 +450,7 @@ static void es9356_button_detect_handler(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4); mod_delayed_work(system_power_efficient_wq, - &es9356->button_detect_work, msecs_to_jiffies(280)); + &es9356->button_detect_work, msecs_to_jiffies(50)); } return; @@ -724,68 +724,122 @@ static bool es9356_sdca_volatile_register(struct device *dev, unsigned int reg) } } +static bool es9356_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES9356_VERSION: + case ES9356_BUF_ADDR_HID: + case ES9356_HID_BYTE2: + case ES9356_HID_BYTE3: + case ES9356_HID_BYTE4: + return false; + default: + return true; + } +} + static const struct regmap_config es9356_sdca_regmap = { .reg_bits = 32, .val_bits = 16, .volatile_reg = es9356_sdca_volatile_register, + .writeable_reg = es9356_writeable_register, .max_register = 0x45ffffff, .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; -static void es9356_register_init(struct es9356_sdw_priv *es9356) +static int es9356_register_init(struct es9356_sdw_priv *es9356) { + unsigned int version; + int ret; + + ret = regmap_read(es9356->regmap, ES9356_VERSION, &version); + if (ret < 0) { + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; + } + + regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x2c); + if (!(version >> 1)) + regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x11); + else + regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x00); + + regmap_write(es9356->regmap, ES9356_SPKCLK_CTL, 0xc3); regmap_write(es9356->regmap, ES9356_STATE, 0x02); regmap_write(es9356->regmap, ES9356_ENDPOINT_MODE, 0x24); regmap_write(es9356->regmap, ES9356_PRE_DIV_CTL, 0x00); regmap_write(es9356->regmap, ES9356_ADC_OSR, 0x18); - regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x13); + regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x11); regmap_write(es9356->regmap, ES9356_DAC_OSR, 0x16); regmap_write(es9356->regmap, ES9356_CLK_CTL, 0x0f); - regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01); - regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30); + if (!(version >> 1)) + regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30); + else + regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x10); + + regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01); regmap_write(es9356->regmap, ES9356_DETCLK_CTL, 0x51); - regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x10); + regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x00); regmap_write(es9356->regmap, ES9356_MICBIAS_CTL, 0x10); regmap_write(es9356->regmap, ES9356_HPDETECT_CTL, 0x07); regmap_write(es9356->regmap, ES9356_ADC_ANA, 0x30); - regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xa8); + regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xaa); regmap_write(es9356->regmap, ES9356_ADC_INT, 0xaa); regmap_write(es9356->regmap, ES9356_ADC_LP, 0x19); regmap_write(es9356->regmap, ES9356_VMID1SEL, 0xbc); regmap_write(es9356->regmap, ES9356_VMID_TIME, 0x0b); regmap_write(es9356->regmap, ES9356_STATE_TIME, 0xbb); - regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x77); + regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x7a); regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); - regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15); + + if ((version & GENMASK(1, 0)) == 0x03) + regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15); + else + regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x25); + + regmap_write(es9356->regmap, ES9356_SPARE_REGISTER3, 0x00); regmap_write(es9356->regmap, ES9356_KEY_PRESS_TIME, 0xff); regmap_write(es9356->regmap, ES9356_KEY_RELEASE_TIME, 0xff); regmap_write(es9356->regmap, ES9356_KEY_HOLD_TIME, 0x0f); - regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00); + + if ((version & GENMASK(1, 0)) == 0x03) + regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x04); + else + regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00); + regmap_write(es9356->regmap, ES9356_KEYD_DETECT, 0x18); regmap_write(es9356->regmap, ES9356_MICBIAS_RES, 0x03); - regmap_write(es9356->regmap, ES9356_BUTTON_CHARGE, 0x00); - regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x13); - regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4); + regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x1a); + + if (!(version >> 1)) { + regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xc3); + } else { + regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4); + regmap_write(es9356->regmap, ES9356_SPK_SCP, 0x55); + } regmap_write(es9356->regmap, ES9356_SPK_VOLUME, 0x33); regmap_write(es9356->regmap, ES9356_DAC_VROI, 0x01); regmap_write(es9356->regmap, ES9356_DAC_LP, 0x00); regmap_write(es9356->regmap, ES9356_HP_IBIAS, 0x04); regmap_write(es9356->regmap, ES9356_HP_LP, 0x03); - regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x65); + regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x6c); regmap_write(es9356->regmap, ES9356_SPKBIAS_COMP, 0x09); - regmap_write(es9356->regmap, ES9356_VMID1STL, 0x00); + regmap_write(es9356->regmap, ES9356_VMID1STL, 0x01); regmap_write(es9356->regmap, ES9356_VMID2STL, 0x00); regmap_write(es9356->regmap, ES9356_VSEL, 0xfc); regmap_write(es9356->regmap, ES9356_IBIASGEN, 0x10); - regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x0d); + regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x05); regmap_write(es9356->regmap, ES9356_STATE, 0x0e); regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x00); regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x08); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE, 0x31); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_WINSIZE, 0x3D); + regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_RESULT1, 0x80); regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); @@ -806,11 +860,14 @@ static void es9356_register_init(struct es9356_sdw_priv *es9356) SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); regmap_write(es9356->regmap, SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); + + return 0; } static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + int ret = 0; if (es9356->hw_init) return 0; @@ -825,7 +882,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - es9356_register_init(es9356); + ret = es9356_register_init(es9356); + if (ret < 0) { + regcache_cache_only(es9356->regmap, true); + goto err_out; + } } pm_runtime_get_noresume(&slave->dev); @@ -852,10 +913,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) es9356->hw_init = true; +err_out: pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); - return 0; + return ret; } static int es9356_sdw_update_status(struct sdw_slave *slave, @@ -1000,10 +1062,10 @@ static int es9356_sdw_interrupt_callback(struct sdw_slave *slave, count++; } while (stat != 0 && count < retry); - /* The 280 ms figure was determined through testing */ + /* The 50 ms figure was determined through testing */ if (status->sdca_cascade && !es9356->disable_irq) mod_delayed_work(system_power_efficient_wq, - &es9356->interrupt_handle_work, msecs_to_jiffies(280)); + &es9356->interrupt_handle_work, msecs_to_jiffies(50)); mutex_unlock(&es9356->disable_irq_lock); return 0; diff --git a/sound/soc/codecs/es9356.h b/sound/soc/codecs/es9356.h index 2a676f36590f..c147ecdd893f 100644 --- a/sound/soc/codecs/es9356.h +++ b/sound/soc/codecs/es9356.h @@ -4,6 +4,7 @@ #define __ES9356_H__ /*ES9356 Implementation-define*/ +#define ES9356_VERSION 0x2000 #define ES9356_FLAGS_HP 0x2003 #define ES9356_CSM_RESET 0x2020 #define ES9356_FUC_RESET 0x2021 @@ -37,6 +38,7 @@ #define ES9356_MICBIAS_CTL 0x2047 #define ES9356_HPDETECT_CTL 0x2048 #define ES9356_MICBIAS_RES 0x2049 +#define ES9356_SPARE_REGISTER3 0x204a /*CLK*/ #define ES9356_CLK_SEL 0x2050 @@ -81,6 +83,9 @@ #define ES9356_DAC_RAMP 0x2093 #define ES9356_DAC_VPPSCALE 0x2094 #define ES9356_DAC_SWAP 0x2097 +#define ES9356_DAC_AUTOMUTE 0x2098 +#define ES9356_DAC_AUTOMUTE_WINSIZE 0x2099 +#define ES9356_DAC_AUTOMUTE_RESULT1 0x209B #define ES9356_SPKCMP_VPPSC 0x20A0 #define ES9356_CALIBRATION_TIME 0x20A1 #define ES9356_CALIBRATION_SETTING 0x20A2 diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 7414be08d3d5..48eb95973b7a 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -514,6 +514,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, false); + /* The codec is in D0, the status is RPM_SUSPENDED after a previous unbind */ + pm_runtime_set_active(&hdev->dev); + /* match for forbid call in snd_hda_codec_device_new() */ pm_runtime_allow(&hdev->dev); @@ -554,6 +557,13 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) return; } + /* + * Resume the codec to have its link reference (dropped below) and + * codec_powered bit in the same state as after the probe. + */ + pm_runtime_get_sync(&hdev->dev); + + /* prevent runtime PM callbacks while the link and the codec are torn down */ pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); @@ -561,6 +571,12 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); + + /* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */ + pm_runtime_enable(&hdev->dev); + pm_runtime_put_noidle(&hdev->dev); + /* The codec is cleaned up, system suspend must skip it from now on */ + pm_runtime_set_suspended(&hdev->dev); } static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { @@ -617,12 +633,35 @@ static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { .endianness = 1, }; +static void hdac_hda_restore_dev_type(void *data) +{ + struct hdac_device *hdev = data; + + hdev->type = HDA_DEV_LEGACY; +} + static int hdac_hda_dev_probe(struct hdac_device *hdev) { - struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev); + struct hdac_hda_priv *hda_pvt; struct hdac_ext_link *hlink; int ret; + hda_pvt = devm_kzalloc(&hdev->dev, sizeof(*hda_pvt), GFP_KERNEL); + if (!hda_pvt) + return -ENOMEM; + + hda_pvt->codec = dev_to_hda_codec(&hdev->dev); + hda_pvt->dev_index = hdev->addr; + /* Intel iDisp (HDMI/DP) codec */ + if (((hdev->vendor_id >> 16) & 0xFFFF) == 0x8086) + hda_pvt->need_display_power = true; + dev_set_drvdata(&hdev->dev, hda_pvt); + + /* Allow the legacy codec driver to match again after unbind */ + ret = devm_add_action(&hdev->dev, hdac_hda_restore_dev_type, hdev); + if (ret) + return ret; + /* hold the ref while we probe */ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index fc34a0537d5f..0c78799793bd 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -92,7 +92,6 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x2000026: case 0x200002c: case 0x200003c: - case 0x2000046: case 0x5810000: case 0x5810033: case 0x5810036: @@ -174,7 +173,6 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x2000026: case 0x200002c: case 0x2000030: - case 0x2000046: case 0x2000067: case 0x2000084: case 0x2000086: diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h index fe04a3726334..eb093992759c 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.h +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -64,8 +64,6 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, @@ -91,13 +89,11 @@ static const struct reg_default rt721_sdca_mbq_defaults[] = { { 0x2000003, 0x0000 }, { 0x2000013, 0x8001 }, { 0x200003c, 0x0000 }, - { 0x2000046, 0x3400 }, { 0x5b10017, 0x1b07 }, { 0x5f00044, 0x6040 }, { 0x5f00045, 0x3333 }, { 0x5f00048, 0x0000 }, { 0x6100005, 0x0005 }, - { 0x6100006, 0x0000 }, { 0x6100007, 0x0007 }, { 0x610000d, 0x0051 }, { 0x6100010, 0x0180 }, diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 149cb6617126..1dad8012d904 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1652,6 +1652,17 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); } +static const struct dmi_system_id dmi_dell_mono_spk_quirk_table[] = { + { + .ident = "Dell Pro 5 Micro P5M1270", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0EBA"), + } + }, + {} +}; + static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) { unsigned int mic_func_status; @@ -1741,6 +1752,13 @@ static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) if (rt722->hw_vid == RT722_VB) regmap_write(rt722->regmap, 0x2f54, 0x00); + /* This Dell machine only uses the mono speaker */ + if (dmi_check_system(dmi_dell_mono_spk_quirk_table)) { + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL2, 0x391a); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL2, 0x0010, 0x0010); + } + /* clear flag */ regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index b654b14334d4..dd245ae9bd44 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -68,9 +68,11 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_ANALOG_BIAS_CTL3 0x04 #define RT722_MISC_CTRL1 0x07 #define RT722_JD_CTRL1 0x09 +#define RT722_MISC_CTRL2 0x0d #define RT722_LDO2_3_CTL1 0x0e #define RT722_LDO1_CTL 0x1a #define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL2 0x38 #define RT722_CLSD_CTRL6 0x3c #define RT722_COMBO_JACK_AUTO_CTL1 0x45 #define RT722_COMBO_JACK_AUTO_CTL2 0x46 diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index ad15ce9c6079..27d186c72315 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -318,6 +318,9 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_aic32x4 *div = to_clk_aic32x4(hw); u8 divisor; + if (!rate) + return -EINVAL; + divisor = DIV_ROUND_UP(parent_rate, rate); if (divisor > AIC32X4_DIV_MAX) return -EINVAL; @@ -331,8 +334,11 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, { unsigned long divisor; + if (!req->rate) + return -EINVAL; + divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate); - if (divisor > AIC32X4_DIV_MAX) + if (!divisor || divisor > AIC32X4_DIV_MAX) return -EINVAL; req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index c65a7769a9d2..6b91e02f130b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -163,9 +163,12 @@ static void wm8958_micd_set_rate(struct snd_soc_component *component) static int configure_aif_clock(struct snd_soc_component *component, int aif) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct clk *mclk = NULL; + bool switch_mclk; int rate; int reg1 = 0; int offset; + int ret; if (aif) offset = 4; @@ -174,11 +177,13 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) switch (wm8994->sysclk[aif]) { case WM8994_SYSCLK_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; rate = wm8994->mclk_rate[0]; break; case WM8994_SYSCLK_MCLK2: reg1 |= 0x8; + mclk = wm8994->mclk[WM8994_MCLK2].clk; rate = wm8994->mclk_rate[1]; break; @@ -196,6 +201,20 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) return -EINVAL; } + /* + * If the AIF clock is running, enable the new MCLK before switching + * to it and release the previous one afterwards. + */ + switch_mclk = wm8994->aif_clk_on[aif] && mclk != wm8994->aif_mclk[aif]; + if (switch_mclk) { + ret = clk_prepare_enable(mclk); + if (ret < 0) { + dev_err(component->dev, + "Failed to enable MCLK for AIF%d\n", aif + 1); + return ret; + } + } + if (rate >= 13500000) { rate /= 2; reg1 |= WM8994_AIF1CLK_DIV; @@ -210,6 +229,11 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, reg1); + if (switch_mclk) { + clk_disable_unprepare(wm8994->aif_mclk[aif]); + wm8994->aif_mclk[aif] = mclk; + } + return 0; } @@ -1059,13 +1083,25 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl unsigned int offset, val, clk_idx; int ret; + /* + * The AIF clock source may be changed while the AIF clock is + * running, so release the MCLK which has been enabled for it + * instead of the one currently selected. + */ + if (!enable) { + clk_disable_unprepare(wm8994->aif_mclk[aif]); + wm8994->aif_mclk[aif] = NULL; + wm8994->aif_clk_on[aif] = false; + return 0; + } + if (aif) offset = 4; else offset = 0; val = snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1 + offset); - val &= WM8994_AIF1CLK_SRC_MASK; + val = (val & WM8994_AIF1CLK_SRC_MASK) >> WM8994_AIF1CLK_SRC_SHIFT; switch (val) { case 0: @@ -1075,19 +1111,18 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl clk_idx = WM8994_MCLK2; break; default: + wm8994->aif_clk_on[aif] = true; return 0; } - if (enable) { - ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); - if (ret < 0) { - dev_err(component->dev, "Failed to enable MCLK%d\n", - clk_idx); - return ret; - } - } else { - clk_disable_unprepare(wm8994->mclk[clk_idx].clk); + ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK%d\n", + clk_idx + 1); + return ret; } + wm8994->aif_mclk[aif] = wm8994->mclk[clk_idx].clk; + wm8994->aif_clk_on[aif] = true; return 0; } @@ -2199,6 +2234,44 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, return 0; } +/* + * If SYSCLK is less than 50kHz adjust AIFnCLK dividers for detection, + * restoring the original ones once SYSCLK is fast enough again. + */ +static void wm8994_update_aif_clk_rate(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { + /* Don't overwrite the saved dividers with the 128fs ones */ + if (!wm8994->aifdiv[0]) { + dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); + + wm8994->aifdiv[0] = snd_soc_component_read(component, + WM8994_AIF1_RATE) + & WM8994_AIF1CLK_RATE_MASK; + wm8994->aifdiv[1] = snd_soc_component_read(component, + WM8994_AIF2_RATE) + & WM8994_AIF1CLK_RATE_MASK; + } + + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, 0x1); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, 0x1); + } else if (wm8994->aifdiv[0]) { + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, + wm8994->aifdiv[0]); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, + wm8994->aifdiv[1]); + + wm8994->aifdiv[0] = 0; + wm8994->aifdiv[1] = 0; + } +} + static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, unsigned int freq_in, unsigned int freq_out) { @@ -2433,33 +2506,7 @@ out: configure_clock(component); - /* - * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers - * for detection. - */ - if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { - dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); - - wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) - & WM8994_AIF1CLK_RATE_MASK; - wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) - & WM8994_AIF1CLK_RATE_MASK; - - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, 0x1); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, 0x1); - } else if (wm8994->aifdiv[0]) { - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, - wm8994->aifdiv[0]); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, - wm8994->aifdiv[1]); - - wm8994->aifdiv[0] = 0; - wm8994->aifdiv[1] = 0; - } + wm8994_update_aif_clk_rate(component); return 0; } @@ -2519,7 +2566,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, case WM8994_SYSCLK_MCLK1: wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1; - ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK1, &freq); if (ret < 0) return ret; @@ -2532,7 +2579,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, /* TODO: Set GPIO AF */ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2; - ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK2, &freq); if (ret < 0) return ret; @@ -2577,33 +2624,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, configure_clock(component); - /* - * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers - * for detection. - */ - if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { - dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); - - wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) - & WM8994_AIF1CLK_RATE_MASK; - wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) - & WM8994_AIF1CLK_RATE_MASK; - - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, 0x1); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, 0x1); - } else if (wm8994->aifdiv[0]) { - snd_soc_component_update_bits(component, WM8994_AIF1_RATE, - WM8994_AIF1CLK_RATE_MASK, - wm8994->aifdiv[0]); - snd_soc_component_update_bits(component, WM8994_AIF2_RATE, - WM8994_AIF2CLK_RATE_MASK, - wm8994->aifdiv[1]); - - wm8994->aifdiv[0] = 0; - wm8994->aifdiv[1] = 0; - } + wm8994_update_aif_clk_rate(component); return 0; } diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index b28398aa9e48..e7df0a1a2314 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -81,6 +81,8 @@ struct wm8994_priv { struct wm_hubs_data hubs; struct wm8994 *wm8994; struct clk_bulk_data mclk[WM8994_NUM_MCLK]; + struct clk *aif_mclk[2]; + bool aif_clk_on[2]; int sysclk[2]; int sysclk_rate[2]; int mclk_rate[2]; diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 47ac3e4985a5..4f3e6bc29c7e 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1115,11 +1115,19 @@ static int wsa881x_probe(struct sdw_slave *pdev, if (!wsa881x) return -ENOMEM; - wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); + /* + * The powerdown GPIO may be shared between several amplifiers. Request + * it asserted, so the shared GPIO proxy treats powerdown as the default + * state and keeps the line de-asserted as long as any amplifier needs + * it, then power up this amplifier. + */ + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(wsa881x->sd_n)) return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), "Shutdown Control GPIO not found\n"); + gpiod_set_value_cansleep(wsa881x->sd_n, 0); + dev_set_drvdata(dev, wsa881x); wsa881x->slave = pdev; wsa881x->dev = dev; @@ -1154,7 +1162,7 @@ static int wsa881x_runtime_suspend(struct device *dev) struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); @@ -1169,11 +1177,11 @@ static int wsa881x_runtime_resume(struct device *dev) struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); int ret; - gpiod_direction_output(wsa881x->sd_n, 0); + gpiod_set_value_cansleep(wsa881x->sd_n, 0); ret = sdw_slave_wait_for_init(slave, WSA881X_PROBE_TIMEOUT); if (ret) { - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); return ret; } @@ -1182,7 +1190,7 @@ static int wsa881x_runtime_resume(struct device *dev) if (ret) { regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_set_value_cansleep(wsa881x->sd_n, 1); return ret; } diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 442cf9f3eaac..17d8babc6123 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -456,10 +456,11 @@ /* * Device will report senseless data in many cases, so discard any measurements - * outside of valid range. + * outside of the two-point trim range. Do not narrow this to ambient: a + * speaker that has been playing easily runs well above 45 degC. */ -#define WSA883X_LOW_TEMP_THRESHOLD 5 -#define WSA883X_HIGH_TEMP_THRESHOLD 45 +#define WSA883X_LOW_TEMP_THRESHOLD WSA883X_T1_TEMP +#define WSA883X_HIGH_TEMP_THRESHOLD WSA883X_T2_TEMP struct wsa883x_priv { struct regmap *regmap; @@ -551,7 +552,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = { .num = WSA883X_PORT_VISENSE + 1, .type = SDW_DPN_SIMPLE, .min_ch = 1, - .max_ch = 1, + .max_ch = 2, .simple_ch_prep_sm = true, .read_only_wordlength = true, } @@ -572,7 +573,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { }, [WSA883X_PORT_VISENSE] = { .num = WSA883X_PORT_VISENSE + 1, - .ch_mask = 0x1, + .ch_mask = 0x3, }, }; @@ -1430,7 +1431,7 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) unsigned int dmeas_msb = 0, dmeas_lsb = 0; int d1, d2, dmeas; unsigned int mask; - int ret, range; + int ret; long val; guard(mutex)(&wsa883x->sp_lock); @@ -1493,8 +1494,8 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) } val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1)); - range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD; - if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) { + if (val >= WSA883X_LOW_TEMP_THRESHOLD && + val <= WSA883X_HIGH_TEMP_THRESHOLD) { wsa883x->temperature = val; *temp = val * 1000; ret = 0; @@ -1668,7 +1669,9 @@ static int wsa883x_probe(struct sdw_slave *pdev, pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto err; ret = devm_snd_soc_register_component(dev, &wsa883x_component_drv, diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index a367c94bfb4d..acf32af5aae3 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -720,10 +720,11 @@ /* * Device will report senseless data in many cases, so discard any measurements - * outside of valid range. + * outside of the two-point trim range. Do not narrow this to ambient: a + * speaker that has been playing easily runs well above 45 degC. */ -#define WSA884X_LOW_TEMP_THRESHOLD 5 -#define WSA884X_HIGH_TEMP_THRESHOLD 45 +#define WSA884X_LOW_TEMP_THRESHOLD WSA884X_T1_TEMP +#define WSA884X_HIGH_TEMP_THRESHOLD WSA884X_T2_TEMP struct wsa884x_priv { struct regmap *regmap; @@ -858,7 +859,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { .num = WSA884X_PORT_VISENSE + 1, .type = SDW_DPN_SIMPLE, .min_ch = 1, - .max_ch = 1, + .max_ch = 2, .simple_ch_prep_sm = true, .read_only_wordlength = true, }, @@ -891,7 +892,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { }, [WSA884X_PORT_VISENSE] = { .num = WSA884X_PORT_VISENSE + 1, - .ch_mask = 0x1, + .ch_mask = 0x3, }, [WSA884X_PORT_CPS] = { .num = WSA884X_PORT_CPS + 1, @@ -1929,8 +1930,8 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n", val, dmeas, d1, d2); - if ((val > WSA884X_LOW_TEMP_THRESHOLD) && - (val < WSA884X_HIGH_TEMP_THRESHOLD)) { + if (val >= WSA884X_LOW_TEMP_THRESHOLD && + val <= WSA884X_HIGH_TEMP_THRESHOLD) { wsa884x->temperature = val; *temp = val * 1000; ret = 0; @@ -2124,7 +2125,9 @@ static int wsa884x_probe(struct sdw_slave *pdev, pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; return devm_snd_soc_register_component(dev, &wsa884x_component_drv, diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 7651c64bc837..74c6ebc0e3d9 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -153,6 +153,9 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, } EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); +/* Fractional audio PLLs may sit a few Hz off nominal */ +#define FSL_RATE_CONSTR_TOL_HZ 100 + /** * fsl_asoc_constrain_rates - constrain rates according to clocks * @@ -172,6 +175,7 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, { int i, j, k = 0; u64 clk_rate[3]; + u32 rate, rem; *target_constr = *original_constr; if (pll8k_clk || pll11k_clk || ext_clk) { @@ -181,10 +185,25 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, clk_rate[0] = clk_get_rate(pll8k_clk); clk_rate[1] = clk_get_rate(pll11k_clk); clk_rate[2] = clk_get_rate(ext_clk); + rate = original_constr->list[i]; for (j = 0; j < 3; j++) { - if (clk_rate[j] != 0 && - do_div(clk_rate[j], original_constr->list[i]) == 0) { - target_rates[k++] = original_constr->list[i]; + if (clk_rate[j] == 0) + continue; + rem = do_div(clk_rate[j], rate); + /* + * The audio PLLs are fractional and cannot + * always reproduce their nominal rate: after + * any clk_set_rate() they may sit a few Hz + * off (observed on i.MX8M Plus: audio_pll1 + * reads 393215997 after restoring 393216000), + * which must not disqualify the entire rate + * family. Accept near-integer ratios; the + * other family misses by tens of kHz, so a + * small tolerance cannot mix families. + */ + if (rem <= FSL_RATE_CONSTR_TOL_HZ || + rem >= rate - FSL_RATE_CONSTR_TOL_HZ) { + target_rates[k++] = rate; target_constr->count++; break; } diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 85b7c929a28a..6a0174d6c78d 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -178,6 +178,16 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component, return 0; } +static int imx_rpmsg_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct rpmsg_info *info = dev_get_drvdata(component->dev); + + flush_workqueue(info->rpmsg_wq); + + return 0; +} + static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -285,9 +295,6 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component, struct rpmsg_info *info = dev_get_drvdata(component->dev); struct rpmsg_msg *msg; - /* Flush work in workqueue to make TX_CLOSE is the last message */ - flush_workqueue(info->rpmsg_wq); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { msg = &info->msg[TX_CLOSE]; msg->s_msg.header.cmd = TX_CLOSE; @@ -624,6 +631,7 @@ static const struct snd_soc_component_driver imx_rpmsg_soc_component = { .open = imx_rpmsg_pcm_open, .close = imx_rpmsg_pcm_close, .hw_params = imx_rpmsg_pcm_hw_params, + .hw_free = imx_rpmsg_pcm_hw_free, .trigger = imx_rpmsg_pcm_trigger, .pointer = imx_rpmsg_pcm_pointer, .ack = imx_rpmsg_pcm_ack, diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 575889a4c69b..310c4b77c4b7 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -70,6 +70,23 @@ static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* DMIC Endpoint */ + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device cs35l56_2_lr_adr[] = { { .adr = 0x00023001FA355601ull, @@ -301,6 +318,35 @@ static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = { } }; +/* + * MSI Raider 16 Max HX B2WI (MS-2651): RT713 (jack/dmic, "VB" SDCA variant) + * on link 0, two RT1320 speaker amps sharing link 2 (unique_id 0 = left, + * unique_id 1 = right). + */ +static const struct snd_soc_acpi_adr_device rt713_vb_0_adr[] = { + { + .adr = 0x000030025D071301ull, + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt713" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_2_lr_adr[] = { + { + .adr = 0x000230025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1320-1" + }, + { + .adr = 0x000231025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + static const struct snd_soc_acpi_link_adr arl_n_mrd_es9356_link1[] = { { .mask = BIT(1), @@ -454,6 +500,20 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr arl_rt713_vb_l0_rt1320_l2[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt713_vb_0_adr), + .adr_d = rt713_vb_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_lr_adr), + .adr_d = rt1320_2_lr_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = { { .mask = BIT(0), @@ -482,6 +542,15 @@ static const struct snd_soc_acpi_link_adr arl_rt712_l0_rt1320_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr arl_rt712_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt712_0_agg_adr), + .adr_d = rt712_0_agg_adr, + }, + {} +}; + static const struct snd_soc_acpi_codecs arl_essx_83x6 = { .num_codecs = 3, .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, @@ -619,6 +688,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { }, { .link_mask = BIT(0) | BIT(2), + .links = arl_rt713_vb_l0_rt1320_l2, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-arl-dummy.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2), .links = arl_rt722_l0_rt1320_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg", @@ -648,6 +725,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { }, { .link_mask = BIT(0), + .links = arl_rt712_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-dummy.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), .links = arl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index f0ff1350e9dd..9ac51c304ead 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -393,6 +393,7 @@ struct q6afe { struct q6core_svc_api_info ainfo; struct mutex lock; struct aprv2_ibasic_rsp_result_t result; + uint32_t vote_result; wait_queue_head_t wait; struct list_head port_list; spinlock_t port_list_lock; @@ -1010,13 +1011,14 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da const struct aprv2_ibasic_rsp_result_t *res; const struct apr_hdr *hdr = &data->hdr; struct q6afe_port *port; + uint32_t *vote_res; if (!data->payload_size) return 0; - res = data->payload; switch (hdr->opcode) { case APR_BASIC_RSP_RESULT: { + res = data->payload; if (res->status) { dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n", res->opcode, res->status); @@ -1043,8 +1045,10 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da } break; case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + vote_res = data->payload; afe->result.opcode = hdr->opcode; - afe->result.status = res->status; + afe->result.status = 0; + afe->vote_result = *vote_res; wake_up(&afe->wait); break; default: @@ -1955,6 +1959,8 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST); if (ret) dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id); + else + *client_handle = afe->vote_result; return ret; } diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index b6b1ef6833b3..bed3b64e8310 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -322,15 +322,15 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr } EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id, +static int __q6apm_unmap_memory_fixed_region(struct q6apm *apm, unsigned int graph_id, bool is_pos_buf) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; + uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), - APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + APM_CMD_SHARED_MEM_UNMAP_REGIONS, (graph_id | pos_mask)); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -357,13 +357,15 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, false); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, false); } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, true); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, true); } EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer); @@ -850,6 +852,21 @@ static int q6apm_audio_probe(struct snd_soc_component *component) static void q6apm_audio_remove(struct snd_soc_component *component) { + struct q6apm *apm = dev_get_drvdata(component->dev); + struct audioreach_graph_info *info; + int id; + + /* + * The card core removes components before freeing the PCMs, so + * q6apm-dai's pcm_free() runs after the topology (and graph_info) + * is gone and can no longer unmap. Release the DSP mappings here, + * otherwise the next pcm_new() fails to map the same buffer again. + */ + idr_for_each_entry(&apm->graph_info_idr, info, id) { + __q6apm_unmap_memory_fixed_region(apm, id, false); + __q6apm_unmap_memory_fixed_region(apm, id, true); + } + /* remove topology */ snd_soc_tplg_component_remove(component); } @@ -960,17 +977,23 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) wake_up(&apm->wait); break; case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; - rsp = data->payload; - - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info) - info->mem_map_handle = 0; - else + apm->result.opcode = result->opcode; + apm->result.status = result->status; + if (result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); + graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF; + + info = idr_find(&apm->graph_info_idr, graph_id); + if (info && !result->status) { + if (is_pos_buf) + info->pos_buf_mem_map_handle = 0; + else + info->mem_map_handle = 0; + } + wake_up(&apm->wait); break; default: diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index faafbc5c0ad0..612d8f03e6e9 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -312,22 +312,37 @@ audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private) for (sz = 0; sz < le32_to_cpu(private->size); ) { const struct snd_soc_tplg_vendor_array *mod_array; + u32 array_size; mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); + array_size = le32_to_cpu(mod_array->size); + if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) { struct audioreach_module_priv_data *pdata; + u32 nwords; + + /* size is the byte count of the __le32 data[] */ + nwords = array_size / sizeof(__le32); + + /* the blob must lie within the private data */ + if (sizeof(*pdata) + (u64)array_size > + le32_to_cpu(private->size) - sz) + return ERR_PTR(-EINVAL); - pdata = kzalloc_flex(*pdata, data, - le32_to_cpu(mod_array->size)); + pdata = kzalloc_flex(*pdata, data, nwords); if (!pdata) return ERR_PTR(-ENOMEM); - memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data, - le32_to_cpu(mod_array->size))); + memcpy(pdata, ((u8 *)private->data + sz), + struct_size(pdata, data, nwords)); return pdata; } - sz = sz + le32_to_cpu(mod_array->size); + /* a zero size would never advance the walk */ + if (!array_size) + return ERR_PTR(-EINVAL); + + sz = sz + array_size; } return NULL; @@ -611,6 +626,12 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon return mod ? PTR_ERR(mod) : -ENODEV; mod->data = audioreach_get_module_priv_data(&tplg_w->priv); + if (IS_ERR(mod->data)) { + int rc = PTR_ERR(mod->data); + + mod->data = NULL; + return rc; + } dobj = &w->dobj; dobj->private = mod; diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 5866f1f34b3d..d776a17f1fd3 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -39,6 +39,16 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) case USB_RX: return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, &data->usb_offload_jack_setup); + case RX_CODEC_DMA_RX_0: + /* + * The RX macro digital gain goes up to +40 dB. Userspace that + * maps full volume to the top of the range (PipeWire, PulseAudio) + * would clip there, so stop the interpolator gains at 0 dB. + */ + snd_soc_limit_volume(rtd->card, "RX_RX0 Digital Volume", 84); + snd_soc_limit_volume(rtd->card, "RX_RX1 Digital Volume", 84); + snd_soc_limit_volume(rtd->card, "RX_RX2 Digital Volume", 84); + fallthrough; default: return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index d43af5069baf..11466dd3539e 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -25,7 +25,8 @@ */ #define MCLK1_RATE 24000000U #define MCLK2_RATE 32768U -#define DEFAULT_FLL1_RATE 11289600U +/* Matches the codec's default AIF1 configuration: 48kHz, 256fs */ +#define DEFAULT_FLL1_RATE 12288000U struct midas_priv { struct gpio_desc *gpio_fm_sel; @@ -34,6 +35,7 @@ struct midas_priv { struct gpio_desc *gpio_headset_key; struct iio_channel *adc_headset_detect; unsigned int fll1_rate; + unsigned int aif1clk_rate; struct snd_soc_jack headset_jack; }; @@ -171,11 +173,12 @@ static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) if (!rate) rate = priv->fll1_rate; /* - * If no new rate is requested, set FLL1 to a sane default for jack - * detection. + * If no new rate is requested, set FLL1 to the rate the codec's AIF1 + * is currently configured for, as its digital paths can be powered + * up without an active stream. */ if (!rate) - rate = DEFAULT_FLL1_RATE; + rate = priv->aif1clk_rate ?: DEFAULT_FLL1_RATE; if (rate != priv->fll1_rate && priv->fll1_rate) { /* while reconfiguring, switch to MCLK2 for SYSCLK */ @@ -241,6 +244,7 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct midas_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned int pll_out; /* AIF1CLK should be at least 3MHz for "optimal performance" */ @@ -249,6 +253,8 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream, else pll_out = params_rate(params) * 256; + priv->aif1clk_rate = pll_out; + return midas_start_fll1(rtd, pll_out); } diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 79fdb672be1d..ee61c983dc57 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -27,8 +27,11 @@ static int class_read_prop(struct sdw_slave *sdw) { struct sdw_slave_prop *prop = &sdw->prop; + int ret; - sdw_slave_read_prop(sdw); + ret = sdw_slave_read_prop(sdw); + if (ret) + return ret; prop->use_domain_irq = true; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | @@ -194,8 +197,10 @@ int sdca_class_probe(struct sdw_slave *sdw, pm_runtime_get_noresume(dev); ret = devm_pm_runtime_enable(dev); - if (ret) + if (ret) { + pm_runtime_put_sync(dev); return ret; + } queue_work(system_long_wq, &drv->boot_work); @@ -260,6 +265,7 @@ int sdca_class_system_suspend(struct sdca_class_drv *drv) ret = pm_runtime_force_suspend(drv->dev); if (ret) { dev_err(drv->dev, "failed to force suspend: %d\n", ret); + enable_irq(drv->sdw->irq); return ret; } diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index d64f682cfbea..baa0fbf07ab2 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -7,6 +7,7 @@ config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD + depends on ACPI depends on X86 || COMPILE_TEST help This adds support for Sound Open Firmware for AMD platforms. diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 91f7ca22a4a7..a2b2ecf1e7e6 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -15,8 +15,6 @@ #include "hda.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) -#include "../../codecs/hdac_hda.h" - #define CODEC_PROBE_RETRIES 3 #define IDISP_VID_INTEL 0x80860000 @@ -148,7 +146,6 @@ static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, i /* probe individual codec */ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) { - struct hdac_hda_priv *hda_priv; struct hda_bus *hbus = sof_to_hbus(sdev); struct hda_codec *codec; u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | @@ -168,27 +165,15 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", address, resp); - hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL); - if (!hda_priv) - return -ENOMEM; - codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY); ret = PTR_ERR_OR_ZERO(codec); if (ret < 0) return ret; - hda_priv->codec = codec; - hda_priv->dev_index = address; - dev_set_drvdata(&codec->core.dev, hda_priv); - - if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) { - if (!hbus->core.audio_component) { - dev_dbg(sdev->dev, - "iDisp hw present but no driver\n"); - ret = -ENOENT; - goto out; - } - hda_priv->need_display_power = true; + if ((resp & 0xFFFF0000) == IDISP_VID_INTEL && !hbus->core.audio_component) { + dev_dbg(sdev->dev, "iDisp hw present but no driver\n"); + ret = -ENOENT; + goto out; } if (is_generic_config(hbus)) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index aeb34310eebd..333f6bbea753 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -349,6 +349,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) bus->chip_init = false; } +EXPORT_SYMBOL_NS(hda_dsp_ctrl_stop_chip, "SND_SOC_SOF_INTEL_HDA_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for HDaudio platforms"); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29..2a40cb9bf24d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -939,6 +939,7 @@ disable_pp_cap: hda_dsp_ctrl_ppcap_enable(sdev, false); } free_ipc_irq: + hda_dsp_ctrl_stop_chip(sdev); free_irq(sdev->ipc_irq, sdev); free_irq_vector: if (sdev->msi_enabled) @@ -1006,6 +1007,9 @@ void hda_dsp_remove(struct snd_sof_dev *sdev) snd_dma_free_pages(&hda->iccmax_dmab); skip_disable_dsp: + /* stop CORB/RIRB and position buffer DMA before the buffers are freed */ + hda_dsp_ctrl_stop_chip(sdev); + free_irq(sdev->ipc_irq, sdev); if (sdev->msi_enabled) pci_free_irq_vectors(pci); diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 304220d9aaf5..781508aef885 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -6,6 +6,7 @@ // // Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> @@ -55,8 +56,8 @@ #define CFG_S2MM_XFER_SHIFT 29 #define CFG_S2MM_PKG_MASK BIT(28) -#define AUD_CTRL_DATA_WIDTH_SHIFT 16 -#define AUD_CTRL_ACTIVE_CH_SHIFT 19 +#define AUD_CTRL_DATA_WIDTH_MASK GENMASK(18, 16) +#define AUD_CTRL_ACTIVE_CH_MASK GENMASK(22, 19) #define PERIOD_CFG_PERIODS_SHIFT 16 #define PERIODS_MIN 2 @@ -506,28 +507,29 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB); val = readl(stream_data->mmio + XLNX_AUD_CTRL); + val &= ~(AUD_CTRL_DATA_WIDTH_MASK | AUD_CTRL_ACTIVE_CH_MASK); bits_per_sample = params_width(params); switch (bits_per_sample) { case 8: - val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_8); break; case 16: - val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_16); break; case 20: - val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_20); break; case 24: - val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_24); break; case 32: - val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT); + val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_32); break; default: return -EINVAL; } - val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT; + val |= FIELD_PREP(AUD_CTRL_ACTIVE_CH_MASK, active_ch); writel(val, stream_data->mmio + XLNX_AUD_CTRL); val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT) 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 */ |
