summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-31 12:30:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-31 12:30:31 -0700
commite9954f3fbddd25e1a01b7e5a0b72fb8b564204e0 (patch)
tree6932c388b83c3c2366a340117adb4a515f7572cf /sound
parentca62fc8c6b1841039aee6de1ef03c9863751ec04 (diff)
parent74f6055d0c5997b13e6a5253fdc2263f3af90408 (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')
-rw-r--r--sound/core/info.c23
-rw-r--r--sound/core/pcm.c56
-rw-r--r--sound/core/seq/seq_ump_client.c5
-rw-r--r--sound/core/ump.c63
-rw-r--r--sound/drivers/opl3/opl3_midi.c15
-rw-r--r--sound/drivers/opl3/opl3_oss.c1
-rw-r--r--sound/drivers/opl3/opl3_seq.c13
-rw-r--r--sound/drivers/opl3/opl3_voice.h1
-rw-r--r--sound/drivers/pcmtest.c24
-rw-r--r--sound/hda/codecs/realtek/alc269.c12
-rw-r--r--sound/hda/common/codec.c6
-rw-r--r--sound/hda/common/sysfs.c4
-rw-r--r--sound/hda/controllers/intel.c2
-rw-r--r--sound/pci/ac97/ac97_codec.c29
-rw-r--r--sound/pci/emu10k1/emufx.c18
-rw-r--r--sound/sh/aica.c2
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c2
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c7
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c27
-rw-r--r--sound/soc/codecs/cs35l56-shared.c38
-rw-r--r--sound/soc/codecs/cs35l56.c17
-rw-r--r--sound/soc/codecs/cs35l56.h1
-rw-r--r--sound/soc/codecs/cs42l56.c1
-rw-r--r--sound/soc/codecs/es8326.c2
-rw-r--r--sound/soc/codecs/es8375.c136
-rw-r--r--sound/soc/codecs/es8375.h5
-rw-r--r--sound/soc/codecs/es9356.c106
-rw-r--r--sound/soc/codecs/es9356.h5
-rw-r--r--sound/soc/codecs/hdac_hda.c41
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.h4
-rw-r--r--sound/soc/codecs/rt722-sdca.c18
-rw-r--r--sound/soc/codecs/rt722-sdca.h2
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c8
-rw-r--r--sound/soc/codecs/wm8994.c153
-rw-r--r--sound/soc/codecs/wm8994.h2
-rw-r--r--sound/soc/codecs/wsa881x.c18
-rw-r--r--sound/soc/codecs/wsa883x.c21
-rw-r--r--sound/soc/codecs/wsa884x.c19
-rw-r--r--sound/soc/fsl/fsl_utils.c25
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c84
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c10
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c49
-rw-r--r--sound/soc/qcom/qdsp6/topology.c31
-rw-r--r--sound/soc/qcom/sm8250.c10
-rw-r--r--sound/soc/samsung/midas_wm1811.c14
-rw-r--r--sound/soc/sdca/sdca_class.c10
-rw-r--r--sound/soc/sof/amd/Kconfig1
-rw-r--r--sound/soc/sof/intel/hda-codec.c23
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c1
-rw-r--r--sound/soc/sof/intel/hda.c4
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c18
-rw-r--r--sound/usb/endpoint.c6
-rw-r--r--sound/usb/misc/ua101.c29
-rw-r--r--sound/usb/pcm.c20
-rw-r--r--sound/usb/quirks-table.h65
-rw-r--r--sound/usb/quirks.c6
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)(&register_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 */